Photo by Jude Beck on Unsplash

Laravel’ de Yardımcı Nesne Arayüzleri (Interface)

Mustafa Akçakaya
Published in
4 min readJan 8, 2020

--

Laravel, kontrol çevrimi prensibi (Inversion of Control Principle — IoC) üzerine kurulu yapısını sahip olduğu güçlü servis konteyneri (Service Container — SC) ile sağlıyor. SC aracılığı ile bağımlılık arama ve bağımlılık enjeksiyonunu (Dependency Injection — DI) mümkün kılıyor. Kendi bağımlılıklarında ise somut sınıfları enjekte etmek yerine genellikle arayüz enjeksiyonu kullanıyor (Interface Injection — II). O yüzden, çekirdeğinde yer alan sınıfları tanımlayan bir çok arayüze sahip. Bu arayüzlere ise Laravel çerçevesinde Contract diyoruz. Adı kontrat olunca akla ilk başta arayüzün en temel görevi olan sınıfların hangi yöntemleri uygulayacağını belirleme (nesne ile hangi yöntemlerin tanımlanacağına dair bir sözleşme imzalandığını düşünün) gelse de en önemli sebebi çalışma anında (run-time) bağımlılıklara müdahale etmek. IoC, SC, DI ve II bu şekilde birleşince bize çok güçlü ve esnek bir yapı sunuyor.

Bu yazıda, bahsettiğim arayüzlerden çekirdek dışında bizim de rahatça kullanabileceğimiz bir kaç yardımcı arayüzden bahsetmek istiyorum: Arrayable, Jsonable, Responsable, Renderable ve Htmlable. Bunlardan özellikle yardımcı diye bahsetmemin sebebi ise aslında \Illuminate\Contracts\Support isim alanında (namespace) yer almaları.

Arrayable

İçerisinde sadece toArray() yöntemini barındırın bu arayüz, gerçeklediği (implementation) objenin bir dizi (array) çıktısı olduğunu ifade ediyor. Örneğin:

<?php

namespace
App\Dummy;

use Illuminate\Contracts\Support\Arrayable;

class MyClass implements Arrayable
{
public $params = [
'a' => 1,
'b' => 2,
'c' => 3,
];

public function toArray(): array
{
return $this->params;
}
}

Bir nesnenin Arrayable arayüzünü gerçeklediğini bildiğimizde ise dizi çıktısına toArray() ile erişebileceğimizi garantilemiş oluyoruz. Örneğin üstteki örnekten yola çıkarsak şu şekilde bir kullanım düşünülebilir:

<?php

$myClass = new MyClass();
$attributes = [];

if ($myClass instanceof \Illuminate\Contracts\Support\Arrayable) {
// Burada artık $myClass nesnesinin kesinlikle bir toArray()
// yöntemine sahip olduğundan ve sonucunun da dizi olduğundan
// eminiz.
$attributes = $myClass->toArray();
}

Arrayable arayüzünü PHP’nin ön tanımlı ArrayAccess ve Countable arayüzleri ile de birleştirerek daha doygun dizi destekli bir obje yaratabiliriz. Aynı şekilde üstteki örneği ele alırsak:

<?php

namespace
App\Dummy;

use Illuminate\Contracts\Support\Arrayable;

class MyClass implements Arrayable, \ArrayAccess, \Countable
{
public $params = [
'a' => 1,
'b' => 2,
'c' => 3,
];

public function toArray(): array
{
return $this->params;
}

public function offsetExists($offset): bool
{
return isset($this->params[$offset]);
}

public function offsetGet($offset)
{
return $this->params[$offset] ?? null;
}

public function offsetSet($offset, $value): void
{
$this->params[$offset] = $value;
}

public function offsetUnset($offset): void
{
unset($this->params[$offset]);
}

public function count()
{
return count($this->toArray());
}
}

Arrayable arayüzünü Laravel içerisinde Model ve Request nesneleri içerisinde görebilirsiniz. Bir modelin ya da Request nesnesinin dizi yansımasını Arrayable::toArray() ile almaktayız.

Jsonable

Bu arayüz bize nesnenin bir JSON string çıktısı olduğunu belirtir. İçerisinde sadece toJson($options = 0) yöntemi yer almaktadır. $options şeklinde bir parametre almasının sebebi ise JSON çıktısının genellikle json_encode ile sağlanıyor olması:

public function toJson($options = 0)
{
return json_encode($this->toArray(), $options);
}

Responsable

Bu arayüz nesnenin bir HTTP yanıtının olduğunu belirtir. Sadece toResponse($request) yöntemini barındırır. Sahip olduğu parametre ise mevcut Request nesnesi varsa onu içeri alıp onun üzerinden dönüş yapmak içindir. Örneğin:

public function toResponse($request): JsonResponse
{
return response()->json($this->toArray());
}

Bu şekilde MyClass nesnesinin artık bir HTTP yanıtına sahip olduğunu biliyoruz. Bu sınıfı basit bir şekilde rota tanımlayıp direk döndürürseniz otomatik olarak Content-type: application/json başlığıyla bir JSON yanıtı olarak döndüğünü göreceksiniz:

<?php

use
App\Dummy\MyClass;

Route::get('/', function () {
return new MyClass();
});

// Çıktısı:
// Content-Type: application/json
/*
{
"a": 1,
"b": 2,
"c": 3
}
*/

Bunun sebebi rotalamadan sorumlu Illuminate\Routing\Router sınıfının dönüşü Responable arayüzünü gerçekleyip gerçeklemediğine dair kontrol etmesi ve gerçekliyor ise toResponse($request) yöntemini çağırmasıdır:

https://github.com/laravel/framework/blob/6.x/src/Illuminate/Routing/Router.php#L733

if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}

Renderable

Bu arayüz ise içerisinde sadece render() yöntemini barındırıyor. Eğer nesnenin bir karakter ifade (string) dönüşü varsa diğer arayüzlerde olduğu gibi gerçekleme kontrol edilerek render() yöntemi çağrılabilir:

public function render(): string
{
return implode(', ', $this->toArray());
}

Laravel içerisinde Illuminate\Http\Response ve Illuminate\View\View sınıfları Renderable arayüzü için gerçekleme kontrolü yapmakta. Yani nesneyi bir yanıt olarak döndürdüğünüzde otomatik olarak render() yöntemi çağrılarak dönecektir.

Htmlable

Bu arayüz nesnenin bir HTML çıktısı olduğunu ifade eder. Bu arayüzü gerçekleyen bir nesne, HTML ifade şeklinde bir çıktı verdiği varsayılır. Sadece toHtml() yöntemini barındıran bu arayüzü Laravel, örneğin helper yöntemlerinden e() yönteminde ve Illuminate\Mail\Mailer sınıfı içerisinde kullanıyor. Örneğin Mailer sınıfında kullanımına bakarsak:

/**
* Render the given view.
*
*
@param string $view
*
@param array $data
*
@return string
*/
protected function renderView($view, $data)
{
return $view instanceof Htmlable
? $view->toHtml()
: $this->views->make($view, $data)->render();
}

Eğer Mailer sınıfına bir şablon adı yerine Htmlable bir nesne verirsek otomatik olarak bizim belirttiğimiz html çıktı kullanılacak.

Değerlendirme

Bu arayüzler ilk bakışta çok büyük işler yapıyor gibi gözükmeselerde aslında uygulamanın katmanları arasında bizi bir çok kontrolden kurtarıyor ve projemizi organize etmemizi kolaylaştırıyor. Kendi yapımız ile Laravel’in yapısının ahenkli bir şekilde iç içe girmesini ve iki yabancının anlaşmasını sağlıyor. Yani Laravel’in anlayacağı dilden konuşmuş oluyoruz.

Tekrar görüşmek üzere…

--

--