AdminLTE3で動的メニュー

ぐぐると検索の下の方に出てくる話題ではありますが、忘れがちなので備忘録としてここにも書いておきたいと思います。

Laravel のプラグインである AdminLTE3 において、左ペインのメニューを構成するには通常、config/adminlte.php の menu キーを使用します。しかし、例えばユーザに admin レベルを設定したいという場合はこの方法が使えません。そこで動的にメニューを追加する必要が出てきます。

AdminLTE3 のマニュアルを読むと、以下のような方法を取ればよいことが分かります。ここでは一例として、メニューを DB テーブルに保存する方法を紹介します。

DBに入れるメニューですが、以下のような構造とします。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('menus', function (Blueprint $table) {
            $table->id();
            $table->tinyInteger('flg_admin')->default(0)->comment('ADMIN なら1');
            $table->string('title', 1024)->comment('タイトル');
            $table->string('icon', 1024)->nullable(true)->comment('icon html');
            $table->string('link', 1024)->nullable(true)->comment('リンク先 URL');
            $table->unsignedBigInteger('parent_id')->nullable(true)->comment('親 メニュー ID');
            $table->integer('sort_number')->default(0)->index()->comment('表示順序');
            $table->datetime('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
            $table->datetime('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('menus');
    }
};

要点となるのは、flg_adminでメニューを切り替える点、parent_idでサブメニューが作成できるようにしている点、sort_number で表示順序を定義している点です。また、users テーブルに flg_admin カラムを追加し、どのユーザが admin であるか示すように修正します。

さて、本題ですが、app/Providers/EventServiceProvider.php の boot 関数に以下を追加します。

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use JeroenNoten\LaravelAdminLte\Events\BuildingMenu;
use App\Models\Menu;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event to listener mappings for the application.
     *
     * @var array<class-string, array<int, class-string>>
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        Event::listen(function (BuildingMenu $event) {
            $pmenus = Menu::whereNull('parent_id');
            if (!auth()->user()->flg_admin) {
                $pmenus = $pmenus->where('flg_admin', 0);
            }
            $pmenus = $pmenus->orderBy('sort_number')->get();
            foreach ($pmenus as $pmenu) {
                $menu = [ 'text' => $pmenu->title ];
                if ($pmenu->icon) {
                    $menu['icon'] = $pmenu->icon;
                }
                if ($pmenu->link) {
                    $menu['url'] = url($pmenu->link);
                }
                $submenus = [];
                $cmenus = Menu::where('parent_id', $pmenu->id);
                if (!auth()->user()->flg_admin) {
                    $cmenus = $cmenus->where('flg_admin', 0);
                }
                $cmenus = $cmenus->orderBy('sort_number')->get();
                foreach ($cmenus as $cmenu) {
                    $submenu = [ 'text' => $cmenu->title ];
                    if ($cmenu->icon) {
                        $submenu['icon'] = $cmenu->icon;
                    }
                if ($pmenu->link) {
                    $menu['url'] = url($pmenu->link);
                }
                $submenus = [];
                $cmenus = Menu::where('parent_id', $pmenu->id);
                if (!auth()->user()->flg_admin) {
                    $cmenus = $cmenus->where('flg_admin', 0);
                }
                $cmenus = $cmenus->orderBy('sort_number')->get();
                foreach ($cmenus as $cmenu) {
                    $submenu = [ 'text' => $cmenu->title ];
                    if ($cmenu->icon) {
                        $submenu['icon'] = $cmenu->icon;
                    }
                    if ($cmenu->link) {
                        $submenu['url'] = url($cmenu->link);
                    }
                    $submenus[] = $submenu;
                }
                if (count($submenus) > 0) {
                    $menu['submenu'] = $submenus;
                }
                $event->menu->add($menu);
            }
        });
    }

    /**
     * Determine if events and listeners should be automatically discovered.
     *
     * @return bool
     */
    public function shouldDiscoverEvents()
    {
        return false;
    }
}

ここでの要点は “use JeroenNoten\LaravelAdminLte\Events\BuildingMenu;” を追加することと、listen() 関数呼び出しを追加することです。ぐぐるとlisten の第1引数に BuildingMenu::class を設定している場合もありますが、Laravel では Data Injection 機能が使えるので、必ずしも必要ではありません。

メニューを構成したら、最後に $event->menu->add() でメニューの最後に追加します。

投稿者について
みのしす

小さいときは科学者になろうとしたのに、その時にたまたま身に着けたプログラミングで未だに飯を食っているしがないおじさんです。(年齢的にはもうすぐおじいさん)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です