先日Laravelコレクションについての記事を書きましたが、ちょっとこんがらがったことがあったので明確にしておきたいと思い記事にします。
それは、$collection->implode()
メソッドと$collection->join()
メソッドの違いです。あれ、どっちを使ったらいいんだっけ!?となりませんか?
本ページではこれら2つのメソッドの違いを忘れないようにまとめたいと思います。
そもそもなぜこんがらがるか
素のPHPでも同名の関数があること。そして、join()
関数は、implode()
のエイリアス(別名)です。
$array = ["a", "b", "c"];
$string = implode(",", $array); // "a,b,c"
// OR
$string = join(",", $array); // "a,b,c"
どちらかしか使わない人が多いとは思いますが、知識として「同じ」と思っています。
なので、Laravelコレクションの同名メソッドはどちらに対応しているんだっけ、と頭がごちゃごちゃしてしまい、すぐに出てこないように思います。
$collection->implode()
内部を見てみる
コードの内部を見てみると、普通にPHPのimplode()
を使っていますね。
/**
* Concatenate values of a given key as a string.
*
* @param callable|string $value
* @param string|null $glue
* @return string
*/
public function implode($value, $glue = null)
{
if ($this->useAsCallable($value)) {
return implode($glue ?? '', $this->map($value)->all());
}
$first = $this->first();
if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
return implode($glue ?? '', $this->pluck($value)->all());
}
return implode($value ?? '', $this->items);
}
処理の流れとしては、第1引数にクロージャ(匿名関数)を渡せば、10~12行目で$collection->map()
したのちimplode()
してくれます。
コレクションが配列やオブジェクトだった場合は、16~18行目で第1引数を$collection->pluck()
してからimplode()
。
それ以外で第1引数が文字列(連結文字)だった場合は、ただのimplode()
となります。
基本的な使い方
コード例を挙げます。
まずは最も簡単な例。$collection->implode()
を、PHPのimplode()
と同じようにして使う例です。
$collection = collect(['りんご', 'ばなな', 'みかん']);
$result = $collection->implode(', ');
echo $result; // "りんご, ばなな, みかん"
連想配列もOKなのは、PHPのimplode()
と同様です。
$collection = collect(['AAA'=>'りんご', 1=>'ばなな', 2=>'みかん']);
$result = $collection->implode(',');
echo $result; // "りんご, ばなな, みかん"
連想配列・オブジェクトのキーを元に連結した例
おそらくこの利用方法が多いでしょうか。$collection->implode()
を使うメリットとして、オブジェクトや連想配列の任意のキーに対応する値を連結可能です。
例えば、以下のように、第1引数のname
をキーとする値を第2引数の,
で連結しています。
$people = collect([
['name' => '山田太郎', 'age' => 25],
['name' => '佐藤花子', 'age' => 30],
['name' => '鈴木一郎', 'age' => 28]
]);
$names = $people->implode('name', ', ');
echo $names; // "山田太郎, 佐藤花子, 鈴木一郎"
クロージャを利用した例
先ほどのとおり、第1引数にクロージャを指定すれば、内部的に$collection->map()
に通してくれます。
以下は、名前(○年生)というフォーマットにして連結しました。
$students = collect([
['name' => '山田太郎', 'grade' => 5],
['name' => '佐藤花子', 'grade' => 4],
['name' => '鈴木一郎', 'grade' => 6]
]);
$formattedStudents = $students->implode(function ($student) {
return "{$student['name']} ({$student['grade']}年生)";
}, ', ');
echo $formattedStudents;
// 結果: "山田太郎 (5年生), 佐藤花子 (4年生), 鈴木一郎 (6年生)"
ただ、7~9行目は以下のように自分で$collection->map()
した方が読みやすい、という人もいるかもしれませんね。
$formattedStudents = $students->map(function ($student) {
return "{$student['name']} ({$student['grade']}年生)";
})->implode(', ');
$collection->join()
$collection->join()
は、単にコレクションの各アイテムを、特定の文字で連結します。
内部を見てみる
/**
* Join all items from the collection using a string. The final items can use a separate glue string.
*
* @param string $glue
* @param string $finalGlue
* @return string
*/
public function join($glue, $finalGlue = '')
{
if ($finalGlue === '') {
return $this->implode($glue);
}
$count = $this->count();
if ($count === 0) {
return '';
}
if ($count === 1) {
return $this->last();
}
$collection = new static($this->items);
$finalItem = $collection->pop();
return $collection->implode($glue).$finalGlue.$finalItem;
}
特筆すべきは、内部的には$collection->implode()
を使っていること。そして26~28行目で、最後の項目だけ後付けしているところくらいでしょうか。
$collection->implode()
を呼び出しているため、第一引数は文字列でなくてもエラーになりませんが、その場合正常に動作しません。
単純なコレクションの文字連結に使う用途になるでしょう。
基本的な使い方
以下のような、単純なコレクションに利用するのが基本です。
$collection = collect(['りんご', 'ばなな', 'みかん']);
$result = $collection->join(', ');
echo $result; // "りんご, ばなな, みかん"
もちろん、シンプルな連想配列なら全く同じ結果になります。
$collection = collect(['AAA'=>'りんご', 1=>'ばなな', 2=>'みかん']);
$result = $collection->join(',');
echo $result; // "りんご, ばなな, みかん"
最後の区切り文字だけ変える
これが唯一の独自機能となります。第2引数として文字列を渡すことで、最後の連結文字を指定できます。
$fruits = collect(['りんご', 'ばなな', 'みかん', 'きうい']);
$list = $fruits->join(', ', ' and ');
echo $list; // "りんご, ばなな, みかん and きうい"
そんなこと普通やる!?
英語のリストの表現においては、アイテムが3つ以上ある場合は最後の区切りとして “and” を入れるのが一般的だからでしょう。
日本人を相手とするWebアプリケーションではあまり使う機会はないかもしれませんね。。
まとめ
最初は、PHP組み込みのimplode()
関数、join()
関数に近いのはどちらだろうと思い調べましたが、どちらとも言いづらい結果になりました。
こんな感じでしょうか。
- 連想配列やオブジェクトのキーを元に結合したいなら
$collection->implode()
一択 - 最後の区切り文字だけ変えたい場合は
$collection->join()
一択 collect(['りんご', 'ばなな'])
等のシンプルなコレクションならどちらでも
個人的には、最後の区切り文字を変えたいシーンが思いつきません。なので$collect->implode()
だけ使っておけば良いのかなぁと思いました。
あなたはどう思われたでしょうか!?
やっぱりちゃんとコードを読むと違いが理解できますね。
コメント