Laravel Tips: Kode Lebih Enak Dibaca Dengan Scope

Published on 16 July

Tampilkan data dengan default properti yang Anda inginkan, tidak hanya itu. Scope juga bisa membantu kita dalam membuat kode kita lebih readable.

Sebelum kita memulainya, pertama saya akan menunjukkan perbedaan dari sebelum dan sesudah.

// Before
Article::where('user_id', $request->user())->where('status', ArticleStatus::PUBLISHED)->get();

// After
Article::query()->wherePublishedAndBelongsToMe()->get();

Karena di sini saya mencontohkan dengan artikel, maka kita akan menganggap bahwa ada status pada artikel tersebut. Seperti misalnya unpublished dan published. Sehingga jika Anda melihat contoh di atas, itu hanya menggunakan enum dari PHP. Kurang lebih enum nya seperti ini.

enum ArticleStatus: int
{
    case UNPUBLISHED = 0;
    case PREVIEW = 1;
    case PUBLISHED = 2;
}

Nah pada sekarang, buka model artikel dan tambahkan 3 metode di dalamnya dengan prefix scopeNameOfFunction seperti:

use App\Enums\ArticleStatus;
use Illuminate\Contracts\Database\Query\Builder;
class Article extends Model
{
    //...
    public function scopeWherePublished(Builder $builder)
    {
        return $builder->where('status', ArticleStatus::PUBLISHED);
    }

    public function scopeWhereBelongsToMe(Builder $builder)
    {
        return $builder->whereBelongsTo(auth()?->user());
    }

    public function scopeWherePublishedAndBelongsToMe(Builder $builder)
    {
        return $builder->wherePublished()->whereBelongsToMe();
    }
}

Setelah itu, maka kalian sudah bisa menggukan seperti contoh di atas, hanya saja untuk metode scopeWhereBelongsToMe ada sedikit pengecualian. Pastikan jika Anda ingin memakainya itu tepat di halaman yang sudah di proteksi middleware auth. Itu hanya contoh, pastinya Anda bisa membuat apapun yang diinginkan.

Baik untuk cara penggunaan nya itu sangat mudah sekali, jika kita butuh yang hanya bersatus published, maka kita bisa melakukan nya seperti:

Article::query()->wherePublished()->get();

Dan jika kalian ingin menampilkan semua yang bersangkutan dengan user yang sedang login dan tidak peduli dengan statusnya bisa seperti:

Article::query()->whereBelongsToMe()->get();

Dan pastinya, jika kalian ingin kedua-dua nya itu bisa seperti:

Article::query()->whereBelongsToMe()->wherePublished()->get();

Atau disatukan dengan prefix and seperti:

Article::query()->wherePublishedAndBelongsToMe()->get();

Kurang lebih untuk penggunaan scope seperti itu. Namun scope yang kita lakukan di atas masih disebut dengan scope secara local. Mengapa seperti itu ? Karena sebenarnya ada cara untuk scope secara global. Jadi scope nya itu diterapkan by default.

Global Scope

Sekarang kita akan mencontohkannya langsung. Buka terminal Anda dan jalankan perintah artisan untuk membuat scope seperti:

php artisan make:scope PublishedScope

Setelah itu, maka dia akan membuatkan kita 1 folder Scopes yang di dalam nya ada file ArticleScope.php. Lihat file nya tepat di dalam direktori app/Models/Scopes/ArticleScope.php. Ingat, bahwa class ini hanya mengandung 1 metode yaitu apply, jadi jika Anda mencoba untuk membuat 1 lagi metode dibawahnya maka itu akan error. Karena class ini sendiri di implementasikan dengan interface Scope yang di dalamnya hanya ada 1 metode seperti:

public function apply(Builder $builder, Model $model);

Nah, sekarang Anda bisa masukkan query yang ingin dibuat tepat pada metode apply itu seperti:

class PublishedScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('status', ArticleStatus::PUBLISHED)->latest();
    }
}

Selanjutnya Anda bisa mendaftarkan scope itu secara global tepat pada model yang membutuhkan nya.

class Article extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new PublishedScope);
    }
}

Jika seandainya model user juga membutuhkan scope itu, maka Anda bisa juga meletekkan nya di sana seperti:

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new PublishedScope);
    }
}

Anonymous Global Scopes

Jika kalian tidak ingin membuatnya di luar dari dari model itu, bisa dengan menggunakan anonymous global scope seperti:

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope('published', function (Builder $builder) {
            $builder->where('status', Article::PUBLISHED);
        });
    }
}

Tanpa Global Scope

Terkadang kita tidak ingin menggunakan scope ini, misalnya kita ingin menampiklan semua artikel tanpa memperhatikan statusnya. Maka itu bisa dicegah dengan menambahkan metode withoutGlobalScope seperti:

Article::withoutGlobalScope(PublishedScope::class)->get();

Atau jika Anda menggunakan anonymous global scope bisa dengan langsung memasukkan namanya seperti:

Article::withoutGlobalScope('published')->get();

Semoga artikel ini bermanfaat. Until next time 👋🏻