Rabu, 11 August 2021

Memahami Laravel Route Model Binding: Langkah Demi Langkah

Dulunya, mungkin ini sangat repot, kita harus definisikan key apa yang ingin kita jadikan wildcardnya seperti berikut.

Laravel

Perkenalan

Route model binding dalam Laravel adalah fitur yang memudahkan Anda dalam mengambil record database berdasarkan parameter yang ada di URL. Laravel akan secara otomatis mencocokkan parameter URL dengan instance model yang sesuai.

Sebelumnya

Sebelum adanya model binding, Anda mungkin akan menuliskan kode seperti ini:

routes/web.php
Route::get('users/{id}', function ($id) {
    return User::findOrFail($id);
});

Atau mungkin jika dengan field lainnya:

Route::get('users/{username}', function ($username) {
    return User::where('username', $username)->firstOrFail();
});

Menggunakan Route Model Binding

Ketika kita menggunakan route model binding ini, kita tidak perlu lagi membuat semacam query di dalam controller nya. Yang perlu kita lakukan adalah menuliskan versi singular dari model yang ingin kita gunakan di dalam route. Contohnya seperti ini:

Route::get('users/{user}', function (App\Models\User $user) {
    return $user->email;
});

By default jika kita tidak memberikan key pada wildcard, maka Laravel akan mencocokkan dengan primary key dari model yang kita gunakan. Jadi dalam case ini Laravel akan mencocokkan dengan field id dari model User.

Customizing Key Name

Namun, jika kita ingin mencocokkan dengan field lainnya, kita bisa menambahkan key pada wildcard nya. Contohnya seperti ini:

Route::get('users/{user:username}', function (App\Models\User $user) {
    return $user->email;
});

Dengan begitu, kita akan mengunjugi URL seperti ini:

http://localhost:8000/users/john

Pastinya kita akan mendapatkan email dari user dengan username john.

2 Identifier

Terkadang ada kalanya kita ingin menampilkan object bukan hanya dari 1 key, bisa jadi 2 bahkan 3. Misal, di dalam model User kita memiliki field username dan id. Kita ingin mencocokkan kedua field tersebut. Caranya adalah dengan menambahkan key nya seperti ini:

Route::get('users/{user:id,username}', function (App\Models\User $user) {
    return $user->email;
});

Itu harapannya ya guys. Tapi sebenarnya itu belum bisa hahaha. Yang bisa kita lakukan saat ini adalah seperti:

Route::get('users/{identifier}', function ($identifier) {
    return User::where('username', $identifier)->orWhere('id', $identifier)->firstOrFail();
});

Bind Route

Namun jika seperti itu, kita akan di tuntut untuk menuliskan query nya di dalam controller. Jadi, bagaimana cara kita untuk mencocokkan 2 identifier tersebut? Kita bisa menggunakan Route::bind pada RouteServiceProvider seperti ini:

app/Providers/RouteServiceProvider.php
use App\Models\User;

Route::bind('user', function ($value) {
    return User::where('username', $value)->orWhere('id', $value)->firstOrFail();
});

Dengan begitu, kita bisa menggunakan simpel route seperti ini:

Route::get('users/{user}', function (App\Models\User $user) {
    return $user->email;
});

Lebih dari 1 wildcard

Explicit Binding

Ketika Anda sudah paham tentang route model binding, Anda mungkin akan bertanya-tanya, bagaimana jika kita ingin menggunakan 2 model dalam 1 route? Misal kita ingin menampilkan artikel dari kategori tertentu. Kita bisa menggunakan route model binding seperti ini:

Route::get('articles/{category:slug}/{article:slug}', [ArticleController::class, 'show']);

Dalam case ini kita akan menggunakan controller. Jika kita membuat wildcard nya 2, pastinya di dalam method nya akan menerima 2 model.

app/Http/Controllers/ArticleController.php
use App\Models\Article;
use App\Models\Category;

public function show(Category $category, Article $article)
{
    return $article;
}

Dan ini akan otomatis Not found (404) jika memang kategori yang diberikan tidak seusai dengan yang berelasi dengan artikel.

Implicit Binding

Namun, jika kita coba untuk tidak menggunakan explicit binding, maka Not found tidak akan terjadi. Contoh nya seperti ini:

Route::get('articles/{category}/{article}', [ArticleController::class, 'show']);

Jika kita coba untuk mengunjungi URL seperti ini:

http://routing.test/articles/2/1

Yang saat ini, pada tabel artikel tidak memiliki relasi ke kategori dengan id 2. Namun, kita tidak akan mendapatkan Not found. Karena Laravel akan mencoba untuk mencocokkan dengan primary key dari model yang kita gunakan. Jadi, jika kita ingin menggunakan implicit binding, kita harus menambahkan key nya seperti contoh pada explicit binding.

Scoped Binding

Pertanyaan nya adalah, bagaimana jika kita tidak ingin menggunakan key nya? Karena memang kita ingin dari keduanya di identifikasi dari id nya. Kita bisa menggunakan scoped binding seperti ini:

Route::get('articles/{category}/{article}', [ArticleController::class, 'show'])->scopeBindings();

Maka dengan begitu, ketika kita mengunjungi url yang sama seperti di atas, kita akan mendapatkan Not found.

Shallow Binding

Ketika kita bekerja dengan route resource. Dalam case ini, kita akan beranggapan bahwa ada table playlist dan juga episode. Yang mana 2 tabel tersebut akan benar-benar berhubungan dalam case apapun.

Maka route yang kita lakukan adalah seperti:

routes/web.php
Route::resource('{playlist}/{episode}', EpisodeController::class);

Dalam case ini, kita benar-benar ingin bahwa yang ada di url adalah model yang berelasi. Dengan resource kita tidak bisa menggunakan yang namanya scopeBindings(), maka kita bisa menggunakan ->shallow()() seperti ini:

Route::resource('{playlist}/{episode}', EpisodeController::class)->shallow();

Dengan begitu, jika playlist atau episode tidak berelasi satu sama lain, maka kita akan mendapatkan Not found.

Trash

Ketika Anda menggunakan fitur soft deletes di laravel, semua route yang telah dihapus akan otomatis mendapatkan Not found. Namun, jika Anda ingin menampilkan route yang telah dihapus, Anda bisa menggunakan withTrashed() seperti ini:

Route::get('articles/{article}', [ArticleController::class, 'restore'])->withTrashed();

Route Caching

Ketika Anda menggunakan route caching, maka semua route yang Anda buat akan di cache. Sehingga, ketika Anda membuat route baru, maka route tersebut tidak akan terbaca. Untuk mengatasi hal tersebut, Anda bisa menggunakan php artisan route:clear untuk menghapus cache route Anda.

Semoga artikel ini bermanfaat untuk Anda. Jika ada pertanyaan, silahkan bertanya di discord atau langsung dm saya di twitter. Terima kasih.