Kategoriler
Teknik

MySQL’de “Truncated incorrect DOUBLE value” hatası

Her ne kadar hata içerisinde "DOUBLE value" ibaresi geçse de bu hatayı aldığınızda tablonuzda DOUBLE tipinde bir sütun mevcut olmayabilir. Bu yüzden bir kafa karışıklığı yaratabiliyor. Benim karşılaştığım, VARCHAR türünde bir sütunda INTEGER koşul sorgulama yaptığım sırada hata vermesiydi. Örnek sorguyu paylaşayım:

 update `foo_table` set `foo_type` = 'PC', `foo_table`.`updated_at` = '2022-07-23 12:32:41' where `id` >= 4843051 and `user
_id` = 8081 and `foo_id` = 12 and `foo_type` = 'CT';

Bu sorgudaki problem, foo_id sütunundaki koşulu belirtirken integer olarak koşullandırmamdan kaynaklı. Halbuki bu sütun string türünde veri de tutabilecek türde yani VARCHAR. Bu sebepten dolayı "Truncated incorrect DOUBLE value" hatası aldım. Bunu düzeltmek için foo_id koşulundaki veriyi tırnak içine aldım.

 update `foo_table` set `foo_type` = 'PC', `foo_table`.`updated_at` = '2022-07-23 12:32:41' where `id` >= 4843051 and `user
_id` = 8081 and `foo_id` = '12' and `foo_type` = 'CT';

Ve artık her şey yolunda.

Araştırdığım kadarıyla daha çok UPDATE sorgularında yaşanan bir problem ve MySQL açısından haklı bir serzeniş.

Bu hata ile karşılaşmanıza olası başka bir senaryoda şu şekildeymiş:

update students SET name='Ali' and score=9 where id=1
ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'Ali'

Bu sorgudaki sıkıntı açık, UPDATE yaparken name ve score sütunlarını belirtirken AND ifadesi kullanmak. Normal şartlarda UPDATE sorgusunda güncelleme yapılacak sütunlar tanımlanırken virgül ile ayırılır. Yani:

update students SET name='Ali', score=9 where id=1

Diğer bir çözüm olarak da CAST() fonksiyonu kullanılabilir.

Ek olarak PDO veya Laravel üzerinde Eloquent ORM ile veriyi bind ederken string olarak göndermelisiniz.

Student::where('foo_id', '15')->update([...]);
Student::where('foo_id', '19RDA32')->update([...]);

PDO ile:

$statement = $pdo->prepare('update ...');
$statement->bindParam('foo_id', '15');
// veya
$statement->bindParam('foo_id', '15', \PDO::PARAM_STR);

PDO‘da, bindParam metodunda üçüncü parametre varsayılan olarak \PDO::PARAM_STR olarak tanımlıdır. Bu detayda hatanıza sebep olabilecek durumlardan birisi olabilir.

İyi çalışmalar.

Kategoriler
Teknik

PHP’de Blog veya İçerik Yönetim Sistemi Oluşturmak

En popüler içerik yönetim sistemi/blog yazılımı olan WordPress’i bir çoğumuz kullanmıştır kullanmasına ama bazılarımız var ki WordPress’in nasıl çalıştığını merak edip, bazılarımız da eklenti/tema geliştirmeye merak salmışızdır. Tüm bunlardan doğru, karşımıza çıkan binlerce fonksiyon, yöntem, ıvır, zıvır vesaire derken kafamız çorba gibi oluyor. Bu çorbalaşmış kafayı netleştirmek için bu yazıda basit bir blog sistemi nasıl yazılır ondan bahsedeceğim. Elbette ki WordPress alternatifi olmayacak. Sadece işimizi görebilecek, basit ve kullanışlı bir sistem hakkında konuşacağız.

Veritabanı Yapısı

Veritabanı yapısından başlayalım ki sistemi kafamızda oturtmamız daha kolay olsun ve rahatça içselleştirebilelim. Bir blog sistemi aslında en minimal haliyle 2 tablo ile çalışıp güçlü hale getirilebilir. Birincisi içerik tablosu, diğeri ise kategorilerin bulunduğu tablo. Yorum tablosunu atlıyorum, yorum kısmını Disqus veya Facebook yorumlarıyla da çözebiliriz.

İçerikler Tablosu

İçerik tablosu, oluşturmak istediğimiz blog sisteminin kalp niteliğini taşıyan tablo olacaktır. Çünkü her türlü içeriği (blog gönderisi, sayfalar, video içerikleri vs.) burada tutacağız. Hemen nasıl göründüğüne bakalım:

contents tablosu

id slug type title body tags category_id created_at
1 merhaba-dunya post Merhaba Dünya İlk Gönderi, . Lorem lipsum dolor sit amet. Merhaba, Dünya 1 2018-07-18 11:27:53
2 hakkimda page Hakkımda Hakkımda sayfası içeriği 2018-07-18 11:37:12
3 harika-bir-video-cektim video Harika Bir Video Çektim! Youtube embed veya bağlantısı Youtube, Video, Macera 2 2018-07-18 12:42:19

Genel hatlarıyla içerik tablomuz bu şekle sahip olacak. Burada slug sütunu, kalıcı bağlantıyı ifade ediyor. Kullanıcı içeriğe erişirken https://foo.com/kalici-baglanti URL’ini kullanabilmemiz için, veritabanımızda slug sütununun olması şart. Eğer https://foo.com/content.php?id=5 şeklinde bir yapı tercih etmiş olsaydı, slug sütunu koymamıza gerek kalmazdı.

type sütunu ise, içeriğin türünü belirlediğimiz tablo olacak. Ben üç tane içerik türü ekledim, siz hayal gücünüze göre bunu ayarlayabilirsiniz. post blog gönderisini, page blog içindeki bir sayfayı video ise bir video gönderisini temsil ediyor. Mesela buna alternatif/ek olarak gallery eklenip, bir fotoğraf galerisi türü oluşturabilirsiniz.

title gönderinin başlığını, body ise gönderinin içeriğini tutacak olan sütunlar olacak. tags sütununda ise, virgülle ayrılmış biçimde etiketler tutacağız. Böylelikle bir etiket sistemimiz olmuş olacak.

created_at sütunu ise oluşturma tarihini ifade ediyor.

category_id ise kritik bir sütun. Burada mevcut gönderinin hangi kategoriye ait olduğunu belirteceğiz. Bunu yaparken kategorinin adını değilde, ID’sini vereceğiz. Peki vereceğimiz bu ID’yi nerede, nasıl kullanıp ne olduğunu nasıl anlayacağız?

MySQL, SQLite, PostgreSQL gibi veritabanları ilişkisel veritabanı türünde olduğu için, bir tablodaki satırı, başka tablodaki satır veya satırlarla bağlayabiliyor/ilişkilendirebiliyoruz. Şimdi bu blog/içerik yönetim sisteminde, her bir içerik bir adet kategoriye ait olmuş olacak (Sayfa türündeki içerikler herhangi bir kategoriye sahip olmasa da olur).

Kategoriler Tablosu

Kategorileri tuttuğumuz bu tabloda sistem içerisindeki temel taksonomiyi en basit haliyle gerçekleştirmiş olacağız. Daha karmaşık ve spesifik yöntemler var ama, bu yazının konusu basitlik olduğundan fazla detaya girmeyeceğiz. Evvela, tablomuzun yapısın görelim:

categories tablosu

id name description post_count
1 Genel Diğer kategorilere dahil olmayan içerikler 1
2 Eğlence Eğlenceli içeriklerin bulunduğu muazzam bölüm! 1

Şimdi önceki tasarladığımız tablo olan contents tablosuna dönelim ve oradaki category_id sütununa bakalım. Bu sütunda 1 değerini alanların bu tablodaki yani categories tablosundaki ID’si 1 olan satıra ait olduğunu anlıyoruz. 2 değerini alanlar ise, Eğlence kategorisine dahilmiş.

İçerikler tablosundaki 100 tane satır/içerik 1 ID’li kategoriye ait olabilir. Aynı şekilde diğer 35 içerik de 2 ID’li kategoriye ait olabilir. Böylelikle her içerik bir kategoriye ait olmuş olur. Her içeriğin en fazla bir kategorisi olabilir. Birden fazla kategoriye sahip olamaz (buna One To Many ilişkilendirme deniyor). Bu yaptığımız sistemde bu mümkün değil. Çok daha farklı bir ilişkilendirme sisteminde bir içerik birden fazla kategoriye ait olacak şekilde ayarlayabilirdik (WordPress’te birden fazla kategori seçebiliyorsunuz mesela ve bu yöntemin adı Many To Many ilişkilendirme) ama yapmadık. İşleri karıştırmadık. 🙂

Temel olarak, veritabanı yapısı bu şekilde. Peki PHP ile oluşturacağımız rotalar/sayfalar hangi işleri yapacak? Hemen kısaca değinelim.

İçerik Sayfası

İçerik sayfasında, veritabanında slug ile eşleşen veriyi alıp, sayfada işleleyeceğiz. Örnek SQL sorgusu şöyle olacak:

SELECT * FROM contents WHERE slug=?

Veritabanından alacağımı veri, göstereceğimiz içerik olacak. type sütunundaki değere göre de gösterilecek içerik için farklı işlevler gerçekleştirebiliriz.

Kategori Sayfası

Kategori sayfasında, kategori ID’sine göre içerikleri alabiliriz. (Kategoriler için slug da kullanabilirdik) Örnek SQL sorgusunu şöyle görelim:

SELECT * FROM contents WHERE category_id=?

Böylelikle kategoriye ait içerikleri de elde etmiş olduk. Şimdilik anlatacaklarım bundan ibaret. Herhangi bir sorunuz varsa severek cevaplarım. Bir sonraki yazıda görüşmek üzere.

Kategoriler
Teknik

Eloquent ORM’de Erişimci ve Mutatörler

Erişimciler (Accessors)

Erişimciler, veritabanından aldığımız bilgileri/verileri önceden tanımlı olarak biçimlendirmemize olanak tanır. İki tane örnek verelim. Öznitelikleri türkçe olarak örneklendireceğim (elbette ki veritabanını tasarlarken ingilizce kullanmak gerekiyor)

Veritabanımızdaki tabloda ad ve soyad adlı iki adet sütun olduğunu varsayalım. Normal şartlarda veritabanındaki ad soyad bilgisini görüntülemek istediğimizde

Ad Soyad: {{ mb_ucfirst($uye->ad) . ' ' . mb_ucfirst($uye->soyad) }}

şeklinde kullanırız. Ama aşağıdaki gibi bir metodu, modelimize tanımlarsak, işimizi, daha kullanışlı hale getirebiliriz.

public function getAdSoyadAttribute()
{
    return mb_ucfirst($this->ad) . ' ' . mb_ucfirst($this->soyad);
}

Artık şöyle kullanılabilir: Ad Soyad: {{ $uye->ad_soyad }}

Önceden tanımladığımız metod, bize veri işlemede yardımcı oluyor. Benzer bir örneği para birimi için verebiliriz. Veritabanımızda fiyat adlı bir sütun olsun ve içeriği ondalık sayı olarak tutsun. 1325.00 olan bir değeri normal şartlarda görüntülemek istediğimizde ondalık hali yerine şunu göreceğiz: 1325. Şimdi bunu biçimlendirelim.

/**
 * @return string
 */
public function getFiyatAttribute()
{
    return number_format((float) $this->fiyat, 2, ',', '.') . ' TL';
}

Şimdi, veritabanındaki fiyat değerini görüntülemek istediğimizde TL’ye uygun biçimde görünecektir. Yani 1.325,00 TL şeklinde.

 Mutatörler (Mutators)

Erişimcilerin aksine mutatörler gösterilen veriyi işlemek yerine gelen/alınan veriyi işlemekte bize yardımcı olurlar.

Aşağıdaki örnekte 1.325,00 TL olarak gelen veriyi veritabanına kaydetmeden önce 1325.00 haline çevireceğiz.

/**
 * @param string $deger
 * @return void
 */
public function setFiyatAttribute($deger)
{
    $this->attributes['fiyat'] = (float) str_replace(
        [' TL', '.', ','], 
        [null, null, '.'], 
        $deger
    );
}

Biraz daha basitleştirirsek, gelen şifre değerini veritabanına kaydetmeden evvel md5 ile şifrelenmiş haliyle kaydetmek isteyelim. (Ama tabii siz bunu yapmayın, bcrypt ile saklayın)

/**
 * @param string $deger
 * @return void
 */
public function setSifreAttribute($deger)
{
    $this->attributes['sifre'] = md5($deger);
}

Zaman Mutatörleri

Eloquent, varsayılan olarak created_at ve updated_at sütunlarını Carbon (tarih/saat sınıfı) örneğine dönüştürür. $dates sınıf değişkeni üzerine zaman özniteliği adını (aşağıdaki örnekte silinme_tarihi ve yorum_tarihi) ekleyerek geçerli kılabiliyoruz.

class Gonderi extends Model
{
    protected $dates = [
        'created_at',
        'updated_at',
        'silinme_tarihi', // deleted_at
        'yorum_tarihi', // commented_at
    ];
}

 Tarih Formatları

Varsayılan olarak tarih formatı Y-m-d H:i:s olan formatını değiştirmek için kullanılır. $dateFormat sınıf değişkeni aracılığıyla değiştirilir.

class Gonderi extends Model
{
    protected $dateFormat = 'U'; // Unix zaman damgası cinsine döndürür
}

Öznitelik Biçimlendirme

$casts sınıf değişkeni sayesinde model özniteliklerini biçimlendirebiliyoruz (cast). Bu özellik oldukça kullanışlı. Örneğin, vertabanında bir kullanıcının yönetici olup olmadığını yonetici_mi sütunu ile belirliyoruz. MySQL’de boolean veri türü olmadığı için işimizi görecek veri türü Tiny Integer oluyor. Eğer yönetici ise 1 değilse 0 değerini alacağını varsayalım. Veriyi elde etmek istediğimizde yonetici_mi‘in 1 değilde, true olarak biçimlendirmek istiyoruz. Şunu yapmak yeterli geliyor:

class Uye extends Model
{
  protected $casts = [
    'yonetici_mi' => 'boolean',
    'son_giris' => 'timestamp',
    'fiyat' => 'float',
    'yas' => 'integer'
  ]
}

$casts dizisinin ilk elemanında değeri boolean veri türüne biçimlendirmesini söyledik. Diğer elemanlarda da farklı işlemler yaptık. son_giris unix zaman damgasına biçimlenecek. fiyat ondalık sayı tipine biçimlenecek. yas sayı tipine biçimlenecek. Desteklenen biçimlerse şu şekilde: integer, real, float, double, string, boolean, object, array, collection, date, datetime, ve timestamp.

 Dizi ve JSON Biçimlenirme

Dizi biçimlendirme, JSON olarak depolanan veri tiplerinde oldukça kullanışlıdır (MySQL’in 5.7 sürümü JSON veri tipini destekliyor, önceki sürümler için TEXT kullanılabilir).

class Uye extends Model
{
  protected $casts = ['bilgiler' => 'array'];
}

$uye = Uye::findOrFail($id);

$bilgiler = $uye->bilgiler;
$bilgiler['yas'] = 25;
$bilgiler['sehir'] = 'İstanbul';
$uye->bilgiler = $bilgiler;

$uye->save();

Kolay gelsin.


Kaynakça

  1. https://laravel.com/docs/5.6/eloquent-mutators
Kategoriler
Teknik

Eloquent ORM’in Tek Başına Kullanımı

Evvela composer paket yöneticisiyle projemizi oluşturalım.

PHP 7 altındaki sürümler için kullanmanız gereken sürüm 5.4 olanı. 5.5 ve üstü olanlar PHP 7 gerektiriyor. Eğer PHP 7 ile çalışacaksanız sürüm belirtmeden gerekliliği indirebilirsiniz.

$ composer require illuminate/database:5.4

Öncelikle bir ‘kapsül’ oluşturacağız. Laravel yaratıcıları Eloquent’in ‘kapsül’ ile çatı dışında kullanılabilmesini amaçlamışlar.

index.php

require __DIR__ . '/vendor/autoload.php';

use Illuminate\Database\Capsule\Manager as Capsule;

$capsule = new Capsule();

$capsule->addConnection([
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => 'password',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
]);

/*
SQLite bağlantısı için

$capsule->addConnection([
  'driver' => 'sqlite',
  'database' => '/hedef/dizin/database.sqlite',
  'prefix' => ''
]);

Dipnot: addConnection'ın ikinci parametresi bağlantı adını alır.
*/

// Kapsülü statik metodlarla global olarak erişilebilir hale getirir
$capsule->setAsGlobal();

// Eloquent ORM'i başlat
$capsule->bootEloquent();

Kurulumu tamamladık. Şimdi ilk örneğimizi sorgu oluşturucu ile yapalım:

$kullanicilar = Capsule::table('kullanicilar')->where('oylama', '>', 100)->get();

Doğrudan sorgu çalıştırmak istediğimizde select metodunu kullanıyoruz:

$sonuclar = Capsule::select('select * from kullanicilar where id = ?', [$id]);

Şema oluşturucusunu da kullanabiliyoruz. (Migrasyon/taşıma (Migration) için kullanılır)

Capsule::schema()->create('kullanicilar', function ($table) {
  $table->increments('id');
  $table->string('epota')->unique();
  $table->string('sifre');
  $table->unsignedTinyInteger('yas')->nullable();
  $table->boolean('aktif')->default(true);
  $table->timestamps();
});

Son olarak model sınıfımız ile kullanabiliriz. Kendimize has yazdığımız bir MVC iskeletinde kullanmak için oldukça kullanışlı oluyor.

class Kullanici extends \Illuminate\Database\Eloquent\Model {}

$kullanicilar = Kullanici::where('oylama', '>', 1)->get();

Kolay gelsin.


Kaynakça

Kategoriler
Teknik

Eloquent İlişkilerinde Koşullu Sorgular

Elimizde stok kartları (Stock), faturalar (Invoice) ve fatura satırları (InvoiceRow) adında üç adet model olduğunu varsayalım. Stok kartını görüntülemek istediğimizde stok bilgilerinin yanında, mevcut stoğun hangi faturalarda işlem gördüğünü de listelemek gerekecek. Faturalar ve fatura satırları modelleri şöyle olsun:

Invoice.php

// ...
use App\Models\InvoiceRow;

class Invoice extends Model
{
    public function rows()
    {
        return $this->hasMany(InvoiceRows::class);
    }
}

InvoiceRow.php

// ...
use App\Models\Stock;
use App\Models\Invoice;

class InvoiceRows extends Model
{
    public function stock()
    {
        return $this->belongsTo(Stock::class);
    }

    public function invoice()
    {
        return $this->belongsTo(Invoice::class);
    }
}

Yukarıda, Fatura ve fatura satırları modellerini ilişkilendirdik. Ek olarak fatura satırları modeliyle stok kartları modelini ilişkilendirdik. Her bir fatura satırının, bir faturaya ve bir stok kartına ait olduğunu ($this->belongsTo() metodu) söyledik.

StockController.php

// ...
use App\Models\Stock;
use App\Models\Invoice;

class StockController extends Controller
{
    // ...
    public function show($id)
    {
        $stock = Stock::findOrFail($id);
        $invoices = Invoice::with('rows')->whereHas('rows', function($query) use ($stock){
            $query->where('stock_id', $stock->id);
        })->orderBy('created_at', 'desc')->get();

        return view('stock.show', compact('stock', 'invoices'));
    }
}

Yukarıdaki kontrolcüde stok kartı görüntülemek istedik. $invoices değişkenine mevcut açmak istediğimiz stok kartının bulunduğu faturaların koleksiyonunu (listesini) getirttik. Burada anahtar metod whereHas metodu. Bu metod sayesinde sorgumuza bir şart koştuk. Dedik ki eğer fatura satırları arasında belirttiğimiz stok varsa faturaları getir.

Kolay gelsin.


Kaynakça