Membuat Active Menu dengan View Composer di Laravel
by Yahya Muhammad - 31 Mar 2016
Dalam sebuah aplikasi web, biasanya yang bertipe web admin, umum kita jumpai sidebar berisi menu-menu.
Daftar menu tersebut biasanya memiliki state active
atau biasa alias tidak aktif, menunjukkan si user
sedang berada di halaman apa. Berbagai template web admin biasanya sudah mempunyai styling sendiri seperti
contoh di bawah ini:
Di atas adalah template favorit anak Badr, yaitu SB-Admin dan AdminLTE, sebagai contoh
styling active menu. Biasanya, style tersebut didapat dengan menambahkan CSS class .active
pada elemen
menu item atau butir navigasi. Secara back-end, kita harus memastikan ketika view tersebut mau dilukis alias render,
mana menu item yang harus diberi class .active
.
Di Laravel, biasanya kita mengirim data atau variabel yang dibutuhkan View dari fungsi yang ada di
Controller. Masalahnya, data menu yang aktif itu kita butuhkan untuk setiap halaman yang diminta.
Solusi naifnya, ya kita mesti ngirim paramater seperti active_menu => 'dashboard'
pada setiap fungsi yang ada.
Nah, ada beberapa cara lain yang lebih cerdas untuk ini, salah satunya dengan View Composer.
Konsep
View Composer ialah sebuah callback pada Laravel yang dipanggil ketika sebuah view akan dilukis (render)1. Nah, fitur ini dapat kita gunakan untuk berbagi data yang sama pada beberapa view. So, kita akan membuat composer untuk view yang memuat sidebar menu tersebut dan mengirim data menu mana yang sekarang lagi diakses.
Masalah berikutnya, darimana kita tahu menu yang lagi diakses? paling gampang sih dari pengecekan
route alias rute yang lagi diakses. Misal site.com/users
biasanya jadi prefix untuk semua halaman yang
berkaitan dengan user. Tapi ada cara lain yang lebih asik nih, mau tau gak? yuk lanjutin bacanya :D
Langkah-langkah
-
Buat class
ActiveMenuComposer.php
seperti contoh di bawah. Oya, berkas class ini bisa ditaruh di direktori mana aja, yang penting namespace-nya bener. Dalam contoh ini berkas ditaruh diapp/Http/Composer/
.<?php namespace App\Http\Composer; use View; use Illuminate\Http\Request; class ActiveMenuComposer { protected $request; /** * ActiveMenuComposer constructor. * @param Request $request */ public function __construct(Request $request) { $this->request = $request; } public function compose(View $view) { } }
-
Selanjutnya, kita kaitkan informasi menu yang aktif dengan rute aplikasi di
app/Http/routes.php
sebagai berikut:Route::group(['prefix' => 'users', 'active_menu' => 'user'], function() { //user related area Route::get('{id}', 'UserController@detail'); Route::get('{id}/edit', 'UserController@editForm'); Route::post('{id}/edit', 'UserController@editSave'); } );
Contoh pembuatan rute di atas memanfaatkan custom route parameter yang akan dikirim sebagai atribut dalam objek
Illuminate\Routing\Route
. Nah, objekRoute
tersebut bisa didapat dari instanceIlluminate\Http\Request
yang sudah kita inject alias masukkan di constructor view composer di langkah sebelumnya.2 -
Tambahkan parameter active menu di view sesuai dengan route parameter yang tadi didefinisikan.
public function compose(View $view) { $routeAction = $this->request->route()->getAction(); if (array_key_exists('active_menu', $routeAction)) { $view->with('active_menu', $routeAction['active_menu']); } else { $view->with('active_menu', 'default'); } }
Fungsi
getAction()
pada objekRoute
akan mengembalikan array berisi route parameter yang kita definisikan tadi3. Isinya lebih banyak dari yang kita tulis sih, tapi yang kita cek cuma yang kita bikin aja yaituactive_menu
. -
Jangan lupa daftarkan view composer kita untuk view yang terkait di sebuah Service Provider. Kita bisa buat provider khusus untuk mendaftarkan view composer, atau gunakan saja
app\Providers\AppServiceProvider.php
. Yang penting isi fungsiboot()
seperti berikut:public function boot() { view()->composer('layouts.admin.sidebar', 'App\Http\Composer\ActiveMenuComposer'); }
Kode di atas mendaftarkan view composer
ActiveMenuComposer
yang kita buat untuk viewlayouts.admin.sidebar
yang memuat sidebar menu. View-nya silakan sesuaikan aja. -
Ambil data
active_menu
yang sudah ditambahkan oleh view composer. Di viewlayouts.admin.sidebar
, kita dapat mengaksesnya seperti biasa, contohnya seperti ini:<li class="{{ $active_menu == 'user' ? 'active' : '' }}"> <a href="{{ url('admin/users') }}"> <i class="fa fa-users"></i> <span>Anggota</span> </a> </li>
Selesai!
Suplemen
-
Satu view composer bisa digunakan untuk banyak view. Parameter pertama ketika mendaftarkannya diubah jadi array:
view()->composer(['layouts.admin.sidebar', 'layouts.admin.header'], 'App\Http\Composer\ActiveMenuComposer');
-
Atau bisa juga pakai wildcard (*):
view()->composer(['layouts.master.sidebar', 'layouts.admin.*'], 'App\Http\Composer\ActiveMenuComposer');
-
Contoh di atas sudah dicoba di Laravel 5.2 dan berjalan baik. Menurut laman dokumentasinya4, hal yang sama dapat dilakukan di versi 5.1 juga. Untuk versi 5.0, cara mendaftarkan view composer-nya sedikit berbeda:
//mendaftar satu composer View::composer('layouts.admin.sidebar', 'App\Http\Composer\ActiveMenuComposer'); //mendaftar banyak composer View::composers([ 'App\Http\ViewComposers\AdminComposer' => ['admin.index', 'admin.profile'], 'App\Http\ViewComposers\UserComposer' => 'user', 'App\Http\ViewComposers\ProductComposer' => 'product' ]);
Penutup
Seperti class Controller di dalam Laravel, kita juga bisa mengguakan Eloquent model dan service lain di dalam view composer. Bisa di-inject melalui constructor ataupun dengan facade. Hal ini berguna jika kita ingin berbagi sebuah data untuk banyak view sekaligus. Misal data daftar kota dan provinsi di database diperlukan di setiap halaman pencarian atau form pendaftaran. Daripada mesti ngambil terus kirim data yang sama di setiap fungsi yang membutuhkan, mending bikin sekali terus daftarin. Beres.
Yak segini dulu dah untuk kali ini. Seperti dibilang di awal, ada banyak cara lain untuk implementasi active menu, googling aja kalau penasaran. Ini juga dapetnya berawal dari gugling, hehe. Nah kalau udah punya metode lain yang mungkin lebih keren, jangan lupa bagi-bagi di sini! :D