🦌

Laravel Collection入門

に公開

前書き

Laravelで開発しているとCollectionというワードをよく目にする。
本記事では、なぜPHPの標準の配列ではなくCollectionを使うのか、Collectionを使うことでどのような利点があるのかを解説する。
Illuminate\Support\Collectionに焦点を当て、その魅力を具体的なコード例とともに紹介する。

Collectionとは

PHPの標準配列との最大の違いは、Collectionがオブジェクトであり、メソッドチェーンによる連続した操作が可能な点である。
実際のコード例を見る方が、よりその違いを明確に理解できるだろう。

例として、アクティブユーザーのログイン履歴から総ログイン秒数を算出するコードを見てみよう。

$loginUsers = collect([
    ['user_id' => 1, 'status' => 'active', 'login_at' => '2025-01-01 09:00:00', 'logout_at' => '2025-01-01 10:15:00'],
    ['user_id' => 2, 'status' => 'inactive', 'login_at' => '2025-01-01 09:30:00', 'logout_at' => '2025-01-01 10:00:00'],
    ['user_id' => 3, 'status' => 'active', 'login_at' => '2025-01-01 10:00:00', 'logout_at' => '2025-01-01 12:00:00'],
]);

$totalSeconds = $loginUsers
    // activeユーザーのみに絞る
    ->filter(fn($loginUser) => $loginUser['status'] === 'active')
    // ログイン時間を秒数に変換
    ->map(function ($loginUser) {
        $login = strtotime($loginUser['login_at']);
        $logout = strtotime($loginUser['logout_at']);
        return $logout - $login;
    })
    // 合計秒数を集計
    ->reduce(fn($carry, $seconds) => $carry + $seconds, 0);

echo "総ログイン秒数: {$totalSeconds}" . PHP_EOL;

このコードは配列処理をデータを主軸として、処理を連鎖させることで、処理の流れが明確になる利点がある。
このようなメソッドチェーンによる処理は、JavaScriptやRust、Java (Stream API)といった他のプログラミング言語でも採用されている手法である。

標準配列との比較

Collectionの利点は、PHPの標準配列で実装した同等のコードと比較するとより明確になる。
最も重要な違いは、Collection宣言的なアプローチを可能にする点である一方、標準配列の処理は主に逐次的なアプローチに依存している点である。

宣言的アプローチでは「何を」したいかを記述し、その方法の詳細は抽象化される。
対照的に、逐次的アプローチでは「どのように」行うかの詳細を明示的に指定する必要がある。
以下の例でその違いを確認できる。

$loginUsers = [
    ['user_id' => 1, 'status' => 'active', 'login_at' => '2025-01-01 09:00:00', 'logout_at' => '2025-01-01 10:15:00'],
    ['user_id' => 2, 'status' => 'inactive', 'login_at' => '2025-01-01 09:30:00', 'logout_at' => '2025-01-01 10:00:00'],
    ['user_id' => 3, 'status' => 'active', 'login_at' => '2025-01-01 10:00:00', 'logout_at' => '2025-01-01 12:00:00'],
];
// foreachで同じ処理を行う
$totalSeconds = 0;
foreach ($loginUsers as $loginUser) {
    if ($loginUser['status'] === 'active') {
        $login = strtotime($loginUser['login_at']);
        $logout = strtotime($loginUser['logout_at']);
        $totalSeconds += $logout - $login;
    }
}

echo "総ログイン秒数: {$totalSeconds}" . PHP_EOL;

あるいは、PHPの配列操作関数を使用した実装も可能であるが、これは完全に宣言的とは言えない中間的なアプローチである。

$loginUsers = [
    ['user_id' => 1, 'status' => 'active', 'login_at' => '2025-01-01 09:00:00', 'logout_at' => '2025-01-01 10:15:00'],
    ['user_id' => 2, 'status' => 'inactive', 'login_at' => '2025-01-01 09:30:00', 'logout_at' => '2025-01-01 10:00:00'],
    ['user_id' => 3, 'status' => 'active', 'login_at' => '2025-01-01 10:00:00', 'logout_at' => '2025-01-01 12:00:00'],
];
$activeUsers = array_filter($loginUsers, fn($loginUser) => $loginUser['status'] === 'active');
$totalSeconds = array_reduce($activeUsers, function ($carry, $loginUser) {
    $login = strtotime($loginUser['login_at']);
    $logout = strtotime($loginUser['logout_at']);
    return $carry + ($logout - $login);
}, 0);
echo "総ログイン秒数: {$totalSeconds}" . PHP_EOL;

これらのコードを比較すると、Collectionを利用した宣言的なアプローチでは処理の流れと意図を明確に表現することに重点を置いていることがわかる。
各ステップが何を行っているかが視覚的に把握しやすく、コードの可読性が高い。
また、データの処理方法ではなく、どのような結果を得たいかに焦点を当てることができる。

宣言的なプログラミングスタイルは、コードの保守性と再利用性を高める傾向にある。
処理の意図が明確になるため、後から見返した際にも理解しやすく、修正や拡張が容易になる。

パフォーマンスの考慮

Collectionには先ほどのようなメリットがある一方で、パフォーマンスの側面では一部のケースでPHPの標準配列の方が優れている場合がある。
私が調べた範囲では、filtermapeachreduceは標準のPHP関数を使用すると高速化できることがある。
searchsortspliceCollectionのメソッドを使用することで高速化できることがある。
そのため、パフォーマンスが特に重要な場面では、PHPの標準配列を使用することも選択肢として考慮する必要がある。

まとめ

Collectionは、PHPの標準配列に比べて宣言的なアプローチを提供し、コードの可読性と保守性を向上させる強力なツールである。
私の主張としては、実務上のアプローチとしては、まずCollectionの採用を検討し、パフォーマンスが実際に問題となる場合に初めてPHPの標準配列への移行を検討すべきだと考える。

あとがき

私はLaravelを使う以前は独自のPHPフレームワークを使用しており、PHPの標準配列に慣れ親しんでいた。
そのため、LaravelのCollectionを初めて使用した際は、その利点を十分に理解できなかった。
Collectionのメソッドをわざわざ調べて使用する必要性を感じず、foreachで十分だと考えていた。

しかし、関数型言語やメソッドチェーンパターンに慣れるにつれ、Collectionの真価を理解するようになった。
処理の各ステップが明確に区分され、コードの可読性が向上し、修正箇所の特定が容易になるという利点が明らかになった。

ただし、Collectionが提供する豊富なメソッドの用途を十分に理解していなければ、これらの恩恵を最大限に享受することは難しい。
そのため、無条件に推奨することはできないが、Laravelを使用する開発者であれば、Collectionの活用を積極的に検討する価値があると強く感じている。

参考

GitHubで編集を提案

Discussion