昨日行ったPHPカンファレンス福岡2024でお話しが出来たエンジニアの方に「LaravelのソースコードはCollectionメソッドの実装を読むのがとっつきやすい」と教えてもらいました。なのでcollectionメソッドの実装を読んでいきたいと思います。
と、その前にCollectionがどういうものかを知る必要があるので今回はCollectionについての概要理解をしていきたいと思います。
Collectionについて
日本語ドキュメントの一文 https://readouble.com/laravel/11.x/ja/collections.html
Illuminate\\\\Support\\\\Collection
クラスは配列データを操作するための、書きやすく使いやすいラッパーです。
元のコレクションは不変であり、すべてのCollection
メソッドは新しいCollection
インスタンスを返します。
最初の一文からは、以下の情報が読み取れます。
- Collectionはクラスであること
- Collectionは配列データを操作するためのラッパー
さらに次の一文からは以下の情報が読み取れます。
- 元のコレクションは不変であること
- 全てのCollectionメソッドは新しいCollectionインスタンスを返す。
配列をCollection化し、Collectionの方を操作することで元の配列は影響を受けることなくデータを扱うことができるということですね。
LaravelのCollectionは昔からあるもののイメージですが、配列のシャローコピーとディープコピーの問題を解決していたと思うとCollectionは素晴らしいと思いました。
👉 今は …${配列} のようなスプレッド演算子を使うことで元の配列に影響を与えることなく配列をコピーすることができるはずです。
Collectionの作り方は以下です。
$collection = collect([1, 2, 3]);
// 少し抽象化する
${collectionにつける変数名} = collect(${collectionにしたい配列})
collect()メソッドを使うことによってCollectionを作れます。
ここで疑問に思いました。
Collectionはクラスなのにインスタンス化しないでいきなりメソッドを使えるのはなぜ?
真相を探るべくLaravelのソースコードを見たらそれっぽい記述を見つけました。
vendor/laravel/framework/src/Illuminate/Collections/helpers.php
use Illuminate\\Support\\Arr;
use Illuminate\\Support\\Collection;
if (! function_exists('collect')) {
/**
* Create a collection from the given value.
*
* @template TKey of array-key
* @template TValue
*
* @param \\Illuminate\\Contracts\\Support\\Arrayable<TKey, TValue>|iterable<TKey, TValue>|null $value
* @return \\Illuminate\\Support\\Collection<TKey, TValue>
*/
function collect($value = [])
{
return new Collection($value);
}
}
読み取れる情報としては
- function_exists がif文の条件に使われている
- if文の中でcollectという関数が定義されている
- 関数はCollectionのインスタンスを返す
- function_existsは名前から察するに関数の存在チェックを行なっていると思われます。関数が存在しない場合のみif文に入れるという事ですね。他のフレームワークでも同じかはわかりませんが、Laravelでは予約語をこのように決めているのですね。
実際にcollect関数を定義しようとしたら以下のようなエラーが出ました。collect()を再定義できないという内容でした。このエラーはfunction_existsが出しているものなのでしょうか。
Fatal error: Cannot redeclare collect() (previously declared in /{User}/vendor/laravel/framework/src/Illuminate/Collections/helpers.php:16) in /{User}/bootstrap/app.php on line 8
- if文の中で関数を定義する事により、「関数の使用条件を満たしているかをチェックする」という考え方ができると思うと学びになりました。もしかしたらアンチパターンかもしれないのでここら辺はもう少し調べる必要がありそうです。
- collectという関数が定義されており、この中でCollectionのインスタンスが返されてます。デフォルト引数が空の配列で引数に何も指定しなかった場合は空のコレクションを返すようです。ドキュメントでは配列を扱いやすくすると記載があるので配列のみを引数にとるかと思ったら、文字列や数字も引数に指定できるようです。やはりドキュメントを読むだけでは分からないことがありますね。
👉 実際の使用を想定したドキュメントにするために細かいことが省かれているのはいいことだと思いました。「気になる人だけソースを見ればいい」みたいな割り切りがあるのかも
まとめ
今回、ソースを読んでみて以下の事が分かりました。
- LaravelのCollectionは配列の扱いやすくするラッパー。配列以外もCollectionに出来るし、空のCollectionも作ることができる
- Collectionを作るcollect() はCollectionのインスタンスを返すヘルパー関数として定義されている。
- ドキュメントだけでは分からないことがある(非常に重要な心持ち)
最初はCollectionの概要を理解する程度でソースコードまで読むつもりは無かったのですが、ドキュメントを正として飲み込むには分からないことが多すぎてソースコードまで手が伸びてしまいました。この「ついつい」という感覚でいけるところまでいってみたいと思いました。
おまけ CursorAI(GPT4)に聞いてみた
入力
下記のコードの解説をお願いします。
"""
if (! function_exists('collect')) {
/**
* Create a collection from the given value.
*
* @template TKey of array-key
* @template TValue
*
* @param \\Illuminate\\Contracts\\Support\\Arrayable<TKey, TValue>|iterable<TKey, TValue>|null $value
* @return \\Illuminate\\Support\\Collection<TKey, TValue>
*/
function collect($value = [])
{
return new Collection($value);
}
}
"""
出力
このコードは、Laravelフレームワークの一部で、collect 関数を定義しています。この関数は、与えられた値からコレクション(Collection)オブジェクトを作成するために使用されます。以下は、コードの各部分についての詳細な説明です:
if (! function_exists('collect')) { ... }: この条件文は、collect 関数がまだ定義されていない場合にのみ、内部のコードを実行します。これは、関数の再定義を防ぐために使われます。
function collect($value = []): collect という名前の関数を定義しています。デフォルトの引数は空の配列 [] です。これは、引数が提供されない場合に空のコレクションを作成するためです。
@template TKey of array-key: この関数がジェネリック型を使用しており、TKey は配列のキー(通常は整数または文字列)を表します。
@template TValue: この関数が扱う値の型を表します。
@param \\Illuminate\\Contracts\\Support\\Arrayable<TKey, TValue>|iterable<TKey, TValue>|null $value: 引数 value は、Arrayable インターフェースを実装したオブジェクト、または iterable(配列やイテレータなど)、または null が指定可能です。
@return \\Illuminate\\Support\\Collection<TKey, TValue>: この関数は、指定された値から作成された Collection オブジェクトを返します。このコレクションは、TKey 型のキーと TValue 型の値を持ちます。
return new Collection($value);: 新しい Collection オブジェクトを作成し、引数で受け取った value をコンストラクタに渡しています。これにより、与えられた値からコレクションが作成されます。
この関数は、配列やオブジェクトをより柔軟に扱うための便利なツールを提供し、Laravelのコレクションメソッドを利用してデータを操作することができます。
コメント