【Laravel / PHP】研修期間に受けたコードレビューから学んだ事

# はじめに
今回が初投稿となります。
今回は、入社してから約1ヶ月間の研修で頂いたコードレビューから
アプリケーションを作る上で大切だと学んだことについてまとめたいと思います。
＊ 言語／フレームワークは`Laravel(PHP)`と`Vue(JavaScript)`です。

# 1. コメント
## 誰が見てもわかりやすく書く
今回作成したSNSアプリケーションの中で、ログインユーザがフォローしているユーザリストを表示する画面を作成するために、以下のような手順を取りました。

1\. フォロー関係を表す`follower`テーブルを作成
```php:migrations/2023_02_01_xxxxxx_create_follower_table.php
public function up()
{
    Schema::create('follower', function (Blueprint $table) {
        $table->unsignedBigInteger('following_id')->nullable(false)->comment('フォローしているユーザID');
        $table->unsignedBigInteger('followed_id')->nullable(false)->comment('フォローされているユーザID');
    });
}
```
2\. ログインユーザ自身がフォロー中のユーザを取得
```php:Services/AccountService.php
public function getFollowings(int $userId)
{
    return Follower::select('followed_id')
                    ->where('following_id', $userId)
                    ->get();
}
```
3\. 2で取得した値をコレクションメソッド`pluck`を使って`followed_id(フォローされているユーザid)`のみを取り出す
```php:Controllers/AccountController.php
$followings = $accountService->getFollowings(Auth::id());
$followingIds = collect($followings)->pluck("followed_id");
```
この時、取得した値を**ログインユーザの目線**で**フォローしているアカウント**という意味で`followingIds`という変数に格納しました。
しかし取得したデータベースのカラム名は**フォローされた**を意味する`followed_id`です。
このような内容は、読み手は書き手と同じ認識レベルであるとは限らない為わかりにくい状態です。
誰が見てもわかりやすいようにコメントを記載する必要があります。
```
// データベースのカラム名はfollowed(フォローされた)ですが、主語が変わるので変数名はfollowing(フォローしている)になっている
```

## PHPdoc(ドキュメントコメント)を書く
### PHPdocとは
> Docブロックはコードに詳細な情報を記述することができる、特別な種類のコメント文です。
> このコメントは機能についての理解を促進させる情報を提供するために利用されます。 DocコメントをもとにIDEが自動補完の手がかりとしたり、phpDocumentorがAPIドキュメントを生成します。
引用：[phpDocumentor](https://zonuexe.github.io/phpDocumentor2-ja/references/phpdoc/basic-syntax.html#doc)

PHPdocを書くことで、自分だけではなく他の開発メンバーもコードを理解するのにとても役立ちます。

:::note info
コメントは全ての開発する人にとって、ソースの意図を理解する助けになり、プログラムを読みやすくするため、とても重要である。
:::

# 2. 安全なアプリケーション
## CSRF対策
### CSRF(クロスサイトリクエストフォージェリ)とは
> クロスサイトリクエストフォージェリは、認証済みユーザーに代わって不正なコマンドを実行する、悪意のある攻撃の一種です。
引用：[CSRF保護 9.x Laravel](https://readouble.com/laravel/9.x/ja/csrf.html)

CSRF対策のためには、処理が実行される前に認証済みユーザからのリクエストであるかを確認する必要があります。

> Laravelは、アプリケーションによって管理されているアクティブなユーザーセッションごとにCSRF「トークン」を自動的に生成します。このトークンは、認証済みユーザーが実際にアプリケーションへリクエストを行っているユーザーであることを確認するために使用されます。このトークンはユーザーのセッションに保存され、セッションが再生成されるたびに変更されるため、悪意のあるアプリケーションはこのトークンへアクセスできません。
引用：[CSRF保護 9.x Laravel](https://readouble.com/laravel/9.x/ja/csrf.html)

このセッションに保存されたトークンを使って、認証済みユーザからのリクエストであるかを確認します。

```php:login.blade.php
<form method="POST" action="{{ route('login') }}">
    @csrf
</form>
```
Bladeディレクティブの`@csrf`を使って、以下の内容となる非表示のinputフィールドを生成します。
```php:login.blade.php
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<!-- csrf_token() : 現在のセッションのCSRFトークンを取得 -->
```
formに`@csrf`を記載することで、リクエストにトークンが含まれ、その値とセッションに保存されたトークンが一致することにより認証済みユーザからのリクエストだと確認でき、CSRF対策ができます。
（一致したかどうかは`App\Http\Middleware\VerificationCsrfToken`ミドルウェアが自動的に検証)

:::note info
CSRFのみならず、安全なWEBアプリケーションの開発のために対策が必要である。
:::

# 3. 無駄のない、綺麗で可読性の高いソースコード
## 値の利用頻度を考える
下記のようなソースコードがある場合
```php:HomeController.php
$isLogged = Auth::check();
if ($isLogged) {
    $postNum = $postService->countUserPost(Auth::id());
    $favoriteNum = $postService->countUserFavorite(Auth::id());
}
```
if文の条件として一度しか使わない`Auth::check()`は変数`$isLogged`に格納され、
Serviceのメソッドの引数として`Auth::id()`が複数回呼び出されています。
このような場合は複数回呼び出されるものを変数として定義し、一度しか利用されないものは直接呼び出します。
```php:HomeController.php
$id = Auth::id();
if(Auth::check()) {
    $postNum = $postService->countUserPost($id);
    $favoriteNum = $postService->countUserFavorite($id);
}
```
:::note warn
利用頻度は低くてもあえて変数に定義することにより可読性が上がることもあるので、その状況に合わせることが必要。
:::

## ソースコードを簡略化する
ControllerからViewに下記のような複数の値を渡す場合
```php:HomeController.php
return view('home.index',[
    'title' => $title,
    'posts' => $posts,
    'followee' => $followee,
    'follower' => $follower,
    'favorite' => $favorite,
]);
```
Viewで使う変数名と値となるphpの変数名が同じ場合は`compact関数`を使うと簡略化できます。
```php:HomeController.php
return view('home.index', compact('title', 'posts', 'followee', 'follower', 'favorite'));
```

## アクセサを利用する
テーブルから取得した`created_at`を`Y年m月d日`のフォーマットで表示するために下記のように実装していました。
```php:index.blade.php
<div class="date">{{ $tweet->created_at->format('Y年m月d日') }}</div>
```
このような場合、データベースからデータを取得した際に実行される`アクセサ`を使うと、画面側の実装がより簡潔化できます。

**アクセサとは**
> アクセサは、Eloquentの属性値にアクセスが合った時に、その値を変換するものです。
引用：[Eloquent：ミューテタ／キャスト Laravel 9.x](https://readouble.com/laravel/9.x/ja/eloquent-mutators.html#defining-an-accessor)

モデルに`created_at`を`Y年m月d日`の形にフォーマットするアクセサを定義します。
```php:Tweet.php
protected function formatedCreatedAt(): Attribute
{
    return Attribute::make(
        get: fn ($value, $attributes) => Carbon::parse($attributes['created_at'])->format('Y年m月d日')
    );
}
```
アクセサでフォーマット処理を実装したことにより、画面側ではアクセサを呼び出す処理だけで同じことが実現できます。
```php:index.blade.php
<div class="date">{{ $tweet->formatedCreatedAt }}</div>
```

:::note info
実装方法はその時々の状況によりますが、無駄のない可読性の高いコードは自分にとってもチーム開発において重要である。
:::

# まとめ
初めて自分の学びを投稿してみましたが、記事を書く中で改めて知ることも多く、アウトプットの大切さを実感しています。
まだまだ学ぶことがたくさんあるので、引き続きアウトプットしていきたいと思います！！