【Laravel】保守性をアップさせるかもしれない 3 つのテクニック

# はじめに

初めまして、MasaKu です。

今年も残すところわずかですね。

今年もたくさんのコードを書いてきたことだと思います。

中には埃をかぶりまくった個人開発のコードもあるかと思いますが、年末のこの時期にちょっとだけリファクタリングとして手を入れていみませんか？

今回は、Laravel で作成されたアプリケーションを、ちょこっとした工夫だけで可読性をアップできるかもしれないテクニックをご紹介します。

# その１：ルートグループ

Laravel では `web.php` にルーティングする処理を記載します。

超ざっくりしたブログページのサンプルを作成しました。

```php
// ホーム画面
Route::get('/home', 'HomeController@home');

// ブログページ
Route::get('/blog', 'BlogController@blog');
Route::get('/create', 'BlogController@create');
Route::get('/update', 'BlogController@update');
Route::get('/delete', 'BlogController@delete');
```

この時、ブログページにアクセスする際は、twitter の OAuth でログイン認証している状態にさせたいという場合は各ルーティングにミドルウェアを設定をするのが便利かと思います。

```php
// ブログページ
Route::get('/blog', 'BlogController@blog')->middleware('oAuth');
Route::get('/create', 'BlogController@create')->middleware('oAuth');
Route::get('/update', 'BlogController@update')->middleware('oAuth');
Route::get('/delete', 'BlogController@delete')->middleware('oAuth');
```

もちろん、このように１件ずつ Middleware を設定することもできるのですが、これだと Middleware を差し替えようとした際に、全ての Middleware の定義を修正しなければならなくなります。

それに、こういったコードがたくさん並ぶとパッと見たときに、どの Middleware を設定しているのかがわかりづらいし、Middleware を付け忘れてしまうことにもなりかねません。

こういう時は、ルーティングをグループ化して、そのグループに Middleware を設定するとコードがスッキリしてわかりやすくなります。

```php
// ブログページ
Route::middleware(['oAuth'])->group(function(){
    Route::get('/blog', 'BlogController@blog');
    Route::get('/create', 'BlogController@create');
    Route::get('/update', 'BlogController@update');
    Route::get('/delete', 'BlogController@delete');
});
```

このようにすれば、パッと見ただけで Middleware をかける処理が一覧として理解しやすく付け忘れるということもなくなると思います。

ちなみに、グループ化しておくことのメリットとして、グループ内のコントローラ対して名前空間を指定することもできるということもあります。

今後コントローラの名前空間を変更した場合なども一括で設定することができるので非常に便利です。

```php
// ブログページ
Route::namespace('Blog')->middleware(['oAuth'])->group(function(){
    Route::get('/blog', 'BlogController@blog');
    Route::get('/create', 'BlogController@create');
    Route::get('/update', 'BlogController@update');
    Route::get('/delete', 'BlogController@delete');
});
```

# その２：サービスコンテナからサービスクラスを利用する

Laravel ではコントローラ内に処理を記載して、処理結果を View に渡すようにしていきます。

そのため、コントローラ内が複雑になってしまうことをできるだけ避けたいのではないでしょうか。

特に、似たような処理がほかのコントローラ内にあるような状態はできるだけ避けたいです。

そんなときは、共通処理をサービスクラスに書き出してサービスコンテナに登録し、コントローラ内でそのクラスのインスタンスを利用するようにしましょう。

Laravel プロジェクトの `app` フォルダ内に `OriginClasses` というフォルダを作成します。

その後、`OriginService.php` というファイルを作成し、以下のように入力してください。

```php
<?php


namespace App\OriginClasses;


class OriginService
{
    private $msg;
    private $data;

    public function __construct()
    {
        $this->msg = 'サービスクラスを取得';
        $this->data = ['ホーム', 'ブログ'];
    }

    public function getMessage()
    {
        return $this->msg;
    }

    public function getData()
    {
        return $this->data;
    }
}
```

メッセージとデータを取得する共通処理です。

この処理を各コントローラ内で呼び出して利用できるようにしていきます。

Laravel では `app()`というメソッドを通してサービスコンテナからインスタンスを取得することができます。

以下のようにコントローラ内に `app('App\OriginClasses\OriginService')` を記載することで、インスタンスを取得することができます。

```php
public function blog()
{
    // OriginServiceクラスのインスタンスを取得
    $originService = app('App\OriginClasses\OriginService');

    echo $originService->getMessage();
    return view('blog');
}
```

このようにしておくことで、コントローラ内に共通の処理がいたるところにベタ書きされることを防ぐことができます。

今後、同じ処理を別のコントローラに埋め込みたくなったときや、これまで定義していた共通処理を修正する場合も、クラスファイルのメソッドを修正するだけですべてのコントローラ内の処理を修正することができるので非常に便利です。

# その３：ファサードを使う

先程はサービスコンテナからインスタンスを取得する方法をご紹介しましたが、サービスを利用する入口だけを使ってサービスを利用する方法もがります。

ファサードではコントローラ側でインスタンスを取得するのではなく、サービスの入口だけを利用して処理することができます。

まずは、Laravel プロジェクト内の`app`フォルダに`Facades`というフォルダを作成します。

その後、`Facades`フォルダ内に`OriginService.php`というファイルを作成して以下のように入力してください。

```php
<?php

namespace App\Facades;
use Illumination\Support\Facades\Facade;

class OriginService extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'originservice';
    }
}
```

それでは、作成したファサードを`/config/app.php`の`aliases`配列に追加していきます。

```php

    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Arr' => Illuminate\Support\Arr::class,

        // 一部省略

        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,
        'originservice' => App\Facades\MyService::class, //この行を追加

    ],

```

最後に`aliases`に登録したファサードを Laravel のサービスプロバイダに設定して利用できるようにしましょう。

サービスプロバイダは以下のコマンドで作成できます。

```sh
php artisan make:provide OriginServiceProvider
```

作成した`OriginServiceProvider.php`を以下の通り修正してください。

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class OriginServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        app()->singleton('originservice',
            'App\OriginClasses\OriginSevice');
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
```

作成したサービスプロバイダを `/config/app.php` に設定します。

```php
    'providers' => [
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,

        // 一部省略

        App\Providers\RouteServiceProvider::class,
        App\Providers\OriginServiceProvider::class, //この行を追加

    ]
```

これでファサードを通してサービスを利用できるようになりました。

あとは以下のようにすることでコントローラ側からサービスを利用することができます。

```php
use App\Facades\OriginService;

class BlogController extends Controller
{
    public function blog()
    {
        OriginService::getMessage();
        return view('blog');
    }
```

ファサードの処理の流れを整理しますと以下のとおりになります

1. ファサードを作成し `getFacadeAccessor()` メソッドに利用するサービスのファサード（入口）の名前を返すようにする
2. `config/app.php` にファサードを登録する（エイリアスとファサードの実態を登録）
3. サービスプロバイダを作成し、利用するサービス（コントローラ側で呼び出したい処理）をファサードの名前で設定する
4. コントローラ側でファサードを経由してサービスの処理を実行する

# おわりに

いかがでしたでしょうか。

いずれも基本的な内容にはなりますが、もし取り入れていなくてもちょっとした工夫だけで直ぐに取り入れることができる内容なので、リファクタリングとしてちょうど良い内容なのではないかと思いました。

ぜひお試しあれ！

# 参考資料

[PHP フレームワーク Laravel 実践開発](https://www.shuwasystem.co.jp/smp/book/9784798059075.html)

[Laravel 5.7 ミドルウェア](https://readouble.com/laravel/5.7/ja/middleware.html)