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

# 親記事
[Laravel 5.7で基本的なCRUDを作る - Qiita](http://qiita.com/sutara79/items/ef30fcdfb7afcb2188ea)


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

- トップページでは「記事」も「ユーザー」も普通に表示させます。  
  ![004.png](https://qiita-image-store.s3.amazonaws.com/0/27816/5926338a-a4a3-2772-637f-83a85e172174.png)<br><br>

- `posts/`以下では、「記事」を強調表示させます。  
  ![005.png](https://qiita-image-store.s3.amazonaws.com/0/27816/3d2e286a-2ec9-ef33-8f0a-1fda45ca1e15.png)


# 問題点
## Request::is()
リクエストを判別する方法で真っ先に思いつくのは`is`メソッドです。
:link: readouble.com: [リクエストURIの取得](https://readouble.com/laravel/5.7/ja/requests.html#request-path-and-method)

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

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

1. `http://foo.com/posts`
1. `http://foo.com/posts/23`
1. `http://foo.com/posts/6/edit`

アスタリスク`*`が使えるので、もしや正規表現で指定できるのではと調べてみました。
しかし、内部で[preg_quote()](https://secure.php.net/manual/ja/function.preg-quote.php)が使われており、アスタリスク以外の正規表現の特殊文字は無効化されてしまうと判明しました。
:link: ソースコード: [Request::is() - 内部でStr::is()が使われている](https://github.com/laravel/framework/blob/5.7/src/Illuminate/Http/Request.php#L185)
:link: ソースコード: [Str::is() - preg_quote()で特殊文字を無効化](https://github.com/laravel/framework/blob/5.6/src/Illuminate/Support/Str.php#L154)

## Route::currentRouteName()
`currentRouteName`メソッドを使う方法もあります。
:link: readouble.com: [現在のルートへのアクセス](https://readouble.com/laravel/5.7/ja/routing.html#accessing-the-current-route)

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

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


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

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


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

## ヘルパー関数を自作する方法
自作の方法は下記の通り様々です。
:link: [Laravel 5へ自作のヘルパー関数を追加するベストプラクティス | ある蜜柑の上にアルミ缶](https://s8a.jp/laravel-custom-helper)

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

まず、空のファイル`app/helpers.php`を作ります。
次に、`composer.json`に追記します。
:link: Composer公式ドキュメント: [autoload](https://getcomposer.org/doc/04-schema.md#autoload)
:link: Composer公式ドキュメント: [files](https://getcomposer.org/doc/04-schema.md#files)

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

そして、下記を実行します。
これで、`app/helpers.php`内で定義した関数をどこからでも呼び出せるようになります。
:link: Composer公式ドキュメント: [dump-autoload](https://getcomposer.org/doc/03-cli.md#dump-autoload-dumpautoload-)

```text:PowerShell
> composer dump-autoload
```

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

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

:link: 関連記事: [公式ドキュメントでは言及されていないヘルパー関数](https://qiita.com/sutara79/items/bdafa8c69f9f3774fef6)

## ヘルパー関数を記述する
では、本題の「現在のコントローラ名を取得する関数」を作ります。
正確には、現在のコントローラ名を返すのではなく、引数で渡した複数のコントローラ名が現在のコントローラ名と一致するかどうかを返すようにします。
そうすれば、「現在のコントローラ名が`login`または`password`かどうか」のような複数の条件でも、ビューでの記述量が少なくて済みます。
なお、自作だと分かるように関数名の先頭に`my_`を付けています。
PHP5.6以降で有効な[可変長引数](http://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list)を使っています。

```php: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要素を表示させたりする処理を追加しています。

```diff: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
```