Kategoriler
Teknik

Spatie Async Paketiyle Laravel Eloquent ORM’i Kullanmak

Bir servisten yüklü miktarda veri çekip veritabanına işlemem gerekiyordu. Veriyi Guzzle’ın havuz özelliğiyle asenkron şekilde çekip veriyi işlemek içinse Spatie Async paketi kullanmam gerekti. Şu şekilde yol aldım.

Paketi composer ile kuralım:

composer require spatie/async

Kullanımı:

use SpatieAsyncPool;

$pool = Pool::create();

foreach ($things as $thing) {
    $pool->add(function () use ($thing) {
        // Do a thing
    })->then(function ($output) {
        // Handle success
    })->catch(function (Throwable $exception) {
        // Handle exception
    });
}

$pool->wait();

Yukarıdaki örnekte varsayılan ayarlamalar kullanıldı. Aşağıdaki örnekte havuza ekleyeceğimiz öğeleri Task sınıfını kullanarak ekleyip, ayarlamaları kendimiz yapacağız.

use SpatieAsyncPool;

$pool = Pool::create()
    ->concurrency(10)
    ->autoload(__DIR__ . '/vendor/autoload.php');

$pool->add(new FooTask(1));
$pool->add(new FooTask(2));
$pool->add(new FooTask(3));

$pool->wait();

Task örneği ise şöyle olacak:

class FooTask extends SpatieAsyncTask
{
    public function configure()
    {
        $app = require __DIR__ . '/../../bootstrap/app.php';
        $app->make(IlluminateContractsConsoleKernel::class)->bootstrap();
    }

    public function run()
    {
        return AppItem::create([
            'foo' => 'bar'
        ]);
    }
}

Burada önemli olan nokta configure() metodunda gereklilikleri ihtiyaçları çağırmak.

Kolay gelsin.

Kategoriler
Teknik

PHP’de Geriçağırım İşlevleri (Fonksiyon ve Yöntemler) (1)

Geriçağırım (callback) işlevleri (functions) aslında PHP’de bildiğimiz fonksiyonları ifade eder. PHP’nin 5.4 sürümünden itibarent bir callable türü ile kendilerini belli ederler.

PHP’de iki çeşit işlev (fonksiyon) vardır. Birincisi adı sanı belli olan işlevler, ikincisi anonim işlevler. Adı sanı belli dediğimiz işlevler, şu ana kadar bildiğimiz kullandığımız fonksiyonlardır. Bunları çağırmanın/kullanmanın birden fazla yöntemi vardır. Birazdan bunları işleyeceğiz. İkinci olan anonim işlevler ise, fonksiyonlarla hemen hemen aynı olup, fonksiyonlar gibi tanımlanmaz. Fark olarak anonim işlevler bir değişkene atanabilir. (en basit örneğiyle, bunun dışında farklı şeylere de yarar, göreceğiz).

PHP’de bir değerin çağırılabilir olup olmadığını is_callable işleviyle kontrol edebiliyoruz. İki tane örnek verelim:

function selam_ver(string $isim): string
{
    return "Selamlar sevgili $isim";
}

var_dump(is_callable('selam_ver')); // true

$selamVer = function(string $isim): string {
    return "Selamlar sevgili $isim";
};

var_dump(is_callable($selamVer)); // true
var_dump(is_callable('isset')); // true çünkü böyle bir fonksiyon zaten var, PHP'de öntanımlı
var_dump(is_callable('herhangi_fonksiyon')); // false, çünkü herhangi_fonksiyon adında bir fonksiyon tanımlamadık

Buradan doğru, bir işlevin (fonksiyonun) tanımlı olup olmadığını veya bir değişkenin işlev olup olmadığını öğrenebiliyoruz. Aynı zamanda bir sınıf içerisindeki statik bir işlevin var olup olmadığını da öğrenebiliriz:

class Selamlayici
{
    public static function selamla(string $isim): string
    {
        return "Selamlar sevgili $isim";
    }
}

var_dump(is_callable('Selamlayici::selamla')); // true

Peki sınıf dinamikse nasıl öğreneceğiz? Şöyle:

class Selamlayici
{
    public function selamla(string $isim): string
    {
        return "Selamlar sevgili $isim";
    }
}

var_dump(is_callable([new Selamlayici, 'selamla'])); // true

Dinamik bir sınıfta dize içerisindeki ilk eleman sınıfın örneğini, ikinci eleman ise yöntemi (metodu) bulundurur.

Şimdi, buraya kadar bazı şeylere tamam dedik. Neyin ne olduğunu nasıl öğreneceğimizi anladık. Peki bunları nasıl kullanacağız. İşlevleri kullanmanın en basit yolu, herkesin bildiği gibi selam_ver('Ali') kodu gibidir. Ama PHP’de işlevleri kullanıp çağırabilmek için birden fazla yöntem vardır. Hemen görelim:

selam_ver('Ali'); // "Selamlar sevgili Ali"
$selamVer('Renas'); // "Selamlar sevgili Renas"
Selamlayici::selamla('Deniz'); // "Selamlar sevgili Deniz"
$selamlayici = new Selamlayici;
$selamlayici->selamla('Nar'); // "Selamlar sevgili Nar"

Bu gördüğümüz klasik bir biçimde işlevleri çağırmamıza yarar, birçoğumuz bilir. Başka hangi yöntemler var işlevleri çağırmak için? İlk iki tanesi call_user_func ve call_user_func_array işlevidir. Bunlar tanımlı bir işlevi bir işlev aracılığı ile çağırır. Görelim:

function parametresiz_selamlama(): string
{
    return 'Selamlar sevgili Django!';
}

var_dump(call_user_func('parametresiz_selamlama')); // "Selamlar sevgili Django!"

Yukarıdaki örnekte, parametresi olmayan bir işlevi parametresiz_selamlama() kodu ile değil, farklı bir yöntemle çağırdık. Peki neden böyle bir şeye ihtiyaç duyduk? Pek çok sebebi olabilir. Bunun normal bir fonksiyon değilde, bir sınıf yöntemi olduğunu varsaydığımızda bunu bir MVC uygulaması geliştirirken kullanabilirdik.

Peki ilk başlarda tanımladığımız parametreli işlevleri nasıl çağıracağız:

// artık var_dump işlevini kullanmadan yazacağım, görsel olarak karışık görünmesin diye

call_user_func_array('selam_ver', ['Ali']); // "Selamlar sevgili Ali"
call_user_func_array($selamVer, ['Renas']); // "Selamlar sevgili Renas"
call_user_func_array('Selamlayici::selamla', ['Deniz']); // "Selamlar sevgili Deniz"
call_user_func_array([new Selamlayici, 'selamla'], ['Nar']); // "Selamlar sevgili Nar"
call_user_func_array('parametresiz_selamlama'); // "Selamlar sevgili Django!"

Yukarıda daha normal biçimde çağırdığımız işlevleri, şimdi başka (call_user_func_array) bir işlev aracılığıyla çağırdık. Anonim bir işlevi şu şekilde de çağırabiliriz:

call_user_func(function(){
    echo 'Hoş geldiniz';
});

Peki, anonim işlevleri gerçek hayatta nasıl kullanacağız? En basit haliyle şunu yazalım:

function ozel_selamlama(string $isim, callable $ozelSelamla): string
{
    return call_user_func_array($ozelSelamla, [$isim]);
    // veya $ozelSelamla($isim); bu ikisi aynı şey
}

ozel_selamla('Ali', function($isim): string {
    return "Bu farklı bir selamlama biçimi sevgili {$isim}!";
}); // "Bu farklı bir selamlama biçimi sevgili Ali!"

Yukarıdaki örnekte, özel bir selamlama mesajı vermek için iki parametreli bir işlev/fonksiyon oluşturduk. İlk parametre selamlanacak kişinin adını alıyor, ikinci parametre ise nasıl selamlanacağını ayarlayan anonim bir işlevi alıyor.

Başka bir örnek daha verelim. Bu sefer daha spesifik bir örnek verelim:

function eklemeli_selamla(string $isim, ?callable $ek = null): string
{
    $selamlama = "Selamlar sevgili $isim";

    if (!is_null($ek)) {
        return $selamlama . $ek($isim);
        // veya:
        // return $selamlama . call_user_func_array($ek, [$isim]);
    }

    return $selamlama;
}

eklemeli_selamlama('Deniz'); // "Selamlar sevgili Deniz"
eklemeli_selamlama('Deniz', function($isim): string {
    return ". Adının $isim olduğunu biliyorum!";
}); // "Selamlar sevgili Deniz. Adının Deniz olduğunu biliyorum!"

Şimdilik bahsedeceklerimiz bunlar. Bu yazının bir devamı daha olacak. Devamı niteliğindeki yazıda ise Closure sınıfı, sınıfların bir işlev olarak çağırılabilmesi ve Reflection sınıfından bahsetmeyi planlıyorum.

İyi çalışmalar.

Kategoriler
Teknik

PHP ile Kelime/Metin/Dizge İşlemleri

PHP ile kelime işlemleri yapabilmek için birçok fonksiyon bulunmaktadır. Bunları hepsine, PHP’nin resmi sitesinden Dizge İşlevleri başlığından göz atabilirsiniz. Ben burada size, çok sık kullanılan ve ihtiyaç duyulan kelime/dizge fonksiyonlarından bahsedeceğim.

PHP ile Kelime Değiştirme İşlemleri

PHP ile bir metindeki herhangi bir kelime veya kelimeleri değiştirmek için str_replace fonksiyonu kullanılmaktadır. PHP’de kelime işlemleri oldukça kolay ve basittir.

$cumle = 'Merhaba sevgili kardeşim, nasılsın?';
$eski = 'kardeşim';
$yeni = 'dostum';

$yeniCumle = str_replace($eski, $yeni, $cumle);
// sonuç: Merhabs sevgili dostum, nasılsın?

PHP ile Birden Fazla Kelime/Metin Değiştirmek

Aynı fonksiyon ile birden fazla kelime değiştirme yapılabiliyor. Şöyle ki:

$cumle = 'Bugün Pazartesi ve yarın ilk iş kod yazmak olacak.';
$eski = ['Bugün', 'yarın'];
$yeni = ['Yarın', 'şimdi'];

$yeniCumle = str_replace($eski, $yeni, $cumle);
// sonuç: Yarın Pazartesi ve şimdi ilk iş kod yazmak olacak.

Eğer bir sözlük gibi, bütün kelimeleri değiştirmek istiyorsak strtr fonksiyonu da işimizi fazlasıyla çözecektir. Şöyle ki:

$cumle = 'Selamlar, nasılsınız? Orada havalar nasıl? Umarım iyidir. Sevgiler.';
$eskiYeni = [
    'Selamlar' => 'Merhaba',
    'havalar' => 'durumlar',
    'iyidir' => 'her şey yolundadır',
    'Sevgiler' => 'Saygılar'
];

$yeniCumle = strtr($cumle, $eskiYeni);
// sonuç: Merhaba, nasılsınız? Orada durumlar nasıl? Umarım her şey yolundadır. Saygılar.

PHP ile Kelime Bölme/Parçalama ve Birleştirme İşlemleri

PHP’de kelime işlemleri arasında en çok ihtiyaç duyulanlardan bir tanesi de kelime/metin bölme (parçalama) işlemleridir. Hemen PHP’de nasıl kelimeleri veya kelime öbeklerini parçalayıp bölebiliriz görelim:

Örneğin virgülle sıralanan bir metini, virgülleri kullanarak birbirinden ayıralım. Bunun için PHP’de explode fonksiyonunu kullanmamız gerekiyor:

$etiketler = 'PHP Dersleri, Metin İşlemleri, Kelime Bölme';
$etiketArray = explode(',', $etiketler);
// sonuç: ['PHP Dersleri', ' Metin İşlemleri', ' Kelime Bölme'];
// yani 3 öğeli bir dizemiz oldu. Fark ettiyseniz bazılarının başında boşluk var. Bunları süzelim:

// array_map fonksiyonu bir dizeyi düzenlememize, süzmemize yarar (ek bilgi)
$etiketArray = array_map(function($kelime){
    // trim fonksiyonu dizgenin sağındaki ve solundaki boşlukları siler
    return trim($kelime);
}, $etiketArray);

İkinci Örnek: Kelimeleri Bölüp/Parçalayıp Tekrar Birleştirmek

İkinci örneğimizde ise, kelimeleri bölüp, farklı şekilde tekrar parçalarını birleştireceğiz. Elimizde yine virgülle ayrılmış bir liste olsun ve bunları daha sonra <br> etiketi ile birleştirelim.

$isimler = 'Yılmaz, İlker, Deniz, Ali, Nar';
$isimlerArray = explode(',', $isimler);

// sonuç: ['Yılmaz', ' İlker', ' Deniz', ' Ali', ' Nar']
// Şimdi bu dizeyi alt alta HTML ile listeleyelim

$yeniIsimler = implode('<br>', $isimlerArray);
echo yeniIsimler;

// sonuç: Yılmaz<br> İlker<br> Deniz<br> Ali<br> Nar

substr fonksiyonu ile bölme/kesme

PHP’de substr fonksiyonu, elimizdeki dizgenin bir kısmını elde etmemize yarar. Yani bir aralık belirleyip, dizgenin o aralıklardaki kısmını elde edebiliyoruz. Bu fonksiyonunun en çok kullanıldığı yer, yazı kısaltma, metin kesme işlemleri oluyor. Örneğin bir blog yazısına "devamını okumak için tıklayın" seçeneği ekleyecekseniz, evvela substr fonksiyonu ile birkaç işlem yapmalıasınız.

UTF-8 destekli olması ve türkçe karakterlerde sıkıntı çıkarmaması açısından mb_substr fonksiyonunu kullanacağız/kullanınız.

Örneklerden ilerleyelim:

$yazi =  'Lorem lipsum dolor sit amet';
$ilk3 =  mb_substr($yazi,  0,  3);  // Lor
$son3 =  mb_substr($yazi,  -3);  // met
$ilk3haric =  mb_substr($yazi,  3);  // em lipsum dolor sit amet
$son3haric =  mb_substr($yazi,  0,  -3);  // Lorem lipsum dolor sit a

Peki gerçek hayatta bunu nasıl kullanabiliriz. Bir blog gönderisinin çok uzun olduğunu ve bunu kesmemiz gerektiğini düşünelim. Şöyle bir yol izlemeliyiz:

$yazi = 'Lorem lipsum dolor sit amet';
$yazi = substr($yazi, 0, 5);
$yazi .= '...';
$yazi .= ' <a href="#">Devamını okumak için tıklayın</a>';
echo $yazi; // Lorem... <a href="#">Devamını okumak için tıklayın</a>

PHP ile Kelime Arama

Bu örneğimizde, PHP ile metin içerisinde kelime veya öbek aramayı işleyeceğiz. Bunun için kullanacağımız strpos ve mb_strpos fonksiyonları olacak. Bu fonksiyonlar bir metin içerisinde bulunan kelimenin indisini (sayısal konumu) döndürür.

Peki neden iki fonksiyon ve bunlar arasındaki fark ne? Birincisi yani strpos fonksiyonu türkçe karakter desteklemez. Yani Türkçe karakterli bir kelimeyi aramak istediğinizde işinize yaramayacaktır ve doğru sonucu vermeyecektir. Bunun yerine UTF-8 destekli olan mb_strpos fonksiyonunu kullanmanız gerekmektedir. Biz örneklerimizde bu fonksiyonu kullanıyor olacağız.

$dizge = 'Merhaba dünya! Bugün hava güzel olacak.';
$nerede = mb_strpos($dizge, 'olacak.');

if ($nerede !== false) {
    echo "Kelime {$nerede}. indiste var!"; 
} else {
    echo "Kelime bulunamadı, taze bitmiş!";
}

// çıktı "Kelime 32. indiste var!" olacak

mb_strpos fonksiyonu, eğer aranılan kelime dizge içerisinde varsa konumunu döndürür yani integer tipinde bir sayı döndürür. Eğer yoksa, false döndürür. Bir kelimenin bir cümle içerisinde olup olmadığını sorgulamak için şunu kullanabiliriz:

if (mb_strpos('Merhaba dünya', 'evren') === false) {
    echo "Kelime yok kardeşim!";
} else {
    echo "Kelime bir yerlerde mevcut";
}

// çıktı "Kelime yok kardeşim!" olacak

Kelimeyi bulup değiştirmek içinse ilk başlıkta anlattığımız str_replace fonksiyonunu kullanabilirsiniz.


Şimdilik en çok kullanılanlardan bazılarına burada yer verdim. Bunun dışında PHP ile kelime sayma, kelime silme ve kelime yineleme gibi birçok işlev mevcut. Talep olursa bunlar hakkında da detaylı bir yazı yazabilirim. Umarım işinize yarar.

Kolay gelsin.

Kategoriler
Teknik

DemirApp PHP 7 Uygulama Çatısı

Başlarken

DemirApp uygulama çatısı, kendi ihtiyaçlarımdan yola çıkarak yazdığım, basit ve minimal tutmaya çalıştığım bir uygulama. Küçük ve orta çaplı uygulamaları rahatça geliştirebileceğiniz bu PHP çatısında, PHP ile kod yazarken sıkça ihtiyaç duyduğum metod ve fonksiyonları bir araya getirdim. Umarım faydalı olur.

https://github.com/yidemir/App

Gereksinimler

  • PHP 7.1 ve üzeri
  • JSON yardımcısı
  • Apache, NGINX gibi bir sunucu

Kurulum

Kurulum için önerim, composer paket yöneticisini kullanmanızdır. Eğer kullanmıyorsanız, projenin Github kaynağından indirerek de kullanabilirsiniz.

Composer ile kurulum

$ composer require yidemir/app

ve kodlamaya başlayın:

<?php

use DemirApp;

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

App::get('/', function(){
    echo 'Merhaba dünya!';
});

App::run();

Git ile kurulum

$ git clone https://github.com/yidemir/App.git

App dizininin içine index.php dosyası oluşturun ve çalıştırın:

<?php

use DemirApp;

require __DIR__.'/src/App.php';

App::get('/', function(){
    echo 'Merhaba dünya!';
});

App::run();

İndirip, dosya sisteminden kullanım

Github’da yayımlanan sürümler sayfasından projeyi indirip, Git ile kurulum bölümünde bahsedildiği gibi kodlamaya başlayabilirsiniz.

Test

Uygulamayı test etmek için composer ve phpunit gerekmektedir.

$ composer test

Rota İşlemleri

DemirApp uygulamasında GET, POST, PATCH, PUT, DELETE ve ANY olmak üzere 6 istek metodu kullanılabilir (veya kendiniz de oluşturabilirsiniz). Bunlardan ANY metodu, herhangi bir istek metodunu ifade ediyor.

Örnek rota tanımlama:

App::map('GET', '/', function(){
    echo 'Selam!';
});

Bu şekilde rotayı haritalarsak / yolunda ve GET metodunda geri çağırım işlevimiz çalışacak ve ekrana ‘Selam!’ çıktısını verecektir. Aynı zamanda şöyle de kullanılabilir ve kullanılması önerilir:

App::get('/', function(){
    echo 'Selam!';
});

Yukarıdaki yazılan kod ile bu, aynı işlevi görür. Birden fazla istek metodunu cevaplamak içinse, yine map metodunu kullanabiliriz.

App::map(['GET', 'POST'], '/', function(){
    echo 'Selam!';
});

İlk parametreye dizi halinde gönderip, içerisine istediğimiz istek metodlarını yazdığımızda, cevabımızı ona göre alırız. Bütün isteklere cevap vermek için any metodunu kullanabiliriz:

App::any('/', function(){
    echo 'Selam!';
});

Geri Çağırım Parametresi

Rota tanımlarken, geri çağırım parametresi, anonim bir fonksiyon, normal bir fonksiyon, Sınıf:metod şeklinde bir tanımlama veya doğrudan bir sınıf olabilir.

Anonim Fonksiyon:

App::get('/', function(){
    echo 'Selam!';
});

Normal Fonksiyon:

function home(){
    echo 'Selam!';
}

App::get('/', 'home');

Sınıf:metod işareti

class HomeController
{
    public function index()
    {
        echo 'Selam!';
    }
}

App::get('/', 'HomeController:index');

Sınıf:

class Home
{
    public function __invoke()
    {
        echo 'Selam!';
    }
}

App::get('/', new Home);

Geri Çağırım Dizesi:

class HomeController
{
    public function index()
    {
        echo 'Selam!';
    }
}

App::get('/', [new HomeController, 'index']);

Bu yöntemlerin hepsi kullanılabilir.

Rota İsimlendirme

Rota ve rota grupları tanımlarken, geri çağırım parametresinden sonra gelen parametre, rotanın ismini işaret eder.

App::get('/homepage', 'AppControllersHomeController:index', 'home');

App::url('home'); // '/homepage' değeri dönecektir

Ara Katman İşlevi (Middleware) Ekleme

Tam manasıyla bir ara katman mimarisi olmasa da, basit bir biçimde rotalara ve rota gruplarına ara katman işlevleri, rota isminden sonraki parametrede dize halinde eklenebilir. Örnek:

class CheckAdmin
{
    public function __invoke()
    {
        if ('admin' !== 'user') {
            exit('Please login');
        }
    }
}

function check_admin()
{
    if ('admin' !== 'user') {
        exit('Please login');
    }
}

App::get('/', new Home, 'home', ['check_admin', new CheckAdmin]);

Yukarıdaki örnekte iki tane middleware tanımladık. Birisi fonksiyon diğer sınıf.

Rota Yolu Parametreleri

DemirApp’te rota yolları düzenle ifadelerle (Regular Expressions) oluşturulup, istek zamanı eşleştirilir. Yalnız, sık kullanılan rota yolu parametreleri hali hazırda belirli öbeklerle kullanılabilir.

Karşılaştırma:

:id ve :number öbeği rakamları ifade eder
:string öbeği dizgeyi ifade eder
:slug öbeği kalıcı bağlantıyı ifade eder
:any herhangi bir değeri ifade eder
:all bütün her şeyi ifade eder

Örnekler:

App::get('/post/:id', function($id){
    echo $id;
});
// /post/5 ile eşleşir
// $id değeri 5 olur

App::get('/post/:slug', function($slug){
    echo $slug;
});
// /post/merhaba-dunya-bu-ilk-yazim ile eşleşir
// $slug değer merhaba-dunya-bu-ilk-yazim olur

App::get('/post/tag/:string', function($tag){
    echo strip_tags($tag);
});

// /post/tag/Yeni_Yazılar ile eşleşir
// $tag değeri Yeni_Yazılar olur

App::post('/hello/:any', function($name){
    echo strip_tags($name);
});
// /hello/Demir<script> ile eşleşir
// $name değeri Demir<script> olur,
// güvenlik için süzmek gerekir

App::get('/:all', function($all){
    echo $all;
});
// /merhaba/dunya/ve/url ile eşleşir
// $all değer merhaba/dunya/ve/url olur

App::post('/name/([demir]+)', function($param){
    echo $param;
});
// /name/demir ile eşleşir
// /name/imedr ile eşleşir
// /name/none ile eşleşmez

App::delete('/(post|category)', function($whichOne){
    $module = $whichOne;
});
// /post ile eşleşir
// /category ile eşleşir
// /setting ile eşleşmez

Rota Gruplandırma

DemirApp’te, rotaları gruplandırmak için group metodu kullanılır.

App::group('/admin', function(){
    App::get('/', 'AdminDashboardController:index');
    App::get('/posts', 'AdminPostController:index');
});

// '/admin' isteğinde DashboardController:index
// '/admin/posts' isteğinde PostController:index çalışır

Aynı şekilde, geri çağırım işlevinden sonra gelen parametre grup adını, sonraki parametre ise ara katmanları (middleware) alır. Örnek:

function check_admin()
{
    if ('admin' !== 'user') {
        exit('Please login');
    }
}

App::group('/admin', function(){
    App::get('/', 'AdminDashboardController:index', 'home');
    App::get('/posts', 'AdminPostController:index', 'posts.index');
}, 'admin.', ['check_admin']);

App::url('admin.posts.index'); // '/admin/posts' değer döner

Kaynak Kontrolcü Tanımlama

DemirApp’te rota tanımlarken, tek tek tüm istek metodlarını, ekle-oku-düzenle-sil (crud) işlevlerini tanımlamaya alternatif bir yöntem olan resource metodu bulunur.

Bu metod üç parametre alır. Birisi rota yolu, diğer geri çağırım işlevi veya sınıf, diğeri ise varsa ara katmanlar. Örneğimizi görelim:

App::resource('/admin/posts', AppControllersPostController::class);
// veya
App::resource('/admin/posts' 'AppControllersPostController');
// veya
App::resource('/admin/posts', new AppControllersPostController);
// birçok şekilde tanımlama yapılabilir
// üçüncü parametre ara katmanları alır

App::resource('/posts', 'PostController', ['check_post_owner']);

Kaynak kontrolcü tanımlarken, rota isimlendirmeleri otomatik yapılır. /admin/posts için admin.posts isimlendirmesi yapılır ve sonrasında işlevin adı gelir.

App::resource('/posts', 'PostController'); için oluşturulan rotalar şunlardır.

Metod URI Kontrolcü Aksiyonu Rota ismi
GET /posts index posts.index
GET /posts/create create posts.create
POST /posts store posts.store
GET /posts/:id show posts.show
GET /posts/:id/edit edit posts.edit
PATCH /posts/:id update posts.update
DELETE /posts/:id destroy posts.destroy

Konteyner İşlemleri

DemirApp, içerisinde basit bir konteyner (bağımlılık yöneticisi, dependency injection) bulundurur. Örnekler:

App::container()->set('name', 'Demir');
App::container()->get('name'); // Demir değeri dönecektir
App::container()->has('name'); // true

// Bir sınıf veya işlev tanımlamak isteyebiliriz:
App::container()->set('obj', function(){
    $obj = new stdClass();
    $obj->name = 'Demir';
    $obj->type = 'App';
    return $obj;
});

$object = App::container()->get('obj'); 
echo $object->name; // Demir çıktısı alınır

Konteyner içinde statik halde bir öge de saklanabilir.

App::container()->singleton('pdo', function() : PDO {
    return new PDO('sqlite:db.sqlite');
});

var_dump(
    App::container()->get('pdo') === App::container()->get('pdo')
); // true

Konteyner içine tanımlama yaparken, parametre de gönderilebilir. Tanımalama sırasında verilen ilk parametre konteynerin kendisidir, sonrakiler kullanıcı parametreleridir.

App::container()->singleton('pdo', function($container, string $dsn, string $user, string $password) : PDO {
    return new PDO($dsn, $user, $password);
});

// çağırırken

$pdo = App::container()->call('pdo', ['mysql:host=localhost;dbname=test', 'root', 'root']);

Kısa Kullanım

İstendiği taktirde getirme ve tanımlama işlevleri daha kısa kullanılabilir:

// Tanımlama:
App::container(['name' => 'Demir']);

// Getirme:
App::container('name'); // Demir

// İkinci parametre yoksa, varsayılan değeri alır:
App::container('nameless', 'DemirApp'); // DemirApp

Bu kullanım, singleton ve call metodları ile uyumlu değildir.

Talep/İstek (Request) İşlemleri

DemirApp’te istek işlemleri basit işlemler yapmak için oldukça kullanışlıdır. Örnekler:

App::request()->params(); // Bütün parametreleri döndürür
App::request()->all(); // params metodu ile aynı işlev
App::request()->param('page'); // page parametresi döner
App::request()->param('page', 5); // page parametresi yoksa 5'tir
App::request('page'); // param metodu ile aynı işlev
App::request('page', 5); // param metodu ile aynı işlev
App::request()->only('title', 'body', 'created_at'); // sadece belirtilen parametreler döner
App::request(['title', 'body', 'created_at']); // only metodu ile aynı işlev
App::request()->has('title'); // boolean döner, title var mı yok mu
App::request()->get('page', 5); // $_GET verisinden değer alır, yoksa varsayılan değer olan 5 döner
App::request()->post('title', 'Başlık'); // $_POST verisinden değer alır, yoksa varsayılan değer olan 'Başlık' döner
App::request()->file('image'); // $_FILES verisinden değer alır
App::request()->method(); // string Geçerli istek metodunu döndürür
App::request()->method('get'); // boolean İstek metodu belirtilen değer mi kontrol eder
App::request()->isAjax(); // boolean Ajax isteği mi kontrol eder

Session İşlemleri

Session metodu basit oturum işlemlerini yönetmemize yarar.

App::session('session-key'); // oturum değeri döner
App::session(['session-key' => 'value']); // oturum değeri belirler

Session işlemlerini gerçekleştirebilmek için evvela oturumun başlatılmış olması gerekir.

App::session()->start(); // başlatır
App::session()->set('key', 'value'); // belirler
App::session()->get('key', 'default'); // döndürür
App::session()->has('key'); // kontrol eder
App::session()->destroy('key'); // kaldırır
App::session()->destroy(); // bütün verileri kaldırır

Flaş Mesajları

Session metodunun içerisinde bir de flash metodu bulunmaktadır. Flaş mesaj sistemi, yönlendirme yapmadan evvel belirlenip, daha sonra görünüm/view sayfasında görüntülenip kaybolurlar. Örnek:

App::session()->flash('Örnek bir flaş mesajı');
// bu şekilde belirlenen bir flaş mesajın varsayılan türü 'message'dır.
// istediğiniz türde flash mesajları belirleyebilirsiniz
// belirlenen flaş mesajlar, dize içerisinde depolanır
// diğer örnekler

// doğrudan flash metoduyla da işleminizi yapabilirisiniz
App::flash('Başlık alanı gereklidir', 'formError');
App::flash('Gövde alanı gereklidir', 'formError');
// Flash mesajlarını view'da elde etmek için:
$formErrors = App::flash()->get('formError');
// ['Başlık alanı gereklidir', 'Gövde alanı gereklidir']

Flaş sistemi içerisinde iki tane türü mevcut halde bulundurur. Bunlar message ve error türleridir. Şu şekilde kullanılır:

App::flash()->error('Birinci hata');
App::flash()->error('İkinci hata');

$errors = App::flash()->getErrors();
// ['Birinci hata', 'İkinci hata']

App::flash()->message('İşleminiz başarıyla tamamlandı');
$messages = App::flash()->getMessages();
// ['İşleminiz başarıyla tamamlandı']

Hata Yönetimi

DemirApp’te hata yönetimi için hali hazırda 404 sayfa bulunamadı ve istisna işleyicisi bulunmaktadır. Şu şekilde kullanılır:

App::notFound(function(){
    http_response_code(404);
    App::render('404'); // 404 görünümünü yorumlar
    // veya
    // echo '404 Sayfa Bulunamadı';
});

İstisna işleyicisi ise şu şekilde kullanılabilir:

App::error(function($error){
    http_response_code(500);
    if (App::container('debug') === true) {
        echo $error->getMessage();
        echo '<br>';
        echo $error->getFile();
        echo '<br>';
        echo $error->getLine();
    } else {
        echo 'Bir uygulama hatası meydana geldi';
    }
});

JSON Yanıtı

Bir rotada tek bir metod ile JSON yanıtı verilebilir.

App::get('/api/posts', function(){
    $posts = Post::all();
    return App::json($posts);
});

// veya 404 sayfası için:

App::notFound(function(){
    http_response_code(404);
    if (App::request()->isAjax()) {
        return App::json(['message' => '404 page not found', 'status' => 404]);
    } else {
        echo '404 page not found';
    }
});

Yönlendirme

App::get('/', function(){
    App::redirect('homepage');
});

App::get('/home', function(){
    echo 'welcome to homepage';
}, 'homepage');

Parametreli yönlendirme:

App::get('/post/:id', 'PostController:show', 'show.post');
App::redirect('show.post', 5);

URL Oluşturma

DemirApp ile isimlendirilmiş rotalar için URL oluşturulabilir.

App::url('homepage'); // '/'

App::get('/:slug/:slug', 'show_function', 'show.post');
App::url('show.post', 'category-slug', 'post-slug');
// '/category-slug/post-slug'

// diğer bir örnek:
App::post('/comment/save/:id', 'save_comment', 'save.comment');
App::url('save.comment', 5); // '/comment/save/5'

Görünüm (View) İşlemleri

DemirApp’te hali hazırda bir Görünüm sınıfı bulunur. Bu sınıfla görünüm dosyaları yorumlanıp, bloklar oluşturulup görüntülenebilir. Basit bir kullanıma sahiptir. Öncelikle bir view dosyasını nasıl yorumlarız ona bakalım.

App::get('/post/show/:id', function($id){
    $post = Post::find($id);
    App::render('post/show', ['post' => $post]);
});

Burada bir gönderi görüntüleme senaryosu hazırladık. $post değişkeni içerisinde bulunan gönderi değerlerini post/show.php dosyasına gönderdik. render yani yorumlamanın birden fazla yöntemi vardır:

echo App::view('post/show')->with('post', $post);
echo App::view('home')->render();
App::view('home')->show();
App::view('home')->with('name', 'Yılmaz')->show();

Temel Görünüm Ekleme ve Kullanma

Görünüm dosyalarını kullanmadan evvel, bu dosyaların hangi dizinde bulunduğunu belirlemeniz gerekiyor. Bunun için sınıf içerisinde bulunan container metodundan faydalanacağız.

App::container(['views.path' => __DIR__ . '/views/dosyalarinin/dizini']);

Artık görünüm dosyalarının bulunacağı dizini işaretledik. App::render('deneme') ifadesi, views/dosyalarinin/dizini/deneme.php dosyasını yorumlayacaktır.

Blok/Bileşen Tanımalama ve Kullanma

DemirApp ile temel görünüm dosyası oluşturup diğer görünüm dosyalarınızda bunu rahatlıkla kullanabilirsiniz.

views/page-layout.php:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title><?= $this->has('title') ? $this->get('title') : 'DemirApp' ?></title>
</head>
<body>
    <header>Header</header>
    <main><?= $this->get('content') ?></main>
    <footer>Footer</footer>
    <?= $this->get('scripts') ?>
</body>
</html>

has metodu, blok değerinin tanımlanıp tanımlanmadığını kontrol eder. get metodu tanımlanan bloğu döndürür. Tanımlama yapmak için örnekleri görelim:

views/home.php:

<?php $this->layout('page-layout') ?>
<?php $this->set('title', 'Ana Sayfa') ?>
<p>Sayfa içeriği buraya gelecek</p>

<?php $this->start('scripts') ?>
<script>
    console.log('javascript içeriği')
</script>
<?php $this->end() ?>

Burada evvela home.php‘nin hangi temel görünümü kullandığını layout metodu ile belirledik. set metodu ile, title değerini tanımladık. Sonrasında start ve end metodlarıyla scripts bloğunu tanımladık.. set ve start metodları arasında ince bir fark vardır. set metodu, ikinci parametresine ne verilirse onu alır. Ama start metodu ile başlatıp end metodu ile bitirdiğimizin arasına ne koyarsak blok içeriği o olur. Bunu çıktı tamponlaması ile yapar (ob_start).

Çıktı tamponlaması yaparak belirlenen blok varsayılan olarak varsa eski blok içeriğine iliştirilir (append). Eğer iliştirmek istemiyorsanız yani üzerine yazmak istiyorsanız end metodunun ilk parametresine false değeri verebilirsiniz. Örn: $this->end(false);

set metodunu varsayılan olarak değeri üzerine yazar. Yazmasını istemiyorsanız üçüncü parametresine true değeri verebilirsiniz. $this->set('title', 'Deneme', true);

Yardımcı Fonksiyonlar

Eğer DemirApp’i composer aracılığıyla kullanıyorsanız, otomatik olarak yüklenen yardımcı fonksiyonları kullanabilirsiniz. Eğer manuel olarak kullanıyorsanız kaynak dosyaları içindeki helpers.php dosyasını sayfaya dahil etmelisiniz. Aşağıda genel kullanım örnekleri mevcuttur.

session()->start();
session(['is_logged_in' => false]);
session('is_logged_in');

container()->set('views.path', __DIR__ . '/views');

app()->get('/', function(){
    render('home');
});

// app() fonksiyonu DemirApp örneğini döndürür

app()->notFound(function(){
    if (request()->isAjax()) {
        return json(['status' => 404]);
    } else {
        return view('errors/notFound')->show();
    }
});

app()->post('/save', function(){
    if (empty(request()->post('title'))) {
        flash()->error('Başlık alanı gereklidir');
        redirect('post.create');
    }
});

// URL oluşturma
// url('post.show', 5);

run();

Soruları aşağıdaki yorum bölümünden sorarsanız severek cevaplarım. Umarım işinize yarar. Sevgiler.

Kategoriler
Teknik

PHP’de Rota Sistemi ile Basit ve Hızlı Uygulamalar

Daha önceden PHP İle Basit Bir Router Nasıl Yapılır başlıklı bir yazı yazmıştım. Bu yazıda nesne yönelimli programlama ile çok basit bir rota sistemi yazmıştık. Şimdiyse bunun daha basit halini yazacağız.

Hemen bir app.php dosyası oluşturup kodlarımızı yazmaya başlayalım. Çok az kod yazacağız ama çok işleve sahip olacak. Kod satırlarında ne işe yaradıklarını, ne için yazdığımızı vs. anlattım. Es geçmemenizi öneririm.

<?php

// Oluşturduğumuz rotaları tutacak değişken
$routes = [];

/**
 * @param string $method Rotanın istek metodu (GET, POST gibi)
 * @param string $path URL yolu (/merhaba mesele)
 * @param callable $callback Anonim işlevimiz yani /merhaba sayfasında çalışacak fonksiyon
 * @return void herhangi bir şey döndürmeyecek
 */
function router_map(string $method, string $path, callable $callback)
{
  // Fonksiyonun dışındaki değişkene erişmek için global'i kullanıyoruz
  global $routes;

  // routes değişkenine yeni eleman ekliyoruz
  $routes[] = [$method, $path, $callback];
}

// Rota sistemini çalıştıracak olan fonksiyon
function router_run()
{
  global $routes;

  // oluşturduğumuz rotaları döngüye sokup tek tek işleyeceğiz
  foreach ($routes as $route) {
    /**
     * Her bir rotanın içindeki verileri list fonksiyonu ile
     * bir değişkene aktarıyoruz. Bu yaptığımız şununla denk:
     * $method = $route[0];
     * $path = $route[1];
     * $callback = $route[2];
     */
    list($method, $path, $callback) = $route;

    if (
      // Eğer tanımlanan method, kullanıcının methodu ile aynıysa
      // yani GET == GET ise sıkıntı yok, devam
      $method == $_SERVER['REQUEST_METHOD'] &&

      // Eğer tanımlanan yol, kullanıcının talep ettiği yol ile aynıysa
      preg_match("@^{$path}$@ixs", $_SERVER['REQUEST_URI'], $params)
    ) {
      // $params değişkenini preg_match fonksiyonu oluşturdu
      // Bu değişken içerisinde varsa parametreler tutulacak
      // yani örneğin: /haber/5 URL'sinde 5 parametre değeridir
      // array_shift fonksiyonu ise $params değişkenindeki ilk 
      // öğeyi kaldırır. Çünkü preg_match parametre verirken 1 tane fazla verir
      array_shift($params);

      // artık fonksiyonumuzu çağırabiliriz
      // aşağıdaki fonksiyon şuna denktir:
      // $callback(...$params);
      return call_user_func_array($callback, $params);
    }
  }

  // Eğer döngüde herhangi bir şey dönmezde
  // sayfa bulunamadı mesajı ver ve uygulamadan çık
    http_response_code(404);
  exit('404 Sayfa Bulunamadı');
}

/**
 * @param string $name View dosyası adı
 * @param array $data View dosyası için çıkarılacak değişkenler
 * @return string
 */
function view_render(string $name, array $data = [])
{
  // View dosyalarının bulunduğu dizin
  $path = __DIR__ . '/views';

  // Eğer dosya mevcutsa
  if (is_file($fullPath = "$path/$name.php")) {

    // Çıktı tamponlamayı başlatalım
    // ob_start() ve ob_get_clean() arasında yazılan kodlar
    // gösterilmez, hafızaya alınır. ob_get_clean bu veriyi döndürür
    ob_start();

    // extract(['isim' => 'Yılmaz']) şeklinde bi kullanım yaparsanız
    // $isim = 'Yılmaz'; yapmış gibi olursunuz. Yani extract fonksiyonu
    // dizedeki verileri değişkene döndürür
    extract($data);

    // Görünüm dosyasını çağıralım
    require $fullPath;

    // Yaptığımız işlemi döndürelim
    return ob_get_clean();
  }
}

// Bütün uygulama kodları bu kadar!

Çekirdek uygulamamızı yaptık. Artık bunu her sayfaya include ederek kullanabileceğiz. İlk örneklerimizi yapalım:

index.php

<?php

require 'app.php';

router_map('GET', '/', function(){
  echo 'Burası giriş sayfası';
});

router_map('GET', '/test_et', function(){
  echo 'Burası test sayfası';
});

// (d+) ifadesi bir rakamı ifade eder
// parantez içinde olmasının sebebi bunu parametre olarak tanımlamak içindir
// /yazi/d+ ile de eşleşir ama parametre olarak çıkmaz
router_map('GET', '/yazi/(d+)', function($id){
  echo 'Burası yazı sayfası<br>';
  echo "$id numaralı yazı gösterilecek";
});

// Form verisini işlemek için POST metodu kullanıyoruz
router_map('POST', '/yazi/kaydet', function(){
  echo 'Burası yazıyı kayıt sayfası';
});

router_run();

Bunu da çalıştırıp testlerimizi yapalım. http://localhost , http://localhost/test_et , http://localhost/yazi/5 sayfalarına erişmeyi deneyelim ve sonuçları gözlemleyelim.

Her şey yolundaysa, doğru yazdınız demektir. Gelelim bir de olayın View yani görünüm yorumlayıcısına. view_render fonksiyonu ile yapacaklarımız oldukça işimize yarayacak. Öncelikle dosyaların bulunduğu dizinde views klasörü oluşturalım ve içerisinde merhaba.php dosyası oluşturalım. İçerisine Merhaba sevgili <?= $name ?>, <?= $age ?> yaşında olduğun söyleniyor! yazıp kaydedelim ve ilk denememizi bir rota oluşturup yapalım:

router_map('GET', '/merhaba_de/(w+)', function($urlDenGelenIsim){
  echo view_render('merhaba', [
    'name' => $urlDenGelenIsim,
    'age' => 35
  ]);
});

Hemen http://localhost/merhaba_de/Yilmaz sayfasına girelim. Türkçe karakter kullanmayalım ama yoksa 404 döner, çünkü regexi bu türkçe karakterli yazmadık.

Bunu da yaptıktan sonra işler sizin hayal gücünüze kalıyor. Bu çok basit bir uygulama. Bunu ufak uygulamalarınızda kullanabilirsiniz de. Ama normal bir uygulama için hiç iyi bir çözüm değil. Geliştirmeniz gerekir. Nasıl geliştirebilirsiniz? En basit örnek şu:

function router_get(string $path, callable $callback)
{
  return router_map('GET', $path, $callback);
}

router_get('/', function(){
  echo 'selam!'
});

Böylelikle GET metodu tanımlarken fazla uğraşmamış olacaksınız. Kalın sağlıcakla. Sorularını sormaktan çekinmeyin, seve seve cevaplarım.