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

# 集合的高阶消息

> 集合的高阶消息（Higher-order Messages）的机制与实用用法

## 什么是高阶消息

高阶消息（Higher-order Messages）是以属性访问形式调用集合方法的语法。无需编写回调即可对每个元素调用方法或取属性。

```php theme={null}
// 常规写法
$names = $users->map(fn ($user) => $user->name);

// 使用高阶消息的写法
$names = $users->map->name;
```

`$users->map->name` 表示「取出每个用户的 `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` 实例。这个代理会同时持有集合与方法名。

### 通过 \_\_get 代理属性访问

继续访问属性（`->name`）时，会调用代理的 `__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()`），会触发代理的 `__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());
```

## 支持的方法一览

可作为高阶消息使用的方法在 `$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 模型集合

高阶消息在 Eloquent 集合中尤其方便。

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

// 方法调用：更新每个用户的状态
$users->each->markAsVerified();

// 属性访问：只取名字
$names = $users->map->name;

// 向方法传参
$users->each->sendPasswordReset('zh-CN');

// 发送邮件
$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 关联，合并为单个集合
$posts = $users->flatMap->posts;
```

### 条件判定

```php theme={null}
// 是否全部为管理员
$allAdmins = $users->every->isAdmin();

// 是否至少一个人是活跃的
$hasActive = $users->contains->isActive();

// 是否全部持有指定套餐
$allPro = $users->every->hasPlan('pro');
```

### 字符串集合

值为字符串的集合也可以使用。代理的 `__call()` 在字符串时以静态方法方式调用。

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

// 对字符串集合，使用闭包更为明确
$upper = $names->map(fn ($name) => strtoupper($name));
// ['ALICE', 'BOB', 'CHARLIE']
```

<Info>
  字符串集合的高阶消息要求方法在字符串类型上是静态定义的。通常在 Eloquent 模型或自定义类的集合中使用更自然。
</Info>

## 与常规闭包的对比

高阶消息简洁，但并不适用于所有场景。

```php theme={null}
// 高阶消息：适合简单的属性获取和方法调用
$emails = $users->map->email;
$users->each->sendWelcomeMail();
$admins = $users->filter->isAdmin();

// 闭包：适合复杂逻辑或需要传参的情况
$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>
  「从每个元素取相同属性」「对每个元素调用相同方法」时，高阶消息可读性更佳。条件复杂或参数需要动态传入时，请使用闭包。
</Tip>

## 在自定义类中使用高阶消息

若希望通过 `EnumeratesValues` trait 使用 `HigherOrderCollectionProxy` 机制，可以调用 `Collection::proxy()` 将方法加入代理列表。

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

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

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // 将自定义宏加入代理列表
        Collection::macro('active', function () {
            return $this->filter(fn ($item) => $item->isActive());
        });

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

```php theme={null}
// 注册后可作为高阶消息使用
$activeUsers = $users->active;
```

## 下一步

<Card title="Conditionable trait" icon="git-branch" href="/zh/advanced/conditionable">
  学习 `when()` / `unless()` 的内部实现，以及在 QueryBuilder 与自定义类中的应用方法。
</Card>


## Related topics

- [Collection Deep Dive](/zh/advanced/collection-deep-dive.md)
- [Conditionable trait](/zh/advanced/conditionable.md)
- [集合](/zh/collections.md)
- [Eloquent 集合](/zh/eloquent-collections.md)
- [辅助函数](/zh/helpers.md)
