23 Eylül – 11 Ekim tarihleri arasında internet ve telefon iletişimim olmayacaktır.
I won’t have internet & cell connection between 23th September and 11th October.
23 Eylül – 11 Ekim tarihleri arasında internet ve telefon iletişimim olmayacaktır.
I won’t have internet & cell connection between 23th September and 11th October.
Mysql 5.7 ile gelen native bir method ile, verilen 2 konum (latitude, longitude noktası) arasındaki uzaklığı metre cinsinden ölçebilirsiniz. Tanıştırayım: ST_Distance_Sphere
Örneğin, üyelerinizin 41.0049823,28.7319855 konumuna uzaklığını ölçmek istiyorsunuz. Hızlıca bir sorgu yazalım:
<?php
$latitude = 41.0049823;
$longitude = 28.7319855;
$sql = "SELECT users.id, users.username, ST_Distance_Sphere(point(users.longitude, users.latitude),point({$longitude}, {$latitude})) as distance FROM users"
Sorgu çıktısına, verilen konumdan uzaklığı metre olarak eklenecektir. Dikkat etmeniz gereken, longitude-latitude ikilisi olarak sorgu yapılıyor.
Verilen noktaya 100 metre çapındaki uzaklıkta olan kullanıcıları da listeleyebilirsiniz, neden olmasın?
<?php
$latitude = 41.0049823;
$longitude = 28.7319855;
$sql = "SELECT users.id, users.username, ST_Distance_Sphere(point(users.longitude, users.latitude),point({$longitude}, {$latitude})) as distance FROM users WHERE distance < 100"
Bu methodu bilmeden önce sinüsler, kosinüler, tanjantlar filan havada uçuşuyordu. Hem yavaş çalışıyordu, hem de doğru sonuç vermiyordu. Bu methodu öğrenince dünyam aydınlandı. Teşekkürler ST_Distance_Sphere
Kısa bir süre önce Symfony ekibi, composer ile kullandığımız PHP paketlerine teşekkür edebileceğimiz bir kütüphane geliştirdi. https://github.com/symfony/thanks adresinden ulaşabileceğiniz kütüphaneyi bağımlılıklarınız arasına ekledikten sonra, composer thanks
diyerek composer.json
dosyanızda dahil ettiğiniz tüm paketleri Github üzerinde beğenmiş oluyorsunuz. Paketinin beğenilmesi de bir geliştirici için oldukça iyi bir teşekkür olsa gerek. Daha iyisi için: teşekkür amaçlı bir e-posta gönderin veya bağış yapın.
Aklıma geldikçe ekleyeceğim :)
Aslında sadece yanıt süresini loglamayı anlatıcaktım ama burada işimiz Middleware ile olacağı için onun da ne olduğunu anlatayım istedim. Middleware, kelime anlamıyla, “ara katman – orta katman” anlamına gelir. Kelime anlamıyla çok uyumlu bir programlama terimi. Bu sefer Laravel’in bir uydurması değil, zaten olan birşey. Bu yazımda “Laravel süper isimlendiriyor yawww” diye övmeyeceğim.
Laravel’de middleware, HTTP istekleri ile isteğin gerçekleşmesi arasında ya da http isteği gerçekleştikten sonra çalışacak şekilde durur. Şöyle ki, Laravel ile yazılmış site.com/hakkimizda sayfasını açmak istediğinizde, Laravel önce router dosyasına bakar, işler ve ilgili controller methodunu çalıştırır. Hah, işte tam bu noktada; router isteği işledikten sonra, controller’a yönlendirmeden önce -eğer tanımlandıysa- middleware sınıfımıza yönlendirir. Middleware çalışır, istenilen kontroller/güncellemeler yapılır, eğer middleware izin verirse controller’a yönlendirilir. (controller’a ne kadar acayip bi kelime ya)
Yani middleware, route ile controller arasında bir ARA KATMANdır. Ara katman’ı büyük yazdım çünkü, sadece Laravel’deki kullanımına bakarsak, controller&route arasındaki katman olduğunu zannedebiliriz. Oysa araya girme niyetindeki herşey bir middleware olabilir. Google Translate’teki tanımına bakalım:
“`software that acts as a bridge between an operating system or database and applications, especially on a network.“`
Yani diyor ki, “bir işletim sistemi/veritabanı ve uygulamalar arasında köprü görevi gören yazılımlardır.” Özellikle bir networkte diye belirtmiş. “Bir network”ü ben şu şekilde yorumluyorum: bir sistemde/yapıda. Dahili bir biçimde. Neyse çok detaya girip kafa karıştırmayayım, benim kafam karıştı bile. Yine de girmek isteyen olursa katkılarını eklerim.
Middleware ile ilgili dökümantasyon şurada bulunuyor: https://laravel.com/docs/master/middleware Laravel 2 tip middleware oluşturmuş. Birincisi, aşina olduğumuz request-controller arası çalışan middleware. Diğeri de response verildikten sonra çalışan “terminable” middleware.
Aşina olduğumuz middleware için şu örneği verebiliriz: Girilen sayfaya, bir kullanıcının yetkisinin olup olmadığını kontrol eden middleware. Kodu ise şu şekildedir:
<?php public function handle($request, Closure $next) { //burada gerekli işlemleri yapıyoruz. örneğin üyenin yetkisi var mı yok mu kontrol edebiliriz. //ya da buna benzer şeyler. return $next($request); }
$request
değişkenimiz, IlluminateHttpRequest
sınıfının ta kendisi. Yani bizim o anki isteğimiz ile ilgili herşeyi bilen sınıf. (Bence birisi bu sınıf üzerine bir yazı yazmalı.) O an hangi route çalışıyor, hangi parametreler almış, sessionda neler var, cookiede neler var vs vs gibi onlarca sorunun cevabı bu sınıf içerisinde. Bu bilgiler ışığında istediğiniz kontrolü yapabilirsiniz. Tabi middleware sadece “kontrol etmek” için kullanılmıyor. Aynı zamanda sisteme yeni parçalar da dahil edebilirsiniz. Herşey sizin elinizde.
Bu middleware sınıfı, HTTP sonucu, yani Response, tarayıcıya (istemciye) gönderildikten sonra çalışır. Bitiş middlewarelerinde handle
methodundan farklı olarak bir de terminate
methodu çalışır. Ve bu method $request
sınıfına sahip olmakla birlikte, bir de $response
sınıfına sahiptir. Response sınıfı sayesinde, istemciye ne sonuç gönderildiğini biliriz ve buna göre istediğimiz işlemi yaparız.
<?php public function terminate($request, $response) { // burada bişeyler yapabiliriz. // artık response tarayıcıya gönderildi. // örneğin, responsu kontrol ederiz. diyelim ki 422 sonuç kodu dönüyor. // o zaman bunu loglatalım, gibi fantazi işler yapılabilir. }
Laravel’deki public/index.php
dosyasına baktığımızda, LARAVEL_START adında bir sabit değişken tanımlandığını görürüz. Bu, Laravel’e ilk istek geldiği anda tanımlanır. Yani ilk çalışan kodumuzdur. Şimdi bir de yapmamız gereken, en son çalışma anında süreyi tespit etmek ve aradaki farkı loglara göndermek.
Bunun için standart bir middleware oluşturalım. php artisan make:middleware ResponseTimeMiddleware
güzel bir başlangıç olacaktır. Daha sonra bu middleware’i AppHttpKernel
dosyamızdaki $middleware
dizisi içerisine ekleyelim. Böylece tüm requestlerde bu eklediğimiz middleware çalışmış olacak. Yeni oluşturduğumuz middleware’e aşağıdaki terminate fonksiyonunu ekleyelim:
<?php .... public function terminate(Request $request, Response $response) { \Log::debug($request->getPathInfo().' => '.(microtime(true) - LARAVEL_START)); }
Böylece uygulama başlangıcından itibarek geçen süreyi loglarımıza yazdırmış olduk. Response süresini loglara yazdırıp debug yapmak çok etkili bir yöntem gibi gözükmese de bazen çok hızlı biçimde sorunlu sayfaları bulmanıza yardımcı olabiliyor. Ama ben genelde production ortamında, daha detaylı bilgi veren NewRelic hizmetini kullanıyorum. Hem sayfaların ortalama response sürelerini kolayca öğrenebiliyorum hem de veritabanı sorgularını detaylıca inceleyebiliyorum. Hangi query ne kadar sürmüş vs diye. Tavsiye ederim.
Bir önceki yazımda, yazdığım kodda sürekli tekrar eden kod parçasını Trait içine alıp, güzelleştirdiğimi iddia etmiştim. Peki kodu güzelleştirdiğimi iddia ettiğim bu trait tam olarak nedir?
Kelime anlamına baktığımızda, trait; bir kişinin karakteristik özelliği anlamına gelir. Birebir çevirdiğimizde ise özellik, kişisel özellik diyebiliriz. Programlama özelinde baktığımızda ise, bir sınıfa özellik/yetenek yüklemek için kullanılan kod diyebiliriz.
PHP’de bir sınıf, yalnızca bir üst sınıfa sahip olabilir. Daha anlaşılır bir deyişle, “bir sınıf yalnızca tek bir sınıftan türetilebilir(extend edilebilir)”. Sınıftan türetme işlemini, genellikle türetilen sınıfın özelliklerini(fonksiyonların/methodlarını/değişkenlerini) kullanabilmek için yaparız.
<?php class Admin extends User {} class Admin extends User, Human {} //böyle bir kullanım yok
Fakat zaman zaman yazdığımız sınıfın, birden fazla sınıfın özelliklerine sahip olması gerektiğini görürüz. Yukarıdaki örnekte gördüğümüz üzre, Admin sınıfımız User sınıfından türetilmiş. User’ın tüm özelliklerini kullanabileceğiz demektir. Fakat aynı zamanda Human sınıfının da özelliklerine sahip olmasını istiyoruz. İkinci satırdaki kullanım mümkün değil.
PHP 5.4 ile hayatımıza dahil olan traitler, bu duruma bir nebze çözüm olabilmektedir. Tabi tek amacı buna çözüm olmak değildir. Aynı zamanda kod tekrarının önüne geçmek konusunda bize oldukça yardımcı olur ki, bir önceki yazımda Trait kullanarak tam olarak bunu hedeflemiştim.
<?php trait ValidatesRequests { public function validate($data) { //parametre olarak gönderilen veriyi burada validate edelim. } }
Bir trait, abstract class gibi işlem görür. Yani bir traitten yeni bir obje oluşturamazsınız. new ValidatesRequests()
işe yaramaz. Traitleri bir sınıfı içerisinde ise, Use
keywordu ile kullanırız.
<?php class BilmemneController { use ValidatesRequests; public function deneme() { $data = ['test'] $this->validate($data); } }
Yukarıdaki örnekte açıkça görüleceği gibi, ValidatesRequests traitini use ile dahil ettik. Ve deneme fonksiyonumuz içinde, traitte tanımladığımız validate methodunu kullanabilir duruma geldik.
Çalışma mantığını ise kopyala yapıştır gibi düşünebiliriz. Traiti sınıfa dahil ettiğimiz anda, içerisindeki methodları sanki dahil ettiğimiz sınıfa kopyalayıp yapıştırıyormuş gibi düşünün. Örneğin bu validate methodunu 5 controller dosyasında kullanacağımızı varsayalım.
Her bir dosyaya aynı methodu kopyalamak yerine, bu 5 sınıfa traiti dahil ediyoruz. Böylece kod tekrarının önüne geçiyoruz. Ve tabii ki yazdığımız kodun bakımı/tekrar çok daha kolay oluyor.
Genelde bu tarz bir paragraf göremezsiniz fakat ben oldukça önemsiyorum. Trait kullanımı Laravel’de yoğun ve bence Laravel isimlendirme konusunda oldukça başarılı. Traitlerin karakteristik/kişisel özellik anlamında geldiğini söylemiştik. Trait, kullanılan sınıfa yeni özellikler katar. Laravel’in Controller örneğine bakalım. 2 trait kullanılıyor:
use AuthorizesRequests, ValidatesRequests;
Bir controllera bu 2 traiti dahil ettiğimizde insan dilinde(ama türkçe değil) şu cümleleri kurabiliyoruz:
“Bu controller, requestleri(istekleri) authorize eder(yetkilerini onaylar).”
“Bu controller, requestleri(isteklerin) validate eder(geçerliliğini kontrol eder).”
Bu iki traiti detaylıca incelediğimizde birisinde authorization ile ilgili methodların, diğerinde ise validation ile ilgili methodların bulunduğunu görürsünüz. Yazılımda en zor şeylerden birisi isimlendirmedir, malumunuz. Ne kadar insan diline yakın isimler verebilirseniz, o kadar başarılı olursunuz (bence).
Sevgiyle kalın.
<?php trait Love() { public function love(Human $human) { } } trait Hate() { public function hate(Human $human) { } } class Human() { use Love; }
Buraya kadar geldiğinize göre muhtemelen ne olduğunu biliyorsunuzdur ama yine de Laravel’de UUID’nin nasıl kullanıldığını anlatmaya başlamadan önce kısaca bir UUID tanımını yapalım ve ne olduğunu anlatmaya çalışalım.
UUID, “Universally unique identifier” nin kısaltılmışıdır. Kabaca çevirirsek, evrensel(?) benzersiz tanımlayıcı diyebiliriz. Normalde veritabanında satırları tanımlayıcı olarak integer kullanırız. UUID ise, 36 karakterlik, sayılar ve harflerden oluşan, bir settir. Ve en önemli özelliği, yüksek derecede benzersiz olmasıdır. (Bilgisayarda rastgele üretilen hiçbir şey aslında tam olarak rastgele değildir. Rastgeleliği arttırabiliriz. Değişik bi konu.)
Örnek olarak şöyle bir kaç tane gösterebilirim:
UUID genellikle büyük projelerde işe yarayabiliyor. Biliyorsunuz, veritabanında integer alanının maximum değeri var. Mysql’deki değeleri şuradan görebilirsiniz: https://dev.mysql.com/doc/refman/5.7/en/integer-types.html
Burada minimum-maximum değerlere bir paragraf açıyorum. Veritabanında id alanı, Primary Key olarak tanımlanır. PrimaryKey alanı doğası gereği unsigned olarak tanımlanır. Bu yüzden ilgilendiğiniz alan unsigned olmalı. Peki unsigned değer ne demek? Negatif olmayan sayılar demek. Linkteki tabloya baktığımızda, TinyInt türü için signed [-127, 127] aralığındayken, unsigned [0, 255] aralığında değer alabiliyor.
Primary key, varsayılan olarak INT, unsigned tanımlanır. Bu da demektir ki, o tabloya maksimum 4294967295
kayıt eklenebilir. Sanırsam, 4 milyar 294 milyon 967 bin 295 kayıt. Her ne kadar çok büyük bir sayı olsa da, sonsuz değil. Eğer uuid olarak tanımlarsanız, sonsuz kayıt ekleyebilirsiniz. (Acaba öyle mi? Keşke birisi bunun doğrulamasını yapsa) Maksimum kaç kayıt eklenebileceği ile ilgili yanıt: https://twitter.com/hkanaktas/status/906891710155849730
Bunun haricinde, çok olası değil ama, bir tablonuz dağıtık biçimde çalışıyorsa, primary key alanınızın çakışmasını önler. Farazi bir örnek verelim. Diyelim ki kullanıcının gezdiği sayfaları kayıt altına alıyorsunuz. Öyle ki, çok yoğun bir sistem ve kayıtları %50 A sunucusundaki logs tablosuna, %50 B sunucusundaki logs tablosuna kaydediyorsunuz. Günün sonunda verileri C sunucusunda birleştirmek istediğinizde, id alanı çakışacaktır. Bunun için idleri yeniden vermeniz gerekmektedir. Fakat uuid kullanırsanız, herhangi bir yenileme yapmadan doğrudan verileri birleştirebilirsiniz.
Son olarak şunu söyleyeyim, tahmin edilmesi oldukça zordur. Örneğin kullanıcılar tablomuzda int kullansaydık, üye kayıtları 1,2,3,4,5 diye düzenli olarak gidecekti. Ve emin olun saldırganlar ilk olarak url’deki idleri değiştirerek erişim sağlamaya çalışır. Gerekli kontrolleri aldıysanız sıkıntı olmaz. Fakat uuid ile bu sıkıntı daha da az yaşanır. Çünkü bir sonraki kullanıcının id’si tahmin edilemez hale gelir. Siz yine de arka tarafta gerekli kontrolleri yapmayı unutmayın.
UUID’leri üretmek için ek paket kullanmamız gerekiyor. Ben ramsey/uuid paketini kullanıyorum.
$ composer require ramsey/uuid
Bu paket sayesinde artık UUID üretebiliriz. README dosyasında kullanımı var, ama kısaca ben de buradan tekrar edeyim:
<?php // Generate a version 1 (time-based) UUID object $uuid1 = Uuid::uuid1(); echo $uuid1->toString() . "n"; // i.e. e4eaaaf2-d142-11e1-b3e4-080027620cdd // Generate a version 3 (name-based and hashed with MD5) UUID object $uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'php.net'); echo $uuid3->toString() . "n"; // i.e. 11a38b9a-b3da-360f-9353-a5a725514269 // Generate a version 4 (random) UUID object $uuid4 = Uuid::uuid4(); echo $uuid4->toString() . "n"; // i.e. 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a // Generate a version 5 (name-based and hashed with SHA1) UUID object $uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net'); echo $uuid5->toString() . "n"; // i.e. c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Bu pakette toplam 4 çeşit üretme yöntemi var. Hangisini kullanacağınız konusunda tercih size kalmış. Ben farklarını bilmiyorum, araştırmadım.
Paketi yükledikten sonra, ilk değişikliğimiz migration yazımında karşımıza çıkıyor.
Normalde bir migration oluşturduğunuzda şu şekilde oluyor:
$table->increments('id');
Bu fonksiyonun detayına baktığımızda, unsignedInteger tipinde oluşturduğunu göreceksiniz. Bunun yerine şunu yazmamız daha manidar:
$table->uuid('id')->primary();
ID alanımızın uuid türünde olduğunu, ve bu alanın tablomuzun birincil alanı(reyizi) olduğunu söyleyerek tablomuu oluşturuyoruz. Bu da aslında özel bir kolon türü değil, 36 karakterlik char alanı oluşturuyor tablomuzda.
İlk önce modelin primary key’in otomatik artma özelliğini kapatalım. Eloquent’te primary key, varsayılan olarak int, auto increment olarak kabul edilir. Bunu değiştirelim.
<?php class Bilmemne extends \Illuminate\Database\Eloquent\Model { public $incrementing = false; }
Otomatik arttırmayı kapattık ama, şimdi de bir modeli kaydetmek istediğimizde, otomatik olarak sayı yazmaya çalışacak eloquent. Bunun için Eloquent event’lerinden yararlanıyoruz:
<?php class Bilmemne extends \Illuminate\Database\Eloquent\Model { public $incrementing = false; protected static function boot() { parent::boot(); static::creating(function ($model) { $model->{$model->getKeyName()} = \Ramsey\Uuid\Uuid::uuid1()->toString(); }); } }
Peki bu kod ne yapıyor? $model->save() komutunu çalıştırdığınızda, model verileri, veritabanına kaydedilmek üzere hazırlanıyor. Bu kod sayesinde, modelimiz veritabanına kaydedilmeden önce ‘id’ alanının değerini uuid ile değiştiriyoruz.
Hepsi bu! Artık id alanımız integer yerine uuid olarak kaydediliyor. Fakat burada göze güzel görünmeyen birşey var. Bu kodu tüm modellere tek tek kopyalayıp yapıştıracağız. Bunun önüne geçmek için yapabileceğimiz 2 şey var. Birincisi, extend edilen sınıfı değiştirmek. İkincisi ise Trait kullanmak.
1. Yöntem: Extend edilen sınıfı değiştirmek
Öncelikle yeni bir model sınıfı üretelim.
<?php class MyModel extends \Illuminate\Database\Eloquent\Model { public $incrementing = false; protected static function boot() { parent::boot(); static::creating(function ($model) { $model->{$model->getKeyName()} = \Ramsey\Uuid\Uuid::uuid1()->toString(); }); } }
Daha sonra kendi sınıfımızın ayarlarıyla oynayalım:
<?php class Bilmemne extends MyModel { }
Bilmemne modelimiz artık integer yerine uuid üretiyor!
2. Yöntem: Trait kullanmak
Bildiğiniz gibi bir sınıf birden fazla sınıftan türetilemiyor(extend edilemiyor). Siz birden fazla sınıfın özelliğini kullanmak isterseniz ya hepsini birbirinden extend edeceksiniz -ki etmeyin- ya da trait kullanarak istediğiniz methodları kullanacaksanız. Laravel’e baktığımızda trait kullanımının yoğun olduğunu görürüz. Traitler bize kodların yeniden kullanımı konusunda yardımcı olur. Tekrar yazımların önüne geçer. Kodumuzu parçalara bölebilmemizi sağlar ve kodun kolay takibini sağlar. Örnek olarak Laravel’in model sınıfının neler kullandığına bakalım:
<?php abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable { use ConcernsHasAttributes, ConcernsHasEvents, ConcernsHasGlobalScopes, ConcernsHasRelationships, ConcernsHasTimestamps, ConcernsHidesAttributes, ConcernsGuardsAttributes; }
Model sınıfı yorumlar da dahil 1460 satır. Traitlerin içindeki methodları da bu sınıfa dahil etseydik kim bilir kaç satır olacaktı. Kodun birbiriyle ilgili bölümlerini alıp trait içerisine koyup, traiti de model içerisinde sadece use ile çağırmak oldukça güzel bir fikir. Daha övülecek çok yanı vardır da, hem şimdi aklıma gelmiyor(hepsini bilmiyor da olabilirim) hem de artık kendi trait kodumuza geçelim.
<?php trait UuidsTrait { protected static function boot() { parent::boot(); static::creating(function ($model) { $model->{$model->getKeyName()} = \Ramsey\Uuid\Uuid::uuid1()->toString(); }); } }
Tek yapmamız gereken, bunu modelimiz içerisinde kullanmak:
<?php class Bilmemne extends \Illuminate\Eloquent\Database\Model { use UuidsTrait; }
Bu da tam olarak istediğimiz gibi sonuç verecek ve integer yerine uuid kullanmamızı sağlayacak.
Trait olarak kullanmak ve extend etmek arasında belli farklar var fakat onları başka bir yazı konusu yapabiliriz. Şimdilik ben size bu iş için trait kullanmanızı öneririm.
Esenlikle kalın.
Uzun süre Gitlab’deki projelerimde unit testleri otomatik çalıştırmayı beceremedim. Gitlab’de bununla ilgili bir örnek bulamadım. Çeşitli forumlarda örnek konfigürasyonlar paylaşılmış ama google’da ilk sırada çıkan konfigürasyonda testin tamamlanması 10 dakikayı buluyordu. (https://laracasts.com/discuss/channels/testing/laravel-ci-testing-with-gitlab)
En sonunda daha derli toplu güzel bir repo buldum. https://laracasts.com/discuss/channels/testing/laravel-ci-testing-with-gitlab/replies/307623
Laravel’e özel bir docker image kullanıyor. Ve bende testin tamamlanması 2 dakika filan sürüyor.
Gitlab ve Laravel için CI(Pipeline) konfigürasyonu arayanlara sevgilerimle. Umarım yardımcı olur.
Benim konfigürasyonum:
before_script: - mv /root/composer.phar . - php -v - git --version - ls -lah - php composer.phar self-update - php composer.phar install --no-interaction --prefer-dist --optimize-autoloader - cp .env.gitlab .env - php artisan key:generate - php artisan config:cache - php artisan migrate --force - php artisan db:seed variables: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: secret phpunit:php-laravel-env:mysql5.7: image: woohuiren/php-laravel-env:latest services: - mysql:5.7 script: - php vendor/bin/phpunit --coverage-text --colors=never