Mari kita membuat sesuatu jadi lebih sederhana agar mata kita tidak langsung sakit ketika melihat pekerjaan kita yang di masalalu.
Dalam case ini saya berencana untuk menampilkan single user, yang mana ini pasti nya sangat simple. Jika kita ingin mudah. Bisa dengan menggunakan model binding seperti:
Route::get('{user}', [ProfileController::class, 'show']);
Namun, karena user bisa di identifikasi dari dua kolom yaitu hash
dan username
. Terpaksa kita harus membuat route model binding pada RouteServiceProvider
seperti:
public function boot(): void
{
Route::bind('user', function ($value) {
return \App\Models\User::where('username', $value)->orWhere('hash', $value)->firstOrFail();
});
}
Dengan begitu kita dapat dengan mudah memanggilnya pada controller.
class ProfileController extends Controller
{
public function show(User $user)
{
return inertia('profile/show', [
'user' => new UserSingleResource($user),
]);
}
}
Namun, ada beberapa relasi yang ingin saya tampilkan disini.
users
(limited)users comments
(limited)comments commentable
commentable
belongs to model
users articles
(limited)users details
(limited)subscription information
counting comments
counting articles
counting exps
Maksud dari limited di atas adalah dimana saya tidak akan menampilkan resource keseluruhan. Bisa saja artikel hanya 6 misalnya. Atau juga kolom nya tidak semua mesti di get, karena itu mempengaruhi kinerja dari mesin pastinya.
Oleh karena itu, maka ada beberapa yang harus saya refactor di sini. Pertama, untuk menghadirkan mereka semua di dalam json (kebetulan saya memakai inertia.js). Sehingga harus melakukan yang namanya eager loading.
Nah ketika kita menampilkan single user seperti ini, hal yang bisa kita lakukan adalah menggunakan load()
dan juga loadCount
. Tapi tetap aja messi, karena sebenarnya saya juga ingin menampilkan users dengan tidak mengambil semua kolom nya. Melainkan menambil yang penting saja dengan menggunakan select
pastinya.
Untuk itu, maka saya berfikir akan langsung menampilkan user nya dengan tidak menggunakan sistem model binding.
Yang pertama, saya akan merobah route yang tadi binding user
menjadi identifier
, sebenarnya tidak mesti sih. Cuma biar jelas aja, bahwa di sini saya tidak menggunakan model, sehingga tidak perlu memanggil nama model nya.
Route::get('{identifier}', [ProfileController::class, 'show']);
Dengan menggantinya jadi identifier
, sehingga saya yakin bahwa saya sedang tidak menggunakan model binding. Setelah itu, karena kita tadinya ingin menampilkan user dari kolom username
ataupun hash
, sehingga kita perlu membuat custom binding pada RouteServiceProvider
. Yang mana rencana itu harus kita batalkan. Jadi saya akan menghapusnya, tinggal lah seperti ini.
public function boot(): void
{
//
}
Setelah itu, maka kita bisa melanjutkan untuk memodifikasi controller nya. Ingat, bahwa kita tidak lagi menggunakan model binding, sehingga kita akan memanggil {identifier}
sebagai identifikasi dari user yang akan muncul.
class ProfileController
{
public function show($identifier)
{
$user = User::query()
->select('id', 'name', 'email', 'about', 'username', 'hash', 'public', 'created_at', 'deleted_at')
->where('public', true)
->where('username', $identifier)
->orWhere('hash', $identifier)
->firstOr(callback: fn () => abort(404));
return inertia('profile/show', [
'user' => new UserSingleResource($user),
]);
}
}
Kode di atas tentu berjalan dengan mulus tanpa error. Namun, jika kita perhatikan lebih lanjut. Semua relasi nampaknya tidak pernah di load dalam query ini. Maka oleh karena itu, kita akan menambahkannya.
class ProfileController
{
public function show($identifier)
{
$user = User::query()
->select('id', 'name', 'email', 'about', 'username', 'hash', 'public', 'created_at', 'deleted_at')
->where('public', true)
->where('username', $identifier)
->orWhere('hash', $identifier)
->with([
'profileInformation:id,user_id,website,github,twitter,facebook,instagram,threads',
'articles' => fn ($query) => $query
->select('id', 'user_id', 'title', 'picture', 'slug', 'created_at')
->where('status', ArticleStatus::PUBLISHED)
->limit(6),
'subscribe:user_id,started_at,ended_at',
'comments' => fn ($query) => $query
->without('author', 'children')
->with([
'commentable' => fn ($commentable) => $commentable
->select('id', 'title', 'series_id', 'episode')
->with('series:id,name,slug'),
]),
])
->withCount(['comments', 'articles', 'likes'])
->firstOr(callback: fn () => abort(404));
return inertia('profile/show', [
'user' => new UserSingleResource($user),
]);
}
}
Hmmmmmm. Wait a minute... Ini sangat messy kelihatan nya, mungkin sekarang tidak masalah. Namun nanti ketika kita sudah masuk ke tahap pengembangan, mata kita akan terasa sakit melihat hal ini tiba-tiba ada di controller. Karena bayangkan, ada beberapa method di dalam controller ini yang 1 method saja sudah membuat kepala berakar.
Dengan case ini, maka model scope sangat berperan penting. Pada model User
, sekarang kita bisa membuat satu fungsi dengan nama scopeWithUserDetails
seperti:
class User
{
public function scopeWithUserDetails($query, $identifier)
{
return $query->select('id', 'name', 'email', 'about', 'username', 'hash', 'public', 'created_at', 'deleted_at')
->where('public', true)
->where('username', $identifier)
->orWhere('hash', $identifier)
->with([
'profileInformation:id,user_id,website,github,twitter,facebook,instagram,threads',
'articles' => fn ($query) => $query
->select('id', 'user_id', 'title', 'picture', 'slug', 'created_at')
->where('status', ArticleStatus::PUBLISHED)
->limit(6),
'subscribe:user_id,started_at,ended_at',
'comments' => fn ($query) => $query
->without('author', 'children')
->with([
'commentable' => fn ($commentable) => $commentable
->select('id', 'title', 'series_id', 'episode')
->with('series:id,name,slug'),
]),
])
->withCount(['comments', 'articles', 'likes']);
}
}
Dengan begitu, sekarang kita bisa merubah semua yang ada di controller menjadi lebih sederhana seperti:
class ProfileController
{
public function show($identifier)
{
$user = User::query()->withUserDetails($identifier)->firstOr(callback: fn () => abort(404));
return inertia('profile/show', [
'user' => new UserSingleResource($user),
]);
}
}
Dengan begitu, maka seandainya kita kembali 6-12 bulan lagi untuk melihat controller ini, mata tidak langsung capek.
Semoga artikel ini bermanfaat. Share jika menurut kalian ini penting, dan sukai untuk sekalian bookmark.
Let's start living like no one can help us in any event, so that when we are helped in certain times, it becomes a plus in itself.