Laravel5.7: ビューで、現在のコントローラ名を取得する


親記事

Laravel 5.7で基本的なCRUDを作る - Qiita

要望

ビューの中で、現在のコントローラ名がpostsかどうかを確認したい場合があります。
例えば、グローバルナビで現在の項目を強調したい場合などです。

  • トップページでは「記事」も「ユーザー」も普通に表示させます。

    004.png

  • posts/以下では、「記事」を強調表示させます。

    005.png

問題点

Request::is()

リクエストを判別する方法で真っ先に思いつくのはisメソッドです。
readouble.com: リクエストURIの取得

ビュー
@if (Request::is('posts/*'))
    // postsの場合の処理
@endif

しかし、上のコードでは下記の2番と3番しかTRUEになりません。
1番も含めるためにスラッシュを省いてRequest::is('posts*')としてもいいのですが、postsendersのような似通った名前のコントローラがある場合は正しく判別できません。

  1. http://foo.com/posts
  2. http://foo.com/posts/23
  3. http://foo.com/posts/6/edit

アスタリスク*が使えるので、もしや正規表現で指定できるのではと調べてみました。
しかし、内部でpreg_quote()が使われており、アスタリスク以外の正規表現の特殊文字は無効化されてしまうと判明しました。
ソースコード: Request::is() - 内部でStr::is()が使われている
ソースコード: Str::is() - preg_quote()で特殊文字を無効化

Route::currentRouteName()

currentRouteNameメソッドを使う方法もあります。
readouble.com: 現在のルートへのアクセス

ビュー
$controllerName = Route::currentRouteName();
// 得られる結果は posts.index, posts.show など。
// トップページの場合はNULLとなる。

posts.indexのようにアクション名も付いてくるのでそれを除去しなければならず、ちょっと面倒です。
しかし、調べた限りではコントローラ名だけを一発で返してくれるメソッドは見当たりませんでした。
仕方ないので、このメソッドの結果に少し手を加えることにします。

解決策

currentRouteNameメソッドの結果を.を区切りとしてexplodeし、その配列の0番目を取り出します。
これでpostsだけを正しく取得できるようになります。

ビュー
$controllerName = explode('.', Route::currentRouteName())[0];

ヘルパー関数にする

どのビューでも使えるように、ヘルパー関数を作ります。

ヘルパー関数を自作する方法

自作の方法は下記の通り様々です。
Laravel 5へ自作のヘルパー関数を追加するベストプラクティス | ある蜜柑の上にアルミ缶

今回は、新たに作るヘルパー関数は3~4個と少ないので、上の記事の1番目の「Composerでオートロードする方法」を使います。

まず、空のファイルapp/helpers.phpを作ります。
次に、composer.jsonに追記します。
Composer公式ドキュメント: autoload
Composer公式ドキュメント: files

composer.json
     "autoload": {
+        "files": [
+            "app/helpers.php"
+        ],
         "classmap": [
             "database/seeds",
             "database/factories"
         ],

そして、下記を実行します。
これで、app/helpers.php内で定義した関数をどこからでも呼び出せるようになります。
:link: Composer公式ドキュメント: dump-autoload

PowerShell
> composer dump-autoload

ちなみに、Laravel本体のヘルパー関数もこれと同じ方法で登録されています。
Laravel本体では2つのファイルに分けて関数を定義しているようです。

vendor/laravel/framework/composer.json
{
    "autoload": {
        "files": [
            "src/Illuminate/Foundation/helpers.php",
            "src/Illuminate/Support/helpers.php"
        ],

関連記事: 公式ドキュメントでは言及されていないヘルパー関数

ヘルパー関数を記述する

では、本題の「現在のコントローラ名を取得する関数」を作ります。
正確には、現在のコントローラ名を返すのではなく、引数で渡した複数のコントローラ名が現在のコントローラ名と一致するかどうかを返すようにします。
そうすれば、「現在のコントローラ名がloginまたはpasswordかどうか」のような複数の条件でも、ビューでの記述量が少なくて済みます。
なお、自作だと分かるように関数名の先頭にmy_を付けています。
PHP5.6以降で有効な可変長引数を使っています。

app/helpers.php
<?php
if (! function_exists('my_is_current_controller')) {
    /**
     * 現在のコントローラ名が、複数の名前のどれかに一致するかどうかを判別する
     *
     * @param array $names コントローラ名 (可変長引数)
     * @return bool
     */
    function my_is_current_controller(...$names)
    {
        $current = explode('.', Route::currentRouteName())[0];
        return in_array($current, $names, true);
    }
}

自作のヘルパー関数を使用する

レイアウトを下記のように修正します。
現在のコントローラ名と一致する場合は、BootstrapのCSSクラス名activeを追加したり、HTML要素を表示させたりする処理を追加しています。

resources/views/layouts/my.blade.php
                 <div class="collapse navbar-collapse" id="navbarSupportedContent">
                     {{-- Navbarの左側 --}}
                     <ul class="navbar-nav mr-auto">
                         {{-- 「記事」と「ユーザー」へのリンク --}}
-                        <li class="nav-item">
-                            <a class="nav-link" href="{{ url('posts') }}">{{ __('Posts') }}</a>
+                        <li class="nav-item @if (my_is_current_controller('posts')) active @endif">
+                            <a class="nav-link" href="{{ url('posts') }}">
+                                {{ __('Posts') }}
+                                @if (my_is_current_controller('posts'))
+                                    <span class="sr-only">(current)</span>
+                                @endif
+                            </a>
                         </li>
-                        <li class="nav-item">
-                            <a class="nav-link" href="{{ url('users') }}">{{ __('Users') }}</a>
+                        <li class="nav-item @if (my_is_current_controller('users')) active @endif">
+                            <a class="nav-link" href="{{ url('users') }}">
+                                {{ __('Users') }}
+                                @if (my_is_current_controller('users'))
+                                    <span class="sr-only">(current)</span>
+                                @endif
+                            </a>
                         </li>
                     </ul>

(中略)

                         {{-- 認証関連のリンク --}}
                         @guest
                             {{-- 「ログイン」と「ユーザー登録」へのリンク --}}
-                            <li class="nav-item">
-                                <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
+                            <li class="nav-item @if (my_is_current_controller('login', 'password')) active @endif">
+                                <a class="nav-link" href="{{ route('login') }}">
+                                    {{ __('Login') }}
+                                    @if (my_is_current_controller('login', 'password'))
+                                        <span class="sr-only">(current)</span>
+                                    @endif
+                                </a>
                             </li>
-                            <li class="nav-item">
-                                <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
+                            <li class="nav-item @if (my_is_current_controller('register')) active @endif">
+                                <a class="nav-link" href="{{ route('register') }}">
+                                    {{ __('Register') }}
+                                    @if (my_is_current_controller('register'))
+                                        <span class="sr-only">(current)</span>
+                                    @endif
+                                </a>
                             </li>
                         @else