Laravel AdminLteの一部を変えたい

# Laravel AdminLteの一部を変えたい
## 2022/09/02 追記
@ShibuyaKosuke様のコメントよりコマンドで編集可能なことが判明しました。
```
php artisan adminlte:install --with=basic_views
php artisan adminlte:install --with=auth_views
php artisan adminlte:install --with=main_views
```
> これらのコマンドで、編集可能な blade ファイルが、resources/views/vendor/adminlte/ 以下に出力されます。
> 出力されたファイルは、vendor/ 以下のファイルよりも優先されるため、resources 以下のファイルを編集するだけで、デザイン変更できます。

ですので以下は暇だったら読んでください。

## 記事の内容
この記事では提供されている上部のナビバーにパンくずを追加する方法を共有します。
基本的な機能はつぶさないようにしてカスタマイズできるよういることを目的としています。
開発環境はWindows10、laravel9、php8

Laravelはあまり理解できていないので間違いや他の方法がありましたら、
ご指摘お願いします。

## 結論
独自にadmin::pageの中身をコピーしたview(common/adminlte/page.blade.phpとします)を作成し、必要部分だけ改変して`@extends`して読み込む。(呼び出し側はsample.blade.phpとします。)
この時にcommon/adminlte/page.blade.phpの`@section('content_top_nav_left')`にやりたいものを記載します。
これだけだとエラーが発生するので、`AdminLteServiceProvider.php`を作成して、common/adminlte/page.blade.phpの描画の時に必要情報も取得できるようにしておくことで解決。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657595/5add08e1-9be7-a29f-83af-7fd7054649ac.png)

## AdminLteとは
AdminLteは管理画面の作成で有名なテンプレート。
自分で開発する手間がいろいろ省けるので作業効率アップにつながります。
公開されているサンプルページは[こちら](https://adminlte.io/themes/v3/)
githubは[こちら](https://github.com/jeroennoten/Laravel-AdminLTE)
インストールが完了するとvendor/jeroennoten/にいろいろ格納されています。
基本的なことはgithubのwikiやvendor/jeroennoten以下を見ることでも理解することができると思います。

## コードを見てみる
### 基本構成
```
@extends('adminlte::page')

@section('title', 'Dashboard')

@section('content_header')
    <h1>Dashboard</h1>
@stop

@section('content')
    <p>Welcome to this beautiful admin panel.</p>
@stop

@section('css')
    <link rel="stylesheet" href="/css/admin_custom.css">
@stop

@section('js')
    <script> console.log('Hi!'); </script>
@stop
```
githubより引用(https://github.com/jeroennoten/Laravel-AdminLTE/wiki/Usage)

これを`sample.blade.php`と呼ぶことにします。

軽く解説すると、`@section('content_header')`部分がサンプルページの「Dashboard]と書かれている部分にあたり、
`@section('content')`が「Dashboad」以下のいろいろものがある部分です。
今回パンくずを追加したい部分は以下の部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657595/e5e87c8e-4e63-b958-2d32-d8917eda6f25.png)


### 上部のナビバーの実態はどこにある？
上部のナビバーは`@extends('adminlte::page')`で呼び出されていますが、実態は`vendor/jeroennotem/laravel-adminlte/resources/views/page.blade.php`です。

以下で上部のナビバーが呼び出されています。
```
 {{-- Top Navbar --}}
 @if($layoutHelper->isLayoutTopnavEnabled())
   @include('adminlte::partials.navbar.navbar-layout-topnav')
 @else
   @include('adminlte::partials.navbar.navbar') // <-- これが上部のナビバー
 @endif
```

次は`adminlte::partials.navbar.navbar`を見てみましょう。
(今回は`if($layoutHelper->isLayoutTopnavEnabled()`の部分がfalseになる想定です。ここはadminlte.phpで制御できます。)

vendor/jeroennoten/laravel_adminlte/resources/views/partials/navbar/navbar.blade.php
```
{{-- Navbar left links --}}
<ul class="navbar-nav">
  {{-- Left sidebar toggler link --}}
  @include('adminlte::partials.navbar.menu-item-left-sidebar-toggler')

  {{-- Configured left links --}}
  @each('adminlte::partials.navbar.menu-item', $adminlte->menu('navbar-left'), 'item')

  {{-- Custom left links --}}
  @yield('content_top_nav_left') // <-- これを動かせば解決
</ul>
```
上記のようになっているので、呼び出しのほうから`@section('content_top_nav_left')`を使用して、ここに差し込むことで解決できそうです。

## ハマりポイント
「じゃあ呼び出しで`@section('cocntent_top_nav_left')`書けば解決じゃん！」と思うかもしれませんが、現状ナビバーを制御している部分はvendor下にあるので、いじりたくありません。
また、sample.blade.phpに`@section('cocntent_top_nav_left')`を書くこともできますが、ほかのページも増えたときにめんどうくさいので、やりたくありません。
そこで、views/common/adminlte/page.blade.phpでも作成しましょう。
しかし、`sample.blade.php`で`@extends('common.adminlte.page')`を呼び出すと、エラーが発生してしまいます。
`Undefined variable $adminlte`
$adminlteが未定義らしいので、`adminlte::page`について調査してみます。
vendor/jeroennoten/laravel_adminlte/src/AdminLteServiceProvider.phpを見てみると下記の部分が見つかります。
```
/**
 * Register the package's view composers.
 *
 * @return void
 */
private function registerViewComposers(Factory $view)
{
    $view->composer('adminlte::page', AdminLteComposer::class);
}
```
vendor/jeroennoten/laravel-adminlte/src/Http/ViewComposers/AdminLteComposer.phpも見てみます。
```
public function compose(View $view)
{
  $view->with('adminlte', $this->adminlte);
}
```
上記の2つでadminlte::pageを呼び出すときに$adminlteもセットしているようですのでこれをコピーして改変しましょう。

## 実装
#### AdminLteServiceProvider.php
(vendor/jeroennoten/laravel-adminlte/src/AdminLteServiceProvider.php)から必要部分だけ抜き取ります。)
```
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\View\Factory;
use JeroenNoten\LaravelAdminLte\Http\ViewComposers\AdminLteComposer;

class AdminlteServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot(Factory $view)
    {
        $this->registerViewComposers($view);
    }

    /**
     * Register the package's view composers.
     *
     * @return void
     */
    private function registerViewComposers(Factory $view)
    {
        // ここの部分をadminlte::pageから差し換えたviewに変更
        $view->composer('common.adminlte.page', AdminLteComposer::class);
    }

}
```
#### config/app.php
```
 'providers' => [
   // これを追加
   App\Providers\AdminLteServiceProvider::class,
  ],
```

#### views/common/adminlte/page.blade.php
```
@extends('adminlte::master')

@inject('layoutHelper', 'JeroenNoten\LaravelAdminLte\Helpers\LayoutHelper')

// ここにパンくず({{ Breadcrumbs::render('adminlte') }})でもなんでもいい)
// 最終的には動的に動くようにする
@section('content_top_nav_left')
    <ol class="breadcrumb float-sm-right">
        <li class="breadcrumb-item"><a href="#">Home</a></li>
        <li class="breadcrumb-item active">Widgets</li>
    </ol>
@stop

@section('adminlte_css')
    @stack('css')
    @yield('css')
@stop

@section('classes_body', $layoutHelper->makeBodyClasses())

@section('body_data', $layoutHelper->makeBodyData())

@section('body')
    <div class="wrapper">

        {{-- Top Navbar --}}
        @if($layoutHelper->isLayoutTopnavEnabled())
            @include('adminlte::partials.navbar.navbar-layout-topnav')
        @else
            @include('adminlte::partials.navbar.navbar')
        @endif

        {{-- Left Main Sidebar --}}
        @if(!$layoutHelper->isLayoutTopnavEnabled())
            @include('adminlte::partials.sidebar.left-sidebar')
        @endif

        {{-- Content Wrapper --}}
        @empty($iFrameEnabled)
            @include('adminlte::partials.cwrapper.cwrapper-default')
        @else
            @include('adminlte::partials.cwrapper.cwrapper-iframe')
        @endempty

        {{-- Footer --}}
        @hasSection('footer')
            @include('adminlte::partials.footer.footer')
        @endif

        {{-- Right Control Sidebar --}}
        @if(config('adminlte.right_sidebar'))
            @include('adminlte::partials.sidebar.right-sidebar')
        @endif

    </div>
@stop

@section('adminlte_js')
    @stack('js')
    @yield('js')
@stop
```

#### views/sample.blade.php
```
@extends('common.adminlte.page')  // <--@extends('adminlte::page')から変更

@section('title', 'Dashboard')

@section('content_header')
    <h1>Dashboard</h1>
@stop

@section('content')
    <p>Welcome to this beautiful admin panel.</p>
@stop

@section('css')
    <link rel="stylesheet" href="/css/admin_custom.css">
@stop

@section('js')
    <script> console.log('Hi!'); </script>
@stop
```
#### 画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2657595/8ac17805-77f8-a5a2-f269-38608079d0b9.png)

## まとめ
独自のpage.bladeが表示できるようになったので、あとは部品も独自に作成して好きは部分で読み込めばいいのでやりたい放題できるようになりました。
adminlte.phpで制御できる部分が多いですが、パンくずのような動的な要素は独自で対応する必要があると思いました。

### 参考資料
[github](https://github.com/jeroennoten/Laravel-AdminLTE)
[AdminLteサンプルページ](https://adminlte.io/themes/v3/index3.html)
[Laravel view composerでロジックを一箇所にまとめよう！](https://qiita.com/bumptakayuki/items/212ec57ffbfb8e71cb60)