[Laravel] IDEによるコード補完まとめ

ほぼ自分用メモ
Laravel は Facade 使いまくりで初期状態ではIDEでコード補完できない。

# どうやるの

IDE でコード補完用のファイルを生成してくれる神ライブラリがあるのでこれを使う。
[barryvdh/laravel-ide-helper](https://github.com/barryvdh/laravel-ide-helper)

上記のライブラリでできることは下記3つ

* Facade を使ったコード補完ファイルの生成
* データベースから生成した`Eloquent\Model`クラスに対してプロパティアクセスの為のコード補完ファイルの生成
* コンテナから取得したオブジェクトに対してコード補完ファイルの生成（PhpStorm限定）

生成方法は下記の2つ

* ServiceProvider に登録し、アプリケーションの実行毎に最新のコード補完ファイルを生成する
* コマンドラインから手動で生成する

ServiceProvider に登録する方法は常に最新のものが生成されるメリットはありそうなものの、
開発中のオーバーヘッドが大きくなりそうなので、**本記事ではコマンドラインから手動生成のみ扱う**。
また、生成オプション等は色々指定できるが、本記事では**よく使いそうなものだけ抜粋**。
詳しくはライブラリのDocument参照。

# 下準備

例のごとく composer でライブラリをインストールする。

```bash
composer require --dev barryvdh/laravel-ide-helper
```

Model のプロパティ補完を行う場合は  [doctrine/dbal](https://github.com/doctrine/dbal) が必要なのでこちらもインストール。

```bash
composer require --dev doctrine/dbal
```

# Facade の補完

`php artisan ide-helper:generate` を実行する。

```bash
$ php artisan ide-helper:generate
A new helper file was written to _ide_helper.php
```
これだけで`_ide_helper.php`というファイルが生成され、
IDEに読み込ませるとFacadeのコード補完ができるようになる。

# Model のプロパティ補完

仕組み的には [doctrine/dbal](https://github.com/doctrine/dbal) を使い実際にデータベースにアクセス、
各テーブルのカラムを判別しコードに落としている。

`php artisan ide-helper:model` を実行すると下記の通り確認のプロンプトが出る。

```
$ php artisan ide-helper:model

 Do you want to overwrite the existing model files? Choose no to write to _ide_helper_models.php instead? (Yes/No):  (yes/no) [no]:
```

yes … 既に存在している Model クラスに対してプロパティのアノテーションを追加する
no(default) … 別ファイル (`_ide_helper_models.php` ) を生成する

yes の場合はこんな感じの phpDoc が各モデルに追記される。

```php
namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * App\User
 *
 * @mixin \Eloquent
 * @property int $id ID
 * @property string $user_name ユーザー名
 * @property \Carbon\Carbon|null $created_at 登録日時
 * @property \Carbon\Carbon|null $updated_at 最終更新日時
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUserName($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value)
 */
class User extends Model
{
  /** ～～ 略 ～～ **/
}
```

ちなみにModelディレクトリを変更している場合は`--dir`オプションで相対パスを指定する必要がある。

```bash
php artisan ide-helper:models --dir="path/to/models" --dir="app/src/Model"
```

# コンテナから取得したオブジェクトのコード補完

**!! PhpStorm限定 !!**

まず何が可能になるか分かりにくいと思うのでサンプル。

```php
<?php
$router = app('url');
$router->route('name');  // \Illuminate\Routing\UrlGenerator のコード補完ができる

$logger = app('log');
$logger->warning('わーにんぐ！'); // \Illuminate\Log\Writer のコード補完ができる
```

下記の記事が詳しい。
[PhpStorm の PHPSTORM_META でサービスロケーターとかを入力補完](http://ngyuki.hatenablog.com/entry/2015/02/20/134239)

PhpStormのメタファイルを `php artisan ide-helper:meta` で生成してくれる。

```bash
$ php artisan ide-helper:meta
A new meta file was written to .phpstorm.meta.php
```

# 所感

* PhpStorm 強い
* DIコンテナって命名が嫌になった。使う側がDIとして使うかサービスロケーターとして使うかだから、ただのコンテナでいいやん的な

# おまけ）なんでコード補完できないの

下記4パターンはほぼ等価な動き。
下3つはエイリアスなのでおまけで書いたようなもんだけど問題は一番上。
なんせ<strong>`\DB`なんてクラスは存在しない</strong>。
公式マニュアルでもバリバリこの書き方を使っていて、当然のごとくコード補完ができない。

```php
\DB::select('select * from users where active = ?', [1]);
```
```php
app('db')->select('select * from users where active = ?', [1]);
```

```php
\Illuminate\Container\Container::getInstance()->make('db')
    ->select('select * from users where active = ?', [1]);
```
```php
$app = \Illuminate\Foundation\Application::getInstance();
($app['db'])->select('select * from users where active = ?', [1]);
```

どうなってるかというと以下のような流れ

1. `config/app.php` の `aliases` が [`class_aliase()`](http://php.net/manual/ja/function.class-alias.php) 関数に登録される
2. `\DB` が呼ばれると 1 で登録した `\Illuminate\Support\Facades\DB` が呼ばれる
3. 存在しないstatic関数が呼ばれているので `Illuminate\Support\Facades\DB` の親クラス(`\Illuminate\Support\Facades\Facade`) の [`__callStatic()`](http://php.net/manual/ja/language.oop5.magic.php)マジックメソッドが呼ばれる
4. 3 のマジックメソッド内で サービスロケーター内の 'db' が呼ばれる
    (`Illuminate\Support\Facades\DB`にアクセサとして記載されている)。
5. 4 で呼ばれたオブジェクトの select メソッドが呼ばれる。

サービスロケーターでは既に ServiceProvider で 'db' が登録済になっているので非常に簡略化して記述することが可能。


余談だけどサービスロケータを簡略化して呼び出すことをFacadeと呼んでいて、デザインパターンの Facade じゃない気がする…