LoginSignup
14
11

More than 5 years have passed since last update.

Laravel Eloquent Collection を拡張してみる

Last updated at Posted at 2015-12-23

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

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

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

追加メソッドを記述するクラスを用意

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

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

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

追加メソッドの記述

少し脱線してしまいましたが、説明に戻ります。

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

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

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()というメソッドをオーバーライドします。

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」テーブルのデータを用意します。
艦種と名前だけのシンプルなデータですが、中身はこんな感じです。

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をキーにした連想配列を取得してみます。
メソッドの使い方はこんな感じです。

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
問題なく取得できました。

ちなみに、さっきのメソッドをall()だけにして、コレクションで返すと...
1.png

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

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

まとめ

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

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

14
11
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
11