目次
- あまり知られていないLaravelのコレクションメソッド #1: macro
- あまり知られていないLaravelのコレクションメソッド #2: concat (本記事)
背景
Laravelのコレクション、使いこなしていますか?
以下の記事を先程読んで面白いと思いました。
「あなたは使ったことがないLaravelのコレクションメソッド10選」
ただし、英語で書かれていますし、各メソッドの説明が不十分に感じました。
ということで、以上の記事を翻訳する上で、それぞれのメソッドの活用方法を説明していきたいです!
今回はCollectionのconcatメソッドを解説します!
concat: コレクションにarrayや他コレクションの中身を追加する
概要
インスタンスメソッドになり、以下のように呼び出すことができます。
$collection = collect([/* ... */]);
$collection->concat(/* ... */);
既存コレクションに、arrayの中身を追加できます。
$collection = collect([1, 2, 3]);
$collection->concat([4]);
// [1, 2, 3, 4]
他コレクションの中身も追加できます!
$collection = collect([1, 2, 3]);
$collection2 = collect([4]);
$collection->concat($collection2);
// [1, 2, 3, 4]
ソースコードを読みましょう
concatメソッドのソースコードは以下になります。(Laravel 11.xのソースコードになります)
/**
* @template TKey of array-key
*
* @template-covariant TValue
*
* @implements \ArrayAccess<TKey, TValue>
* @implements \Illuminate\Support\Enumerable<TKey, TValue>
*/
class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
{
// ...
/**
* Push all of the given items onto the collection.
*
* @template TConcatKey of array-key
* @template TConcatValue
*
* @param iterable<TConcatKey, TConcatValue> $source
* @return static<TKey|TConcatKey, TValue|TConcatValue>
*/
public function concat($source)
{
$result = new static($this);
foreach ($source as $item) {
$result->push($item);
}
return $result;
}
// ...
}
ソースコードを読むことで、2つのことがわかります。
追加されるarrayやコレクションのキーが捨てられる
今までキーのないarrayやコレクションでconcatメソッドを使ってきましたが、キーを指定するとどうなるでしょう?
$collection = collect([1, 2, 3]);
$collection->concat(['key' => 4]);
// [1, 2, 3, 4]
キーが全く考慮されず、値を追加しています。
ソースコードをもう一度読むと原因がわかります。
// ...
foreach ($source as $item) {
$result->push($item);
}
// ...
foreachを使って追加されるarrayを処理しているため、キーが捨てられます。
既存コレクションにキーがあれば、追加キーがいい感じに追加される
追加元のコレクションにキーがある場合、キーはどう変わるでしょう?
実は普通のarrayに値を追加する場合と一緒です。
上記のように、キーを省略して新規要素を追加する場合、 追加される数値添字は、使用されている添字の最大値 +1 (ただし、少なくとも 0 以上) になります。 まだ数値添字が存在しない場合は、添字は 0 (ゼロ) となります。
つまりこういう感じです。
$collection = collect([1 => 1, 2 => 2, 3 => 3]);
$collection->concat(['key' => 123]);
// [1 => 1, 2 => 2, 3 => 3, 4 => 123]
$collection = collect(['key1' => 1, 'key2' => 2, 'key3' => 3]);
$collection->concat(['key' => 123]);
// ['key1' => 1, 'key2' => 2, 'key3' => 3, 0 => 123]
型の定義がちゃんと変わる...?
下記はPHPStanで検証しています
上級PHPになりますが、コレクションにジェネリックスが入っています。
$collection = collect([1, 2, 3]); // \Illuminate\Support\Collection<int,int>
concatメソッドを使うとジェネリックスがちゃんと変わります。
$collection = collect([1, 2, 3]); // \Illuminate\Support\Collection<int,int>
$collection->concat(['a']); // \Illuminate\Support\Collection<int,int|string>
// [1, 2, 3, 'a']
ただし、stringのキーを持っているコレクションに値を追加すると...
$collection = collect(['ab' => 1]); // \Illuminate\Support\Collection<string,int>
$collection->concat([2]); // \Illuminate\Support\Collection<string,int> !!!
// ['ab' => 1, 0 => 2] !!!
どうやら型と実際のarrayは一致しないようです。
PHPStanのバグ?Laravelのバグ?私の脳みそのバグ?はっきりわからないので調査中です。
もしわかる人がいれば是非コメントお願いします!!
参考
まとめ
いかがでしたか?
今度はpadメソッドなど、あまり知られていないLaravelのコレクションメソッドをさらに解説していきたいです。
ではまた!