> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Collection 的 Higher Order Messages

> Collection 之 Higher-order Messages 的機制與實務用法。

## 什麼是 Higher Order Messages

Higher-order Messages 是以屬性存取形式呼叫 Collection 方法的語法。無需撰寫 callback，就能對每個元素呼叫方法或取得屬性。

```php theme={null}
// 一般的寫法
$names = $users->map(fn ($user) => $user->name);

// 使用 Higher Order Messages 的寫法
$names = $users->map->name;
```

`$users->map->name` 的意義是「取出每個 user 的 `name` 屬性再 `map`」。

## 機制 — HigherOrderCollectionProxy

若像 `$collection->map` 一樣以屬性存取，`EnumeratesValues` trait 的 `__get()` 會被呼叫。

```php theme={null}
// src/Illuminate/Collections/Traits/EnumeratesValues.php
public function __get($key)
{
    if (! in_array($key, static::$proxies)) {
        throw new Exception("Property [{$key}] does not exist on this collection instance.");
    }

    return new HigherOrderCollectionProxy($this, $key);
}
```

若名稱包含於 `$proxies` 中，會回傳 `HigherOrderCollectionProxy` 實例。此 proxy 保存 collection 與方法名稱兩者。

### 透過 \_\_get 的屬性存取代理

接著若存取屬性（`->name`），會呼叫 proxy 的 `__get()`。

```php theme={null}
// src/Illuminate/Collections/HigherOrderCollectionProxy.php
public function __get($key)
{
    return $this->collection->{$this->method}(function ($value) use ($key) {
        return is_array($value) ? $value[$key] : $value->{$key};
    });
}
```

也就是說，`$users->map->name` 等同於：

```php theme={null}
$users->map(fn ($user) => $user->name);
```

### 透過 \_\_call 的方法呼叫代理

接著若呼叫方法（`->activate()`），會呼叫 proxy 的 `__call()`。

```php theme={null}
public function __call($method, $parameters)
{
    return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
        return is_string($value)
            ? $value::{$method}(...$parameters)
            : $value->{$method}(...$parameters);
    });
}
```

`$users->each->activate()` 等同於：

```php theme={null}
$users->each(fn ($user) => $user->activate());
```

## 支援的方法列表

可作為 Higher Order Messages 使用的方法定義於 `$proxies` 陣列。

```php theme={null}
protected static $proxies = [
    'average', 'avg',
    'contains', 'doesntContain', 'some',
    'each', 'every',
    'filter', 'reject',
    'first', 'last',
    'flatMap', 'map',
    'groupBy', 'keyBy',
    'hasMany', 'hasSole',
    'max', 'min', 'sum', 'percentage',
    'partition',
    'skipUntil', 'skipWhile',
    'sortBy', 'sortByDesc',
    'takeUntil', 'takeWhile',
    'unique',
    'unless', 'until', 'when',
];
```

要新增自訂方法可使用 `Collection::proxy()`。

```php theme={null}
Collection::proxy('myCustomMethod');
```

## 實務使用情境

### Eloquent Model 的 Collection

Higher Order Messages 在 Eloquent 的 Collection 中特別便利。

```php theme={null}
$users = User::with('posts')->get();

// 方法呼叫：更新每個使用者的狀態
$users->each->markAsVerified();

// 屬性存取：僅取出名稱
$names = $users->map->name;

// 傳入引數的方法
$users->each->sendPasswordReset('zh-TW');

// 寄送 mail
$users->each->notify(new WelcomeNotification);
```

### 篩選

```php theme={null}
$activeUsers = $users->filter->isActive();

// 只保留 isActive() 為 true 的使用者
// 等同於 $users->filter(fn ($user) => $user->isActive())

$inactiveUsers = $users->reject->isActive();
```

### 彙總

```php theme={null}
// 對每個使用者的 post_count 屬性求和
$totalPosts = $users->sum->post_count;

// 對每個使用者的 score 取最大值
$highestScore = $users->max->score;

// 對每個使用者的 age 取平均
$averageAge = $users->avg->age;
```

### 分組、排序

```php theme={null}
// 依 role 屬性分組
$grouped = $users->groupBy->role;

// 依 name 屬性升冪排序
$sorted = $users->sortBy->name;

// 依 created_at 屬性降冪排序
$sorted = $users->sortByDesc->created_at;
```

### 以 flatMap 展開巢狀

```php theme={null}
// 展開每個使用者的 posts 關聯彙整為單一 collection
$posts = $users->flatMap->posts;
```

### 條件確認

```php theme={null}
// 確認是否全員為管理員
$allAdmins = $users->every->isAdmin();

// 確認是否有任一位為 active
$hasActive = $users->contains->isActive();

// 確認是否全員屬於特定 plan
$allPro = $users->every->hasPlan('pro');
```

### 字串 Collection

值為字串的 collection 也可以使用。proxy 的 `__call()` 於字串時會以靜態方法呼叫。

```php theme={null}
$names = collect(['alice', 'bob', 'charlie']);

// 對字串 collection 使用 closure 較為明確
$upper = $names->map(fn ($name) => strtoupper($name));
// ['ALICE', 'BOB', 'CHARLIE']
```

<Info>
  對字串 collection 使用 Higher Order Messages 時，方法必須以靜態方式定義於字串型別上。通常較自然的用法是搭配 Eloquent 模型或自訂類別的 collection。
</Info>

## 與一般 closure 的比較

Higher Order Messages 雖然簡潔，但並非所有情境皆適用。

```php theme={null}
// Higher Order Messages：適合單純的屬性取得、方法呼叫
$emails = $users->map->email;
$users->each->sendWelcomeMail();
$admins = $users->filter->isAdmin();

// Closure：適合需要複雜邏輯或引數的情境
$formatted = $users->map(function ($user) {
    return "{$user->name} <{$user->email}>";
});

$filtered = $users->filter(function ($user) use ($minAge, $role) {
    return $user->age >= $minAge && $user->role === $role;
});
```

<Tip>
  「取出每個元素的相同屬性」、「呼叫每個元素的相同方法」時，使用 Higher Order Messages 會更易讀。條件複雜或引數需動態時則使用 closure。
</Tip>

## 於自訂類別使用 Higher Order Messages

要透過 `EnumeratesValues` trait 利用 `HigherOrderCollectionProxy` 機制，可呼叫 `Collection::proxy()` 將方法加入 proxy 對象。

```php theme={null}
namespace App\Providers;

use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // 將自訂 macro 加入 proxy 對象
        Collection::macro('active', function () {
            return $this->filter(fn ($item) => $item->isActive());
        });

        Collection::proxy('active');
    }
}
```

```php theme={null}
// 註冊後可作為 Higher Order Messages 使用
$activeUsers = $users->active;
```

## 下一步

<Card title="Conditionable trait" icon="git-branch" href="/zh-TW/advanced/conditionable">
  學習 `when()` / `unless()` 的內部實作，以及套用於 QueryBuilder 及自訂類別的方法。
</Card>


## Related topics

- [Collection Deep Dive](/zh-TW/advanced/collection-deep-dive.md)
- [Conditionable trait](/zh-TW/advanced/conditionable.md)
- [进阶主题](/zh-CN/advanced/index.md)
- [從 Laravel 12 升級到 13 指南](/zh-TW/blog/upgrade-12-to-13.md)
- [tap() helper 與 Tappable trait](/zh-TW/advanced/tap.md)
