Laravelのコレクションをできるだけ使用したメモ

## はじめに
前回は、LaravelのEloquentで取得したデータをPHPで配列を再設定し直してビュー用にセットしました。
今回はLaravelで取得したデータを、Eloquentのコレクションを使用し対応するように再加工します。

## 前回
ビュー用のデータ再設定をLaravelのEloquentのコレクション機能をせず、PHPで対応しています。
```php
// ユーザーがフォローしている人とフォローされている人のデータを取得
$following = UserRelation::where('user_id', $userId)->get(['target_user_id', 'is_following', 'is_blocking']);
$followers = UserRelation::where('target_user_id', $userId)->get(['user_id', 'is_following', 'is_blocking']);

$relations = [];

// フォローしている人たちのデータを処理
foreach ($following as $follow) {
    $relations[$follow->target_user_id] = [
        'follow' => $follow->is_following,
        'follower' => false,
        'mutual' => false,
        'blocking' => $follow->is_blocking,
        'blocked_by' => false
    ];
}

// フォロワーのデータを処理
foreach ($followers as $follower) {
    if (!isset($relations[$follower->user_id])) {
        $relations[$follower->user_id] = [
            'follow' => false,
            'follower' => false,
            'mutual' => false,
            'blocking' => false,
            'blocked_by' => false
        ];
    }
    $relations[$follower->user_id]['follower'] = $follower->is_following && !$follower->is_blocking;
    $relations[$follower->user_id]['follow'] = $relations[$follower->user_id]['follow'] ?? false;
    $relations[$follower->user_id]['mutual'] = $relations[$follower->user_id]['follow'] && $follower->is_following;
    $relations[$follower->user_id]['blocked_by'] = $follower->is_blocking && !$follower->is_following;
}

// ソートしてビューに返す
ksort($relations);
return view('user_relations.index', compact('relations'));
```
## 修正(今回)
今回はLaravelで取得したデータを、Eloquentのコレクションを使用し対応するように再加工します。
```php
// ユーザーがフォローしている人たちのデータを取得し、加工
$following = UserRelation::where('user_id', $userId)
        ->get(['target_user_id', 'is_following', 'is_blocking'])
        ->keyBy('target_user_id')
        ->map(function ($item) {
            return [
                'follow' => $item->is_following,
                'follower' => false,
                'mutual' => false,
                'blocking' => $item->is_blocking,
                'blocked_by' => false  // 初期状態では不明
            ];
        });

// ユーザーをフォローしている人たちのデータを取得し、加工
$followers = UserRelation::where('target_user_id', $userId)
        ->get(['user_id', 'is_following', 'is_blocking'])
        ->each(function ($item) use ($following) {
            // 既存のデータの更新、または新しいデータの追加
            $relation = $following->get($item->user_id, [
                'follow' => false,
                'follower' => $item->is_following && !$item->is_blocking,
                'mutual' => false,
                'blocking' => false,
                'blocked_by' => false
            ]);

            $relation['follower'] = $item->is_following && !$item->is_blocking;
            $relation['mutual'] = $relation['follow'] && $item->is_following;
            $relation['blocked_by'] = $item->is_blocking && !$item->is_following;

            $following->put($item->user_id, $relation);
        });

// 配列をキー（ID）で昇順にソート
$following = $following->sortKeys();

return view('user_relations.index', ['relations' => $following]);
```
- 主なコレクション機能を記載しておきます。

### `use` キーワードについて

PHP の `use` キーワードは、主にクロージャ（無名関数）の中で外部の変数を使うために使用されます。クロージャはデフォルトでそのスコープ内の変数のみにアクセス可能ですが、`use` キーワードを使うことで、クロージャが定義された親スコープの変数をクロージャ内で利用できるようになります。

```php
$followers = UserRelation::where('target_user_id', $userId)
    ->get(['user_id', 'is_following', 'is_blocking'])
    ->each(function ($item) use ($following) {
        // $following は外部の変数で、use キーワードを使用してクロージャ内で利用可能
        $relation = $following->get($item->user_id, [
            // 初期データ
        ]);
        // ここで $relation を更新
    });
```
上記の例では、`$following` コレクションをクロージャ内で使用しています。この `$following` は、`$followers->each()` の外で定義されているため、クロージャの中で直接アクセスすることはできません。`use` を使って `$following` をクロージャのスコープに含めることで、そのデータにアクセスして操作を行うことができます。

### `put` メソッドについて

Laravel のコレクションにおいて、`put` メソッドは指定されたキーに値を設定するために使用されます。もし指定されたキーがすでに存在する場合は、そのキーの値を新しい値で上書きします。存在しない場合は、新しいキーと値のペアをコレクションに追加します。

```php
$following->put($item->user_id, $relation);
```
このコード行では、`$following` コレクションの `$item->user_id` キーに `$relation` を設定しています。この操作により、`$following` コレクションの該当するユーザーIDのデータが更新されるか、新しく追加されます。

これらのメソッドとキーワードを適切に使用することで、Laravel の強力なコレクション操作機能を活用し、効率的かつ表現豊かなデータ処理が可能になります。

もちろんです。Laravelのコレクションメソッドである `keyBy` と `each` について説明します。

### `keyBy` メソッドについて

`keyBy` メソッドは、コレクション内の各アイテムを指定されたキーに基づいて再キー設定します。これにより、特定の属性（例えばユーザーID）に基づいて要素に簡単にアクセスできるようになります。

```php
$following = UserRelation::where('user_id', $userId)
                ->get(['target_user_id', 'is_following', 'is_blocking'])
                ->keyBy('target_user_id');
```
この例では、`UserRelation` モデルから取得したデータを `target_user_id` でインデックス化しています。`keyBy` の結果、コレクションの各要素はその `target_user_id` をキーとしてアクセス可能になります。これにより、後続の処理で特定の `target_user_id` のデータを直接参照したり、更新したりするのが容易になります。

### `each` メソッドについて

`each` メソッドは、コレクションの各要素に対してループ処理を行い、指定されたクロージャ（無名関数）を適用します。`each` メソッドは、一般的な `foreach` ループと似ていますが、コレクションのメソッドチェーンの一部として使用できる点が特徴です。

```php
$followers = UserRelation::where('target_user_id', $userId)
                ->get(['user_id', 'is_following', 'is_blocking'])
                ->each(function ($item) use ($following) {
                    // $item に対する操作
                });
```
このコードでは、`$followers` コレクションの各要素に対して関数を適用しています。ここでの `$item` は `UserRelation` モデルのインスタンスであり、`each` 内のクロージャで定義された処理を行います。この処理は、条件に基づいてデータを更新する、ログを出力するなど、任意の操作が可能です。

`each` メソッドは元のコレクションを変更せずに新しいコレクションを返す点で、他のメソッド（例えば `map`）とは異なります。そのため、データの副作用を避けつつ、各要素に何らかの操作を行う場合に適しています。

## コメント(考察)
今回は、前回よりもわかりにくくなってしまったと思います、検討の余地はありそうです。
ただLaravel のコレクション機能は非常に強力で柔軟ですが、場合によっては従来の PHP 配列を使ったアプローチの方が直感的で理解しやすい場合もあります。これは特に、特定の処理においてコレクションメソッドが複雑すぎるか、またはコレクションのメソッドチェーンが長くなりすぎて読みづらくなる場合にあるかと思います。

### PHP 配列を使用する場合の利点
1. **直感的**: 従来の PHP 配列とその操作は多くの PHP 開発者にとってより馴染み深く、理解しやすいです。
2. **シンプル**: コレクションが提供する多くのメソッドは便利ですが、シンプルな配列操作で十分な場合も多く、オーバーエンジニアリングを避けることができます。
3. **パフォーマンス**: 非常に大規模なデータセットを扱う場合、コレクションメソッドは内部的に多くの処理を行うため、生の配列操作の方がパフォーマンスが優れていることがあります。

### Laravel コレクションを使用する場合の利点
1. **メソッドチェーン**: データの変換やフィルタリングを行う際に、連続したメソッド呼び出しを利用でき、コードが簡潔になります。
2. **一貫性**: Eloquent モデルからデータを取得する場合、デフォルトでコレクションが返されるため、Laravel アプリケーション全体で一貫したデータ操作が可能です。
3. **高度な機能**: コレクションは多くの高度な機能を提供し、複雑なデータ操作を簡単に実行できます。

どちらのアプローチが最適かは、プロジェクトの要件、チームの経験、および具体的なタスクによって異なります。
もしよりシンプルでパフォーマンスに優れたアプローチを好む場合は、PHP の配列操作を選択するのが良いかもしれません。一方で、コードの一貫性やメソッドチェーンの利便性を重視する場合は、Laravel のコレクションを活用する方が適しているでしょう。


## 参考記事
ex ver8.x)
https://readouble.com/laravel/8.x/ja/collections.html