単体系の複数形のdocとタイプヒンティング戻り値の設定で補完できるようにしてプログラミングを快適にするTips

IDEはInteliJを使っているが、ファイルに下線が表示されるのがものすごく気になる。
設定で下線表示されないようにレベルを落とせばいいんだろうけど、それはしたくない。
できるだけおれおれ的なことはせず、グローバルにピュアな状態でアタックしていきたい。

Laravelになるが題材として取得系のデータ操作に限定し、単体系と複数形を用意した。
以下、Repositoryクラスを例に変移を見ていく。
私の手元のIDEでは以下の状態では、`firstById`と`getAll`に下線がついている。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    public function firstById(string $id, array $relations = [])
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }

    public function getAll(array $relations = [])
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

非常に気持ち悪いので解消していく。
まず、phpdocを生成しよう。
優秀なIDEは、`/**`と入力してreturn押下することでdocが自動生成される。
あとはそれで本当に正しいのかあなたがチェックするだけだ。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    /**
     * @param string $id
     * @param array  $relations
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model
     */
    public function firstById(string $id, array $relations = [])
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }

    /**
     * @param array $relations
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public function getAll(array $relations = [])
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

名前空間が長くて使ってるPCによっては見づらいのでuse句を使って最適化する。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    /**
     * @param string $id
     * @param array  $relations
     * @return Builder|Model
     */
    public function firstById(string $id, array $relations = [])
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }

    /**
     * @param array $relations
     * @return Builder[]|Collection
     */
    public function getAll(array $relations = [])
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

次にタイプヒンティングの戻り値を定義していく。
優秀なIDEは下線にマウスオーバーすると詳細がでてきて解決できそうなものであればそのままクリックでこれまた自動生成される。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    /**
     * @param string $id
     * @param array  $relations
     * @return Builder|Model
     */
    public function firstById(string $id, array $relations = []): Builder|Model
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }

    /**
     * @param array $relations
     * @return Builder[]|Collection
     */
    public function getAll(array $relations = []): Builder|Collection
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

`firstById`で実際に返される型は、そのModelクラス（この例でいうと`App\Models\Car`）ではあるが、
App\Models\Carは`Illuminate\Database\Eloquent\Model`を継承しているので、現状のfirstByIdでも動作はする。

***システム的にはなんら問題ない！***

が、プログラミングをする上では少し弱い。
carモデルはcar_class_type_idをプロパティーとして持っているが、今のままだと補完できない。
何故なら、firstByIdの戻り値の型がCarではないとIDEが認識しているから。

```web.php
Route::get('/', function (\App\Repositories\CarRepository $carRepository) {
    $car = $carRepository->firstById(1);
    dd($car->car_class_type_id);
});
```

システム的には、carはcar_class_type_idを取得できるし、問題ないがそのシステムを作るためのプログラミングが快適であれば保守性も可視性もより良くなる。

ということで返り値の型がcarであると認識させるために少し補正する。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    /**
     * @param string $id
     * @param array  $relations
     * @return Car|Builder
     */
    public function firstById(string $id, array $relations = []): Car|Builder
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }

    /**
     * @param array $relations
     * @return Builder[]|Collection
     */
    public function getAll(array $relations = []): Builder|Collection
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

ModelクラスはCarクラスで継承されているので、せっかく自動生成されはしたが、消した。
その代わり、Carクラスをdocのreturnにもタイプヒンティングの戻り値にも指定した。

これによって先ほどの、`$car->car_class_type_id`の部分がIDE上では色が変わりアクティブになっていることがわかる。
`$car->ca`まで打てば補完がでるようになる。

![スクリーンショット 2022-03-03 1.16.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125469/8c64b24a-f339-4412-b480-1108bf3e4abf.png)


そしたら次は、`getAll`。
`getAll`で実際に返される型は、Collection型。
タイプヒンティングの戻り値的にはOKだし動作する。

ただ、先ほどの`firstById`の結果と同じようにプログラミングを行う上ではこれまた弱い。
Collection型で複数形の取得なので、どこかでループ処理をすることが想定される。


```web.php
Route::get('/', function (\App\Repositories\CarRepository $carRepository) {
    $cars = $carRepository->getAll();
    foreach ($cars as $car) {
        dump($car->car_class_type_id);
    }
});
```

foreach内で、dump($car->car_class_type_id);しているが、システム的に取得もできるし動作もする。
が、補完ができないのでこれもコピペしないプログラマーはタイポミスする可能性がある。
まぁ気づいて修正すれば良いのですが、生産性だとか効率の話になります。

ということでこれもループ内で補完が効くように少し補正する。

```CarRepository.php
<?php

namespace App\Repositories;

use App\Core\Repositories\Bases\BaseRepository;
use App\Models\Car;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;

class CarRepository extends BaseRepository
{
    /**
     * @var Car
     */
    public Car $car;

    /**
     * @param Car $car
     */
    public function __construct(Car $car)
    {
        parent::__construct();
        $this->car = $car;
    }

    /**
     * idから車両情報を取得する
     *
     * @param string $id
     * @param array  $relations
     * @return Car|Builder
     */
    public function firstById(string $id, array $relations = []): Car|Builder
    {
        return $this->car::with($relations)
            ->where('id', $id)
            ->firstOrFail();
    }


    /**
     * 廃車ではない車両情報を取得する
     *
     * @param array $relations
     * @return Car[]|Builder[]|Collection
     */
    public function getAll(array $relations = []): Car|Builder|Collection
    {
        return $this->car::with($relations)
            ->where('is_abolition', false)
            ->orderBy('display_priority')
            ->get();
    }
}
```

これだけです。（これが最終形態）
補完が効くようになっているのはdocのreturnに`Car[]`を追加したからです。
タイプヒンティングの型指定の`Car`によって、ではありません。
タイプヒンティングの型指定は`Car[]`とすることができず
`複数型はドキュメント型でのみ許可されています`と警告されるので、つまりやるなら`array`を定義しろということですが、Collectionを定義してるのでわざわざ複雑にするまでもないだろうとの判断で、docに合わせる形で定義しました。

docはIDEを操作しやすくするもの
タイプヒンティングはシステムの堅牢性をあげて保守性を高めるためのもの
と認識しています。

repositoryの変更はレビュー時に特にチェックして行えばみんなが快適に作業できるようになると思います。
ものづくりに幸あれ！