DEV Community

Cover image for Laravel’de Backed Enum Nedir? Pure Enum ile Farkı Ne?
Barış Bideratan
Barış Bideratan

Posted on

Laravel’de Backed Enum Nedir? Pure Enum ile Farkı Ne?

Laravel’de enum kullanmaya başlayınca ilk karşılaşılan kavramlardan biri şu oluyor:

Backed enum ne demek?

İlk bakışta biraz “fazla akademik” gibi duran bir terim. Ama aslında olay oldukça basit:

Backed enum, her enum case’inin arkasında gerçek bir string veya int değer taşıdığı enum türüdür.

Yani enum sadece PHP tarafında sembolik bir isimden ibaret değildir; aynı zamanda veritabanına yazılabilecek, request’ten gelebilecek veya API response’unda kullanılabilecek gerçek bir değere sahiptir.

Bu yazıda Laravel üzerinden backed enum mantığını, pure enum’dan farkını ve günlük projelerde nasıl kullanılacağını anlatacağım.


Enum nedir?

Enum, belirli bir alanın alabileceği değerleri kontrollü hale getiren bir yapıdır.

Mesela bir kullanıcı rolü düşünelim:

admin
editor
user
Enter fullscreen mode Exit fullscreen mode

Normalde bunları string olarak kullanabiliriz:

if ($user->role === 'admin') {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Ama bu kullanımın birkaç problemi var:

  • Yazım hatalarına açık.
  • Her yerde string tekrar ediyor.
  • IDE desteği zayıf.
  • Refactor yapmak zorlaşıyor.
  • Sistemde hangi roller var, tek bir yerden anlaşılmıyor.

Enum ile bu değerleri tek bir yerde toplarız:

namespace App\Enums;

enum UserRole: string
{
    case Admin = 'admin';
    case Editor = 'editor';
    case User = 'user';
}
Enter fullscreen mode Exit fullscreen mode

Artık rol kontrolünü şöyle yapabiliriz:

if ($user->role === UserRole::Admin) {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Bu hem daha okunabilir hem de daha güvenli.


Pure Enum ve Backed Enum farkı

PHP’de temel olarak iki enum türü var:

  1. Pure enum
  2. Backed enum

Laravel’de de aslında bu PHP enum’larını kullanıyoruz. Laravel burada enum’ları model cast, validation ve request işlemlerinde daha pratik hale getiriyor.


Pure Enum nedir?

Pure enum, case’lerin arkasında herhangi bir değer taşımadığı enum türüdür.

enum UserRole
{
    case Admin;
    case Editor;
    case User;
}
Enter fullscreen mode Exit fullscreen mode

Burada Admin, Editor ve User sadece enum case’leridir.

Şunu yapabiliriz:

$role = UserRole::Admin;
Enter fullscreen mode Exit fullscreen mode

Ama şunu yapamayız:

UserRole::Admin->value;
Enter fullscreen mode Exit fullscreen mode

Çünkü pure enum’da value yoktur.

Sadece name vardır:

UserRole::Admin->name; // "Admin"
Enter fullscreen mode Exit fullscreen mode

Pure enum daha çok uygulama içinde sembolik durumları belirtmek için kullanılabilir. Fakat veritabanına değer yazmak istediğimiz senaryolarda genellikle yeterli olmaz.


Backed Enum nedir?

Backed enum’da her case’in arkasında gerçek bir değer bulunur.

Bu değer yalnızca iki tür olabilir:

string
int
Enter fullscreen mode Exit fullscreen mode

Örneğin:

namespace App\Enums;

enum UserRole: string
{
    case Admin = 'admin';
    case Editor = 'editor';
    case User = 'user';
}
Enter fullscreen mode Exit fullscreen mode

Burada enum case’i şudur:

UserRole::Admin
Enter fullscreen mode Exit fullscreen mode

Bu case’in arkasındaki gerçek değer ise şudur:

UserRole::Admin->value; // "admin"
Enter fullscreen mode Exit fullscreen mode

Yani:

case Admin = 'admin';
Enter fullscreen mode Exit fullscreen mode

satırında iki farklı şey var:

UserRole::Admin->name;  // "Admin"
UserRole::Admin->value; // "admin"
Enter fullscreen mode Exit fullscreen mode

name, PHP tarafındaki case adıdır.
value, dış dünyaya veya veritabanına yazılacak gerçek değerdir.


Backed enum neden Laravel’de daha çok kullanılır?

Çünkü Laravel projelerinde enum kullandığımız alanların çoğu veritabanıyla ilişkilidir.

Örneğin:

  • role
  • status
  • type
  • payment_status
  • order_status
  • notification_channel
  • provider

Bu alanların veritabanında bir karşılığı vardır.

Mesela users tablosunda şöyle bir değer tutmak isteriz:

role = admin
Enter fullscreen mode Exit fullscreen mode

Ama PHP tarafında bu değeri düz string olarak kontrol etmek yerine enum olarak kullanmak isteriz:

$user->role === UserRole::Admin
Enter fullscreen mode Exit fullscreen mode

İşte backed enum burada çok işe yarar.


Laravel’de enum dosyası oluşturmak

Laravel’de enum oluşturmak için Artisan komutu kullanılabilir:

php artisan make:enum Enums/UserRole
Enter fullscreen mode Exit fullscreen mode

Bu komutla enum dosyasını app/Enums/UserRole.php altında tutabiliriz.

Örnek enum:

namespace App\Enums;

enum UserRole: string
{
    case Admin = 'admin';
    case Editor = 'editor';
    case User = 'user';
}
Enter fullscreen mode Exit fullscreen mode

Ben genelde enum’ları app/Enums klasörü altında tutmayı daha düzenli buluyorum. Çünkü proje büyüdükçe enum sayısı artıyor ve bunların app dizininin altında dağınık durması pek hoş olmuyor.


Laravel model cast ile kullanım

Enum’un en güzel taraflarından biri, Laravel model cast ile çok temiz çalışmasıdır.

Örneğin users tablosunda role alanımız olsun.

Migration tarafında bunu basit bir string kolon olarak tutabiliriz:

$table->string('role')->default(UserRole::User->value);
Enter fullscreen mode Exit fullscreen mode

Modelde ise cast tanımlarız:

use App\Enums\UserRole;

protected function casts(): array
{
    return [
        'role' => UserRole::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode

Bundan sonra Laravel, veritabanındaki string değeri otomatik olarak enum’a çevirir.

Yani veritabanında şu varsa:

role = admin
Enter fullscreen mode Exit fullscreen mode

Modelde şunu elde ederiz:

$user->role; // UserRole::Admin
Enter fullscreen mode Exit fullscreen mode

Artık kontrolü şöyle yapabiliriz:

if ($user->role === UserRole::Admin) {
    // Admin işlemleri
}
Enter fullscreen mode Exit fullscreen mode

Kaydetmek de oldukça temizdir:

$user->role = UserRole::Editor;
$user->save();
Enter fullscreen mode Exit fullscreen mode

Laravel bunu veritabanına otomatik olarak şöyle yazar:

editor
Enter fullscreen mode Exit fullscreen mode

Burada bizim manuel olarak ->value vermemize gerek kalmaz. Laravel enum cast sayesinde bunu bizim yerimize halleder.


Form request ve validation tarafı

Backed enum’ları request doğrulamada da kullanabiliriz.

use App\Enums\UserRole;
use Illuminate\Validation\Rule;

$request->validate([
    'role' => ['required', Rule::enum(UserRole::class)],
]);
Enter fullscreen mode Exit fullscreen mode

Bu sayede role alanı sadece enum içinde tanımlı değerlerden biri olabilir.

Yani şunlar geçerlidir:

admin
editor
user
Enter fullscreen mode Exit fullscreen mode

Ama şu geçerli olmaz:

super_admin
Enter fullscreen mode Exit fullscreen mode

Bu küçük gibi görünen şey aslında büyük projelerde ciddi fayda sağlar. Çünkü veri daha sisteme girerken kontrol altına alınır.


from() ve tryFrom() farkı

Backed enum’ların güzel özelliklerinden biri de string veya int değerden enum case’i oluşturabilmemizdir.

$role = UserRole::from('admin');
Enter fullscreen mode Exit fullscreen mode

Bu bize şunu döndürür:

UserRole::Admin
Enter fullscreen mode Exit fullscreen mode

Ama dikkat etmek gerekir. Eğer geçersiz bir değer verirsek:

UserRole::from('wrong-role');
Enter fullscreen mode Exit fullscreen mode

PHP hata fırlatır.

Daha güvenli kullanım için tryFrom() tercih edilebilir:

$role = UserRole::tryFrom('admin');
Enter fullscreen mode Exit fullscreen mode

Geçerli değer varsa enum case’i döner:

UserRole::Admin
Enter fullscreen mode Exit fullscreen mode

Geçersiz değer varsa null döner:

$role = UserRole::tryFrom('wrong-role'); // null
Enter fullscreen mode Exit fullscreen mode

Ben genelde dışarıdan gelen, güvenmediğim verilerde tryFrom() kullanmayı daha doğru buluyorum.


Enum içine yardımcı metotlar eklemek

Enum’lar sadece sabit değer listesi olmak zorunda değil. İçlerine metot da ekleyebiliriz.

Mesela kullanıcı rollerine ekranda gösterilecek label değerleri ekleyelim:

namespace App\Enums;

enum UserRole: string
{
    case Admin = 'admin';
    case Editor = 'editor';
    case User = 'user';

    public function label(): string
    {
        return match ($this) {
            self::Admin => 'Yönetici',
            self::Editor => 'Editör',
            self::User => 'Kullanıcı',
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Kullanım:

$user->role->label();
Enter fullscreen mode Exit fullscreen mode

Bu şekilde enum sadece teknik değer tutmaz, aynı zamanda o değere ait davranışı da taşıyabilir.

Bu özellikle admin panel, select input, filtreleme ve raporlama gibi alanlarda çok işe yarar.


Select input için options üretmek

Enum’dan select option üretmek de güzel bir kullanım örneğidir.

public static function options(): array
{
    return collect(self::cases())
        ->mapWithKeys(fn (self $role) => [
            $role->value => $role->label(),
        ])
        ->toArray();
}
Enter fullscreen mode Exit fullscreen mode

Artık şunu yapabiliriz:

UserRole::options();
Enter fullscreen mode Exit fullscreen mode

Çıktı:

[
    'admin' => 'Yönetici',
    'editor' => 'Editör',
    'user' => 'Kullanıcı',
]
Enter fullscreen mode Exit fullscreen mode

Bu yaklaşım sayesinde hem backend hem frontend tarafında aynı değerleri daha kontrollü kullanabiliriz.


Veritabanında SQL enum mu, string kolon mu?

Burada önemli bir tercih var.

MySQL tarafında gerçekten enum kolon tipi kullanabiliriz. Ama ben çoğu Laravel projesinde string kolon kullanmayı daha esnek buluyorum.

Örneğin:

$table->string('status');
Enter fullscreen mode Exit fullscreen mode

Neden?

Çünkü enum değerleri zamanla değişebilir. Yeni bir status eklemek isteyebiliriz. SQL enum kullanırsak veritabanı şemasını değiştirmek gerekir. String kolon kullanıp uygulama tarafında PHP enum ile kontrol sağlamak genelde daha rahat bir yaklaşımdır.

Yani kontrolü database seviyesinde değil, uygulama seviyesinde enum ile sağlamış oluruz.

Bu her zaman tek doğru demek değil. Ama Laravel projelerinde çoğu senaryo için pratik ve sürdürülebilir bir tercih olduğunu düşünüyorum.


Ne zaman backed enum kullanmalıyız?

Şu tarz alanlarda backed enum oldukça mantıklıdır:

role
status
type
category
payment_status
order_status
provider
channel
visibility
priority
Enter fullscreen mode Exit fullscreen mode

Mesela:

enum OrderStatus: string
{
    case Pending = 'pending';
    case Paid = 'paid';
    case Shipped = 'shipped';
    case Cancelled = 'cancelled';
}
Enter fullscreen mode Exit fullscreen mode

Ya da:

enum PaymentStatus: string
{
    case Waiting = 'waiting';
    case Successful = 'successful';
    case Failed = 'failed';
}
Enter fullscreen mode Exit fullscreen mode

Bu tarz alanlarda backed enum kullanmak, magic string kullanımını ciddi ölçüde azaltır.


Ne zaman pure enum kullanılır?

Pure enum’u ise daha çok veritabanına yazılmayacak, sadece uygulama içinde anlam taşıyan durumlarda düşünebiliriz.

Örneğin:

enum ModalState
{
    case Open;
    case Closed;
}
Enter fullscreen mode Exit fullscreen mode

Ama açık konuşmak gerekirse Laravel projelerinde benim elim çoğu zaman backed enum’a gidiyor. Çünkü genelde bu değerler bir yerden geliyor, bir yere yazılıyor veya API ile dış dünyaya açılıyor.


Kötü kullanım örneği

Şöyle bir kod düşünelim:

if ($user->role === 'admin') {
    //
}

if ($user->role === 'editor') {
    //
}

if ($user->role === 'user') {
    //
}
Enter fullscreen mode Exit fullscreen mode

Bu kod çalışır. Ama uzun vadede çok güven vermiyor.

Daha sonra biri yanlışlıkla şöyle yazabilir:

if ($user->role === 'admn') {
    //
}
Enter fullscreen mode Exit fullscreen mode

PHP buna kızmaz. Çünkü sonuçta bu da bir string.

Ama enum kullandığımızda:

if ($user->role === UserRole::Admin) {
    //
}
Enter fullscreen mode Exit fullscreen mode

IDE bizi destekler. Refactor kolaylaşır. Yanlış değer kullanma ihtimali azalır. Kodun niyeti daha açık hale gelir.

Bence enum’un asıl değeri de burada:
Sadece daha “modern” göründüğü için değil, kodun anlamını netleştirdiği için kullanılır.


Kısa özet

Backed enum, arkasında gerçek bir string veya int değer taşıyan enum türüdür.

Pure enum:

enum UserRole
{
    case Admin;
}
Enter fullscreen mode Exit fullscreen mode

Backed enum:

enum UserRole: string
{
    case Admin = 'admin';
}
Enter fullscreen mode Exit fullscreen mode

Laravel projelerinde backed enum özellikle veritabanı, request, validation ve API response gibi alanlarda çok kullanışlıdır.

Benim genel yaklaşımım şu:

  • Veritabanına yazılacaksa backed enum
  • Request’ten gelecekse backed enum
  • API’de dönecekse backed enum
  • Sadece uygulama içi sembolik bir durumsa pure enum düşünülebilir

Enum kullanmak küçük projede bile kodu daha okunabilir yapar. Büyük projede ise magic string karmaşasını azaltır, refactor sürecini kolaylaştırır ve domain’i daha anlaşılır hale getirir.

Kısacası, Laravel’de role, status, type gibi alanlarda hâlâ düz string’lerle ilerliyorsak, backed enum’a geçmek çoğu zaman daha temiz bir adımdır.

Top comments (0)