Laravel Pintでクリーンなコードにしよう

# はじめに

少し前にLaravel Pintというツールが入っていることを知り、
ガッツリ使ってみたことはなかったため
夏休みのお勉強も兼ねて、一度使ってみました。

# 本編
## Laravel Pintとは...?

* `Laravel 9.3`以上ではデフォルトでインストールされている
* コード規約に則ったクリーンなコードになるように自動で修正を行なってくれるツール
* PHP-CS-Fixer がLaravel向けにカスタマイズされている
* `Laravel` `PSR12`などのいくつかコーディング規約があり、どれを使うかを設定できる

参考：[Laravel 10.x Laravel Pint](https://readouble.com/laravel/10.x/ja/pint.html)

> Laravel Pint（ピント：PHP+lint）は、ミニマリストのための主張を持ったPHPコードスタイルフィクサです。PintはPHP-CS-Fixer上に構築されており、あなたのコードスタイルがクリーンで一貫したものになるよう、シンプルにします。
Pintは、すべての新しいLaravelアプリケーションに自動的にインストールされますので、すぐに使い始めることができます。デフォルトで、Pintは設定を必要とせず、Laravelの主張を取り入れたコーディングスタイルに従い、コードスタイルの問題を修正します。

## 使ってみる
### （任意）Pintの設定ファイルを準備する

Laravelプロジェクトルートに`pint.json`を作成。


```pint.json
{
    "preset": "laravel",
    "rules": {
        "no_superfluous_phpdoc_tags": false
    }
}
```

隙あらばPHPDocを消そうとするので
その対策の設定のみ入れています。

### Pintを実行する
以下のコマンドを実行することで自動整形が行われます。

`./vendor/bin/pint`

以下のようなオプションがありました。

| コマンド | 備考 |
|:-|:-|
| ./vendor/bin/pint app/Http/Controllers | ディレクトリを指定して実行する  |
| ./vendor/bin/pint --test  | チェックのみ実施（自動整形は行わない） |
| ./vendor/bin/pint -v  | チェックしたファイルの詳細まで記載する  |
| ./vendor/bin/pint --dirty  | コミットされていないファイルのみをチェックする |

## チェック例

### 修正前
 ```app/Services/Internal/MenuService.php:
<?php

namespace App\Services\Internal;

use App\Services\BaseService;
use Illuminate\Database\Eloquent\Collection; // ⭐︎ アルファベット順になっていない
use App\Models\Menu;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;

/**
 * Class SavingService
 * @package App\Services\Internal
 */
final class MenuService extends BaseService
{
    /**
     * 検索処理
     * @return Collection // ⭐︎ 名称と戻り値の間が詰まっている
     */
    public function getMenu(): Collection
    {
        $query = Menu::query();

        // 検索条件
        $currentDate = Carbon::now();
        $query->where(function ($q) use ($currentDate){// ⭐︎ {の前にスペースがない
            // 公開開始日が未指定、または指定されていて現在日時が公開開始日以降
            $q->where(Menu::PUBLISHED_FROM, '<=', $currentDate)
                ->orWhereNull(Menu::PUBLISHED_FROM);
        })->where(function ($q) use ($currentDate){
            // 公開終了日が未指定、または指定されていて現在日時が公開終了日以前
                $q->where(Menu::PUBLISHED_TO, '>=', $currentDate) // ⭐︎ インデントが異なっている
                    ->orWhereNull(Menu::PUBLISHED_TO);
        });

        // 認証状態ならログインは取得しない
        if (Auth::check()){// ⭐︎ {の前にスペースがない
            $query->whereNot(Menu::NAME, 'ログイン');
        }else{// ⭐︎ {の前にスペースがない
            $query->whereNot(Menu::NAME, 'ログアウト');
        }

        // ソート
        $query->orderBy(Menu::SORT, 'asc');

        return
            $query->get();
    }
}
```

### 修正後

```app/Services/Internal/MenuService.php:
<?php

namespace App\Services\Internal;

use App\Models\Menu;
use App\Services\BaseService;
use Illuminate\Database\Eloquent\Collection; // ⭐︎ アルファベット順になった
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;

/**
 * Class SavingService
 */
final class MenuService extends BaseService
{
    /**
     * 検索処理
     * 
     * @return Collection  // ⭐︎ 名称と戻り値の間が1行空いた
     */
    public function getMenu(): Collection
    {
        $query = Menu::query();

        // 検索条件
        $currentDate = Carbon::now();
        $query->where(function ($q) use ($currentDate) {// ⭐︎ {の前にスペースが入った
            // 公開開始日が未指定、または指定されていて現在日時が公開開始日以降
            $q->where(Menu::PUBLISHED_FROM, '<=', $currentDate)
                ->orWhereNull(Menu::PUBLISHED_FROM);
        })->where(function ($q) use ($currentDate) {
            // 公開終了日が未指定、または指定されていて現在日時が公開終了日以前
            $q->where(Menu::PUBLISHED_TO, '>=', $currentDate)// ⭐︎ インデントが揃った
                ->orWhereNull(Menu::PUBLISHED_TO);
        });

        // 認証状態ならログインは取得しない
        if (Auth::check()) {// ⭐︎ {の前にスペースが入った
            $query->whereNot(Menu::NAME, 'ログイン');
        } else {// ⭐︎ {の前にスペースが入った
            $query->whereNot(Menu::NAME, 'ログアウト');
        }

        // ソート
        $query->orderBy(Menu::SORT, 'asc');

        return $query->get();
    }
}
```

変更された箇所を抜粋し、⭐️をつけています。
* `{`前後のスペースが統一
* インデントのずれを修正
* インポートしたクラスの並び順がアルファベット順に修正
* PHPDocの修正
などが行われました。

# 所感
## 使ってみて

実装ロジックを優先していると
この辺りの細かいチェックまで手が回らないことがあるので
ツールで自動反映してくれるのはありがたいです。

標準化してしまえば、
一定の可読性は担保できそうです。

## 実際の運用を考えてみる
### 運用フロー
**個人の場合**

1. 実装を進める
2. Laravel Pintを当てる
3. コミット & マージなど

**チームの場合**
1. 実装を進める
2. 実装後に一度Commitする（ロジックを保存しておく）
3. Laravel Pintを当てて再度コミットする
4. PRを発行する

### 考え方
個人の場合は、適宜修正を反映すれば良いかなと思います。

チームで実装を進める場合は
一度、業務ロジックを作成し、コミットしておくことでロジックを担保しつつ、
再度、Laravel Pintを使って修正を行うことで
安全にクリーンなコードにできそうな気がします。

あらかじめ、Laravel Pintを使って実装者側で修正して
レビュー時にコーディング規約型のレビューは出来るだけ減らしておけば、
ロジック面でのレビューに時間を割くことにできるんじゃないかなと思います。

# おわりに
Laravel Pintを推していきたいと思います。