# Laravel API kaynak (resource) sınıflarıyla yanıt biçimlemek

> Laravel API Resource sınıflarıyla veritabanı modelini istemciye doğrudan sızdırmadan temiz yanıtlar nasıl üretilir, örneklerle anlatıyorum.

- Yayın: 2018-02-04
- Kategori: Web Geliştirme
- Etiketler: Laravel, API
- Okuma süresi: 3 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/laravel-api-kaynak-resource-siniflariyla-yanit-bicimlemek/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
[API geliştirirken](/blog/laravel-ile-json-api-uclari-yazmak) en sık düştüğüm tuzaklardan biri, [Eloquent modelini](/blog/eloquent-orm-ile-veritabani-islemlerini-sadestirmek) doğrudan yanıt olarak döndürmekti. `return $user;` yazdım, işe yaradı, geçtim. Sonra o modele yeni bir alan eklendi, ilişki eklendi, hassas bir kolon geldi — ve istemci tarafı beklenmedik alanlarla karşılaştı. Laravel 5.5 ile gelen **API Resource** sınıfları bu soruna temiz bir çözüm sunuyor.

## Sorun: modeli doğrudan döndürmek

Bir `User` modeli düşünün. `email`, `name`, `password`, `remember_token`, `created_at`, `updated_at` gibi alanlar var. Controller'da şunu yazarsanız:

```php
<?php

public function show(User $user)
{
    return response()->json($user);
}
```

`password` ve `remember_token` da yanıtta gidecek. `$hidden` dizisini doldurarak bunu engelleyebilirsiniz ama bu, yanıt biçimini modelin sorumluluğuna yıkmak demek. Model büyüdükçe ne göstereceğinizi, ne gizleyeceğinizi takip etmek güçleşiyor.

Bir de şöyle bir senaryo var: modele `is_admin` alanı ekliyorsunuz, o alan da istemciye gidiyor. İstemci bunu kullanıcıya göstermeye başlıyor. Sonra o alanı yeniden adlandırıyorsunuz — ve istemci tarafı bozuluyor. Modeli doğrudan sızdırdığınızda veritabanı şemanız ile API sözleşmeniz arasındaki sınır ortadan kalkıyor; bu ikisini birbirinden yalıtmak gerekiyor.

## API Resource sınıfı nasıl oluşturulur?

Laravel'in Artisan komutuyla bir kaynak sınıfı iskeletini hızla üretebilirsiniz:

```bash
php artisan make:resource UserResource
```

Bu komut `app/Http/Resources/UserResource.php` dosyasını oluşturur. `toArray` metodunu istediğiniz gibi doldurursunuz:

```php
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id'         => $this->id,
            'name'       => $this->name,
            'email'      => $this->email,
            'created_at' => $this->created_at->toDateTimeString(),
        ];
    }
}
```

Controller'da kullanımı basit:

```php
<?php

public function show(User $user)
{
    return new UserResource($user);
}
```

Bu kadar. Artık istemci tam olarak ne görmesi gerektiğini görüyor, fazlasını değil.

## Koleksiyon yanıtları

Tek model değil, liste döndürmeniz gerektiğinde `ResourceCollection` devreye giriyor. En hızlı yol `UserResource::collection()` metodunu çağırmak:

```php
<?php

public function index()
{
    return UserResource::collection(User::all());
}
```

Sayfalama (pagination) kullananlar için de bu yöntem çalışıyor; [`paginate()`](/blog/rest-api-de-sayfalama-filtreleme-ve-siralama) döndürdüğünüzde Laravel otomatik olarak `meta` ve `links` bloklarını ekliyor.

## Koşullu alanlar

Bazen bir alanı yalnızca belirli durumlarda yanıta dahil etmek istiyorum. Örneğin token alanını yalnızca yeni oluşturulan kayıtta göstermek gibi. `when` metodu bu işi görüyor:

```php
<?php

public function toArray($request)
{
    return [
        'id'    => $this->id,
        'name'  => $this->name,
        'email' => $this->email,
        'token' => $this->when($this->wasRecentlyCreated, $this->api_token),
    ];
}
```

`wasRecentlyCreated` false döndürdüğünde `token` anahtarı yanıtta hiç yer almıyor; `null` değil, tamamen yok. Bu fark önemli.

## İlişkili veriler

Kullanıcının rollerini de yanıta dahil etmek istediğinizi düşünelim. Başka bir kaynak sınıfı tanımlayıp içinden çağırabilirsiniz:

```php
<?php

public function toArray($request)
{
    return [
        'id'    => $this->id,
        'name'  => $this->name,
        'roles' => RoleResource::collection($this->whenLoaded('roles')),
    ];
}
```

`whenLoaded` metodunun değeri şu: ilişki eager load edilmediyse o alanı yanıta eklemiyor. Yani Controller'da `User::with('roles')->find($id)` yazdığınızda roller görünüyor, yazmadığınızda görünmüyor. N+1 sorgu tuzağına düşülmeden, istek başına gereksiz veri çekilmeden çalışıyor.

## Yanıta ek metadata eklemek

Bazen yanıtın etrafına ilave bilgi sarmak gerekiyor: durum kodu, mesaj, sayfalama bilgisi dışında özel bir meta gibi. Bunun için kaynak sınıfında `with` metodunu override edebilirsiniz:

```php
<?php

public function with($request)
{
    return [
        'meta' => [
            'api_version' => '1.0',
        ],
    ];
}
```

## İstemci sayısı arttığında bu katmanın değeri netleşiyor

Resource sınıfını ilk kullandığımda projenin tek istemcisi bir web arayüzüydü. Sonradan mobil uygulama gerekti. Mobile farklı bir alan adlandırması lazımdı — `full_name` yerine `name`, `created_at` yerine `joined_at` gibi. Web istemcisini bozmadan mobil için ayrı bir `UserMobileResource` yazabildim; ikisi aynı `User` modelini farklı biçimlerle sunuyor. Modeli doğrudan döndürseydiniz, bu ayrımı yapmanın temiz bir yolu olmazdı.

Birden fazla istemci — ya da zamanla değişen tek bir istemci — olan her projede bu dönüşüm katmanı, veritabanı değişikliklerini API sözleşmesinden yalıtıyor. Yeni bir kolon eklemek, mevcut yanıtı bozmak anlamına gelmiyor.

## Sonuç

API Resource sınıfları başta "ekstra bir katman daha" gibi görünüyor. Ama ilk projede kullandıktan sonra modeli yanıta bağlamak için başka bir yola dönmek istemiyorum. Hangi alanın gideceğini tek bir dosyada görüp yönetmek, istemci tarafının beklenmedik değişikliklerden etkilenmemesini sağlıyor. Özellikle birden çok istemci varsa — mobil uygulama, web uygulaması, üçüncü parti tüketici — bu katmanın değeri daha da belirginleşiyor.
