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

Kategoriler
Teknik

PHP ile Basit Bir Router Nasıl Yapılır

PHP’de gelen istekleri (request) cevaplamak (response) için en kullanışlı yöntemlerden birisi router kullanmaktır. Popüler PHP ve PHP olmayan web çatılarının (framework) olmazsa olmazıdır router/yönlendirici/rotacı.

Rotalar, belli özelliklere sahiptir. Bunlardan en önemli üçü şunlardır:

  1. Rota yolu (/sayfa/5, /gonderi/3 gibi)
  2. İstek metdodları (GET, POST, PUT, PATCH, DELETE vs. gibi)
  3. Geri çağırım işlevleri (en basit tabiriyle fonksiyonlar veya sınıf metodları)

Eğer tüm bunları sağlarsak, sağlıklı bir rotacı yaratabilir ve rahatlıkla kullanabiliriz. Bunların dışında middleware(lar) (ara katmanlar olarak çevrilebilir), rota isimlendirmesi gibi birçok özellik/parametreye sahip olabilirler.

Şimdi vereceğim örnekte yalnızca GET ve POST istek metodlarını karşılayacak olan basit bir rotacı yazacağız.

Başlayalım.

Evvela bir Router sınıfımızı yaratıp gerekli metodları yaratalım.

Router.php

class Router
{
  /**
   * Mevcut yolu tutar
   * @var string
   */
  protected $actualPath;

  /**
   * Mevcut istek metodunu tutar
   * @var string
   */
  protected $actualMethod;

  /**
   * Tanımlanmış rotaları tutar
   * @var array
   */
  protected $routes = [];

  /**
   * 404 Sayfasını tutar
   * @var Closure|string
   */
  protected $notFound;

  /**
   * Rotacıyı başlatır
   * @param string $currentPath Mevcut yol
   * @param string $currentMethod Mevcut istek metodu
   */
  public function __construct($currentPath, $currentMethod)
  {
    $this->actualPath = $currentPath;
    $this->actualMethod = $currentMethod;

    // Sayfa bulunamadı rotasını ayarlayalım
    $this->notFound = function(){
      http_response_code(404);
      echo '404 Not Found';
    };
  }

  /**
   * Yeni bir GET rotası yaratır
   * @param string $path İstek yolu
   * @param Closure|string $callback Geri çağırım işlevi
   * @return void 
   */
  public function get($path, $callback)
  {
    $this->routes[] = ['GET', $path, $callback];
  }

  /**
   * Yeni bir POST rotası yaratır
   * @param string $path İstek yolu
   * @param Closure|string $callback Geri çağırım işlevi
   * @return void
   */
  public function post($path, $callback)
  {
    $this->routes[] = ['POST', $path, $callback];
  }

  /**
   * Rotalar tanımlandıktan sonra eşleşen rotayı bulup çalıştırır
   * @return mixed
   */
  public function run()
  {
    foreach ($this->routes as $route) {
      list($method, $path, $callback) = $route;

      $checkMethod = $this->actualMethod == $method;
      $checkPath = preg_match("~^{$path}$~ixs", $this->actualPath, $params);

      if ($checkMethod && $checkPath) {
        array_shift($params);
        return call_user_func_array($callback, $params);
      }
    }

    return call_user_func($this->notFound);
  }
}

Yukarıdaki yazdığım sınıfta her metodun üstündeki açıklama bölümlerinde alacağı parametreleri ve metodların ne iş yaptığını yazdım. Ama biz yine de metodların ne işe yaradığını detaylandıralım:

  1. __construct metodunda Router sınıfı oluşturulurken iki parametre aldık. Bunlardan $currentPath parametresi kullanıcının sayfaya girmek istediğinde oluşacak olan URL barındaki yol değeridir. Yani https://yilmazdemir.com.tr/herhangi/bir/sey dizgesindeki /herhangi/bir/sey. $_SERVER['REQUEST_URI'] ile elde edebiliyoruz. Bir sonraki adımda yapacağız.
  2. İkinci parametre $currentMethod parametresi. Kullanıcıdan gelen mevcut istek metoduna işaret eder. GET, POST, PUT, PATCH, DELETE gibi değerlerdir. Ama biz detaya inmeden yalnızca GET ve POST tiplerini işleyeceğiz. Daha detaylı bilgi için HTTP Protokolü’nü araştırabilirsiniz. İstek metodunu $_SERVER['REQUEST_METHOD'] ile elde edeceğiz.
  3. Metodun içinde ayrıca belirttiğimiz bir anonim fonksiyon var. Bu herhangi bir rota eşleşmediğinde döndürmek için kullanacağımız fonksiyon. Ben doğrudan kodlu (hard-coded) biçimde yazdım ama bir notFound metodu yazılarak dinamikleştirilebilir.
  4. get ve post metodlarında ise rota tanımlaması yaptırıyoruz. Sınıf içerisinde $routes sınıf değişkenine yeni bir değer ekleyerek aldığımız $path ve $callback parametreleriyle rotamızı oluşturuyoruz.
  5. Son olarak rotayı çalıştırabilmek için bir metoda ihtiyacımız var. Bu metod run metodu. Örneğimizde bu metod sınıf değişkeni olan $routes değişkeniyle bir döngü oluşturuyor ve işlemlerimizi burada yapıyoruz. list() fonksiyonu ile her bir rota tanımlamamız için dize değerlerini değişken yapıyoruz. $checkMethod ve $checkPath değişkenleriyle de bir doğrulama yapıyoruz. Bu doğrulamada bizim rotaları oluştururken tanımladığımız değerlerle kullanıcıdan gelen değerler eşleşiyor mu bunu arıyoruz. Eğer doğru (true) dönerse $callback‘i çalıştırıp döndürüyoruz. Eğer hiçbir uyuşma yoksa döngüden sonra 404 döndürüyoruz.

index.php

require 'Router.php';

$path = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];

$router = new Router($path, $method);

$router->get('/', function(){
  echo 'Ana Sayfa';
});

$router->get('/haber/([d]+)', function($id){
  echo "Haber ID'si: {$id}";
});

$router->post('/foo', function(){
  echo 'Diğer işlemler';
});

$router->run();

Router hazır. Rota parametresi için düzenli ifadeler (Regular Expressions) kullanıyoruz. Bunlar daha da basitleştirilebilir. Ayrıca bazı buglar da mevcut. Bunları kullandıkça fark edersiniz elbette. Yeni özellikler katmak sizin hayal gücünüze bağlı. Örneğin /haberler rotası oluşturduğunuz ve sayfalamayı QueryString ile yapacaksınız. Yani /haberler?sayfa=5 şeklinde bir ifade maalesef ki eşleşmeyecektir. Bu durumda istek yolunu düzenledikten sonra sınıf içine tanımlamanız gerekiyor.

Apache için htaccess şu şekilde:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Kolay gelsin.

Kategoriler
Teknik Yaşamsal

Yeni Bir Görünüm ve Altyapıya Geçtim

Merhaba,

Kısa bir süre önce üzerine çalıştığım bir blog yazılımı vardı, onu bitirdim ve yeni bir görünüm ile birleştirerek arkayüzü ve önyüzü tamamıyle değiştirdim.

Bunu yapmamdaki yegane sebep WordPress’i beğenmemem. Bir blog sistemi için özellikle benim bireysel olarak kullanabileceğim bir blog sistemi için WordPress oldukça hantal.

Basit bir veritabanı üzerinde içerik, kategori ve yorum tablolarıyla, Slim Framework üzerinde basit bir router ile ufak bir sistem yazdım. Bu sistem bütün ihtiyaçlarımı fazlasıyla karşılıyor.