Laravel Eloquent Collection を拡張してみる

Eloquent ORMでDBから値を取得すると、Collectionオブジェクトで返ってきます。
取得後のデータを色々と加工できてとても便利です。
しかし、どうしても欲しい形式のデータに変換できなかったので、変換して返すメソッドを追加してみます。

この拡張は、サービスプロバイダーに登録する必要が無いので、とても簡単に出来ます。

対象のバージョン（動作確認済み）は5.1です。
DBは、Postgresql9.4.4を使用しています。


#追加メソッドを記述するクラスを用意
Illuminate\Database\Eloquent\Collectionを継承したクラスを用意します。
追加メソッドについては後ほど説明します。

こんな感じで用意してみました。

```php:app/KantaiCollection.php
<?php namespace App\KantaiCollection;

use Illuminate\Database\Eloquent\Collection;

class KantaiCollection extends Collection
{

}
```

正直これがやりたかっただけです。寒いと思った方は、ブラウザバックして下さい！

...ですが、実は全く無関係な話ではなく、艦これで使用されているかは不明ですが、DMMが運営しているゲームにはLaravel5を導入しているものもあるようです。
>DMMゲームのログ解析~ログ収集と解析の概要~
>[http://labotech.dmm.com/entry/2015/08/20/184051](http://labotech.dmm.com/entry/2015/08/20/184051 "DMMゲームのログ解析~ログ収集と解析の概要~")


#追加メソッドの記述
少し脱線してしまいましたが、説明に戻ります。

私が欲しかったデータ形式というのは、指定したカラムをキーにした連想配列です。
理由としては、ループ処理の時にissetで存在確認ができ、パフォーマンス向上(脱in_array)が見込めたからです。
また、連想配列の性質を利用して、SQLでdistinctを使用しなくても、重複を省いた一覧を取得できます。
(どちらのパフォーマンスが上かは調べてないですが)

ということで、こんな感じに実装してみました。

```php:app/KantaiCollection.php
<?php namespace App\KantaiCollection;

use Illuminate\Database\Eloquent\Collection;

class KantaiCollection extends Collection
{
    /**
     * 指定したカラムをキーにHash化 
     * 
     * @param  string $column
     * @return array 
     */
    public function toHash($column)
    {
        $result = [];

        foreach ($this->items as $item)
        {
            $result[$item[$column]] = $item;
        } 

        return $result;
    }
}
```
簡単に処理を説明すると、
$this->itemsには、取得時の配列とオブジェクトが入っています。
なので、これをループさせ、指定したカラムをキーにした配列に入れなおしています。

#Eloquent ModelのnewCollectionをオーバーライドする
Eloquent Modelを用意し、newCollection()というメソッドをオーバーライドします。

```php:app/Kantai.php
<?php namespace App\Kantai;
    
use Illuminate\Database\Eloquent\Model;

use App\KantaiCollection;

class Kantai extends Model
{
    /**
     * コレクションの拡張
     *
     * @param  array  $models
     * @return \App\KantaiCollection
     */
    public function newCollection(array $models = [])
    {
        return new KantaiCollection($models);
    }
}
```

これで、先ほどのメソッドが使えるようになりました！


ちなみに、ドキュメントにも記載されているので、ご存知の方が多いかもですが、
artisanコマンドでモデル作成時に-mオプションを付けると、マイグレーションも同時に作成されるので便利です。
(seederも一緒に作成出来るオプションがあれば、さらに便利なんだけど)



#データを用意して確認してみる
「Kantais」テーブルのデータを用意します。
艦種と名前だけのシンプルなデータですが、中身はこんな感じです。

```lang:Kantais
 id |   type   | name |     created_at      |     updated_at      
----+----------+------+---------------------+---------------------
  1 | 駆逐艦　　| 吹雪 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  2 | 駆逐艦　　| 睦月 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  3 | 軽巡洋艦　| 川内 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  4 | 軽巡洋艦　| 神通 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  5 | 重巡洋艦　| 愛宕 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  6 | 重巡洋艦　| 利根 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  7 | 戦艦　　　| 長門 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  8 | 戦艦　　　| 金剛 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
  9 | 空母　　　| 赤城 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00
 10 | 空母　　　| 加賀 | 2015-12-24 00:00:00 | 2015-12-24 00:00:00

```


では、レコード全てを取得して、nameをキーにした連想配列を取得してみます。
メソッドの使い方はこんな感じです。

```php:app/Http/Controllers/KanColleController.php
<?php

namespace App\Http\Controllers\KanColle;

use App\Http\Controllers\Controller;

class KanColleController extends Controller
{
    public function index()
    {
        $kantai = app('App\Kantai');
        dd($kantai->all()->toHash('name'));
    }
}
```

実行してみる
![2.png](https://qiita-image-store.s3.amazonaws.com/0/105496/cd1ac488-17ea-71cf-5c86-dcb220e3ea84.png)
問題なく取得できました。

ちなみに、さっきのメソッドをall()だけにして、コレクションで返すと...
![1.png](https://qiita-image-store.s3.amazonaws.com/0/105496/7ea014e1-9d8b-0e01-200f-01bebe59be68.png)

当たり前ですが、Collectionオブジェクトの名前が「KantaiCollection」になります。

ということで、２回目ですが、これがやりたかっただけなんです :persevere: 
以上、お付き合いありがとうございました。

#まとめ
- Illuminate\Database\Eloquent\Collectionを継承したクラスを用意
- そこに、追加したい処理を記述
- Eloquent ModelのnewCollection()をオーバーライドする



ご指摘や、もっと良いやり方があるという方は、ぜひコメント下さい。