九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、ここのところLaravel関連の記事にアクセスが多くなってきているようで、ド素人の文章ながら読んでくれている人たちに感謝している今日この頃です。m_ _m
で、そんなLaravelですが現在も開発がドンドン進んでいて一週間か二週間すると必ずといってもいいほどマイナーバージョンアップされ、その都度便利な関数やメソッドが追加されています。
これはとても嬉しい半面、きちんとチェックしておかないとせっかく用意されてる便利な機能を知らないという状態になり、あとから
「えっ、こんな便利な書き方できるの!?ゆってよ〜・・・」
ってなることもたまにあったりします。(なので今は、Laravel News に登録してメールマガジンを購読しています。 ^^;)
そして、そんなLaravelの特徴のひとつといってもいい「コレクション」もその例外ではなく、新しいメソッドがドンドン登録されているので、ここいらでこの前公開した「Laravelのよく使うヘルパー関数7選!」のように、個人的に便利だと思うメソッドを7つ選んでみることにしました。
ぜひ、「働き方改革」の波にのって時短コーディングに役立ててください(笑)
目次
groupBy() 値が同じものでグループ化
コレクションのgroupBy()は、値が同じものでグループ化をするというメソッドです。
たとえば、以下のような会員データがあったとします。
$collection = collect([ ['name' => '山田', 'pref' => '東京'], ['name' => '鈴木', 'pref' => '大阪'], ['name' => '佐藤', 'pref' => '東京'], ['name' => '田中', 'pref' => '北海道'], ['name' => '山本', 'pref' => '大阪'] ]);
そして、もしこの会員たちを出身地(pref)ごとに分けたい場合、groupBy()が使えます。
$grouped = $collection->groupBy('pref');
結果は、以下のようになります。
$results = [ '東京' => [ ['name' => '山田', 'pref' => '東京'], ['name' => '佐藤', 'pref' => '東京'] ], '大阪' => [ ['name' => '鈴木', 'pref' => '大阪'], ['name' => '山本', 'pref' => '大阪'] ], '北海道' => [ ['name' => '山本', 'pref' => '北海道'] ] ];
ちなみに、この例ではわかりやすくするために「東京」など全角文字を使っていますが、そうなるとキーの中身も全角文字になってしまうので気をつけてください。実際のコードでは半角文字をつかうべき方がいいと思います。
もし、どうして全角文字を使ったグループ化をしたい場合は以下のように引数を関数化すれば、キーを自由に変更することができます。
$grouped = $collection->groupBy(function($item, $key){ $prefs = [ 'tokyo' => '東京', 'osaka' => '大阪', 'hokkaido' => '北海道' ]; return array_search($item['pref'], $prefs); });
やってることは、$itemから都道府県名を抽出して、return の部分で該当する都道府県キーをarray_search()を使って返しています。
で、結果はこうなります。
$results = [ 'tokyo' => [ ['name' => '山田', 'pref' => '東京'], ['name' => '佐藤', 'pref' => '東京'] ], 'osaka' => [ ['name' => '鈴木', 'pref' => '大阪'], ['name' => '山本', 'pref' => '大阪'] ], 'hokkaido' => [ ['name' => '山本', 'pref' => '北海道'] ] ];
とても便利ですね!
map() 全要素に色々な加工をする
map()を使えば、コレクションの全要素に色々な加工を加えることができます。
たとえば、以下のような年齢が入った会員データがあったとしましょう。
$collection = collect([ ['name' => '山田', 'age' => 22], ['name' => '鈴木', 'age' => 25], ['name' => '佐藤', 'age' => 18], ['name' => '田中', 'age' => 30], ['name' => '山本', 'age' => 16] ]);
そして、例えば名前の後ろに年齢も表示するなら、map()が使えます。
$multiplied = $collection->map(function($item, $key){ $item['name'] .= '('. $item['age'] .'才)'; return $item; });
map()を指定すると、全要素の$itemと$valueが取得できるので、これらのデータを使って加工をすることができます。
結果は、
$results = [ ['name' => '山田(22才)', 'age' => 22], ['name' => '鈴木(25才)', 'age' => 25], ['name' => '佐藤(18才)', 'age' => 18], ['name' => '田中(30才)', 'age' => 30], ['name' => '山本(16才)', 'age' => 16] ];
というようになります。
もちろん、新しいデータを追加することもできて、以下のようにすると新しい「age_unit」を追加することができます。
$multiplied = $collection->map(function($item, $key){ $item['age_unit'] = $item['age'] .'才'; return $item; });
結果は、こうなります。
$results = [ ['name' => '山田', 'age' => 22, 'age_unit' => '22才'], ['name' => '鈴木', 'age' => 25, 'age_unit' => '25才'], ['name' => '佐藤', 'age' => 18, 'age_unit' => '18才'], ['name' => '田中', 'age' => 30, 'age_unit' => '30才'], ['name' => '山本', 'age' => 16, 'age_unit' => '16才'] ];
※もし、モデルを使った場合だとaccessorがあるので、そっちの使う手もあると思います。
chunk() 指定された数ごとに分割
chunkを使うと、たくさんのデータを「2個ずつ」とか「3個ずつ」というようにデータの分割することができます。
実際の例を見てみましょう。
$collection = collect([ ['name' => '山田'], ['name' => '鈴木'], ['name' => '佐藤'], ['name' => '田中'], ['name' => '山本'], ]); $chunks = $collection->chunk(2);
これは、名前のデータを2つずつに分けるコードで、結果は以下のようになります。
$results = [ '0' => [ ['0' => ['name' => '山田']], ['1' => ['name' => '鈴木']] ], '1' => [ ['2' => ['name' => '佐藤']], ['3' => ['name' => '田中']] ], '2' => [ ['4' => ['name' => '山本']] ] ];
ちなみにこれがどこで役立つかというと、次のような「段組みされた表示」をしたいときです。
つまり、こんなカンジで foreach()を2つ使ってやると簡単に2段のテーブルを作成できるというわけですね。
<table> @foreach ($chunks as $items) <tr> @foreach ($items as $item) <td>{{ $item['name'] }}</td> @endforeach </tr> @endforeach </table>
sortBy(sortByDesc) あるデータ順に並べ替え
sortBy()とsortByDesc()を使うと、ある特定のデータ順にコレクション・データを並べ替えることができます。
例えば、以下のようなテストの点数が入った生徒のデータがあったとします。
$collection = collect([ ['name' => '山田', 'score' => 98], ['name' => '鈴木', 'score' => 85], ['name' => '佐藤', 'score' => 32], ['name' => '田中', 'score' => 77], ['name' => '山本', 'score' => 91] ]);
これを、点数順に並べ替えたい、そんなときにsortBy()を使いましょう。
$sorted = $collection->sortBy('score');
結果は、こうなります。
$results = [ 2 => ['name' => '佐藤', 'score' => 32], 3 => ['name' => '田中', 'score' => 77], 1 => ['name' => '鈴木', 'score' => 85], 4 => ['name' => '山本', 'score' => 91], 0 => ['name' => '山田', 'score' => 98], ];
ただ、ここで気をつけておかないといけないことが1つあります!それは、
キーが保持されている
ということです。もし、キーも順に並べ替えたいときは、values()をかましてください。
$sorted = $collection->sortBy('score')->values();
これで、キーも点数順に並び替えられました。
$results = [ 0 => ['name' => '佐藤', 'score' => 32], 1 => ['name' => '田中', 'score' => 77], 2 => ['name' => '鈴木', 'score' => 85], 3 => ['name' => '山本', 'score' => 91], 4 => ['name' => '山田', 'score' => 98], ];
※もし逆に点数が多い順にしたい場合は、sortByDesc()を使ってください。使い方は同じです。
filter() 必要なデータだけをゲット
filter()メソッドを使えば、簡単に必要のあるデータだけを取得することができます。
例えば、このような所属している部活がわかる生徒データがあったとします。
$collection = collect([ ['name' => '山田', 'club' => '野球部'], ['name' => '鈴木', 'club' => 'サッカー部'], ['name' => '佐藤', 'club' => '卓球部'], ['name' => '田中', 'club' => '野球部'], ['name' => '山本', 'club' => '野球部'] ]);
この中から、野球部に所属してる人たちだけをピックアップしたいときにfilter()が使えます。
$filtered = $collection->filter(function ($item, $key) { // ここがtrueのものだけ残る return ($item['club'] == '野球部'); });
使い方は簡単で、true を返せばそのデータは残り、falseの場合はそのデータは $filtered には入ってきません。
結果はこうなります。
$results = [ 0 => ['name' => '山田', 'club' => '野球部'], 3 => ['name' => '田中', 'club' => '野球部'], 4 => ['name' => '山本', 'club' => '野球部'], ];
filter()もキーが前のまま保持されるので、もしキーをゼロから並べ替えたい場合はvalues()を追加して使いましょう。
$filtered = $collection->filter(function ($item, $key) { return ($item['club'] == '野球部'); })->values();
dd() コレクションの中身をチェックする
dd()を使うと、コレクションの中身を表示することができます。
そこだけ見るとtoArray()して中身を表示するのと違いがないように思いますけど、実はこのdd()にはもう一つ便利な使い方があるんです。
例をみてみましょう。
例えば、点数が入ったデータがあって、さらにすべての点数に1を足す場合は、map()を使えばこうなります。
$collection = collect([ ['name' => '山田', 'score' => 10], ['name' => '鈴木', 'score' => 8], ['name' => '佐藤', 'score' => 3], ['name' => '田中', 'score' => 7], ['name' => '山本', 'score' => 9] ]) ->map(function($item, $key){ $item['score'] += 1; return $item; });
で、さらに10を足す場合は、こんな風に新しいmapを数珠つなぎで追加すればいいわけですよね。
$collection = collect([ ['name' => '山田', 'score' => 10], ['name' => '鈴木', 'score' => 8], ['name' => '佐藤', 'score' => 3], ['name' => '田中', 'score' => 7], ['name' => '山本', 'score' => 9] ]) ->map(function($item, $key){ $item['score'] += 1; return $item; }) ->map(function($item, $key){ $item['score'] += 10; return $item; });
では、この状態で1を足した直後のデータを確認したくなったらどうでしょうか??おそらく、最後のmap()をコメントアウトするなどしてdd();を実行したりするんじゃないでしょうか。
でも、dd()はそんな面倒なことをしなくても、こうすれば途中の中身を表示してくれます。
$collection = collect([ ['name' => '山田', 'score' => 10], ['name' => '鈴木', 'score' => 8], ['name' => '佐藤', 'score' => 3], ['name' => '田中', 'score' => 7], ['name' => '山本', 'score' => 9] ]) ->map(function($item, $key){ $item['score'] += 1; return $item; }) ->dd() // 中身を表示(ここで処理がとまる) ->map(function($item, $key){ $item['score'] += 1; return $item; });
つまり、元のコードを崩さなくても中身をチェックできるので、開発する側からするととても便利なメソッドになってます。
※ちなみに、コレクションをechoしたときどうなるか知ってますか??実は、エラーにはならず、jsonになって中身を表示してくれます。この辺も嬉しいですね。
pluck() キーを指定してほしいデータだけ取得する
pluckはシンプルですけどとても便利なメソッドで、指定したキーのデータだけを取得することができます。
例えば、IDが入ったユーザーデータがあったとします。
$collection = collect([ ['id' => 1, 'name' => '山田'], ['id' => 2, 'name' => '鈴木'], ['id' => 5, 'name' => '佐藤'], ['id' => 7, 'name' => '田中'], ['id' => 8, 'name' => '山本'] ]);
もしこのデータを、
- キーがID
- 値がname
の連想配列にしたい場合、pluck()を使って楽に実現することができます。
$filtered = $collection->pluck('name', 'id');
結果は、こうなります。
$results = [ 1 => '山田', 2 => '鈴木', 5 => '佐藤', 7 => '田中', 8 => '山本' ];
もし名前を通常の配列で取得したい場合は、nameだけを指定すればOKです。
$filtered = $collection->pluck('name');
結果はこうです。
$results = [ 0 => '山田', 1 => '鈴木', 2 => '佐藤', 3 => '田中', 4 => '山本' ];
ちなみに、独自のメソッドも作れちゃう!
Laravelは独自の○○を作るというのが、特徴のひとつだったりもしていて、コレクションでも独自のメソッドを作ることもできます。
では、例を見てみましょう。
開発をしていると結構「あるある」だと思うんですけど、数字を3桁カンマ形式にしたい場合です。もちろんいちいちnumber_format()すればいい話なんですけど、コレクションでよく使う場合は以下のように独自メソッドを登録しておくといつでも呼び出せて便利だと思います。
Collection::macro('number_format', function () { return $this->map(function ($value) { return number_format($value); }); });
独自メソッドを追加するには、macro()を使います。
やっているのは、中身でmap()を使って全要素にnumber_format()でデータ加工してるだけです。
使い方はこうです。
$collection = collect([100000, 200000, 1500000]); $numbers = $collection->number_format();
ちなみに、Collectionのnamespace宣言を忘れないようにしてくださいね。
use Illuminate\Support\Collection;
また、どこからでも呼び出すために、この独自メソッドの定義はapp/Providers/AppServiceProvider.phpのboot()にでも入れておきましょう。
このテクニックを使えば、会社独自の請求書番号を作ったりするのも楽にいけると思います。(僕の場合はaccessorをよく使いますけどね ^^)
おわりに
ということで、今回は僕がおすすめする(実際よく使う)コレクション・メソッド7つを紹介しました。
いかがだったでしょうか??
けっこう定番ものが多くなっちゃったなーって思ってるんで、もし「そんなの知ってるよ!」状態になっちゃってたらゴメンナサイ。m_ _m
あと、これもちなみにですけど、コレクションのメソッド名を見てると lodash.jsと同じものが多いので、おそらくここから影響を受けたんだろうなーって勝手に思ってます。Laravelのそういう吸収力の高いところもいいですね!(てか、lodashが先であってますよね??)
ではでは〜。