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

# ForwardsCalls trait

> 讲解如何使用 Illuminate\Support\Traits\ForwardsCalls 安全地在 Proxy/Decorator 类中转发方法调用。

## 什么是 ForwardsCalls trait

`Illuminate\Support\Traits\ForwardsCalls` 是用于统一某个对象方法委托的 trait。在 Laravel 本体中，它被 Eloquent、Mail 等「包装对象」使用。

<Info>
  实现位于 `src/Illuminate/Support/Traits/ForwardsCalls.php`。当转发到不存在的方法时，会重新抛出一个包含调用方类名的 `BadMethodCallException`。
</Info>

```mermaid theme={null}
flowchart LR
  A["你的 Proxy 类"] -->|__call| B["forwardCallTo()"]
  B --> C["内部的 Driver / Builder"]
  C --> D["返回结果"]
  D --> A
```

## 核心 API

### `forwardCallTo($object, $method, $parameters)`

将方法原样转发到指定对象。通常从 `__call()` 中调用。

### `forwardDecoratedCallTo($object, $method, $parameters)`

在 Builder 或 Decorator 中希望保持链式调用时使用。若转发目标方法的返回值是「目标对象自身」，则替换为「调用方对象（`$this`）」返回。

### 与 `__call()` / `__callStatic()` 的组合

`ForwardsCalls` 本身不提供魔术方法。你需要在自己的类中实现 `__call()`（必要时还有 `__callStatic()`），并在其中调用 `forwardCallTo` 系列方法。

## 实际使用示例（Laravel 内部）

<Steps>
  <Step title="Facade 提供静态代理">
    `Illuminate\Support\Facades\Facade` 通过 `__callStatic()` 直接委托到 root instance。Facade 是静态调用，因此并没有使用 `ForwardsCalls`，而是直接转发实现。
  </Step>

  <Step title="Eloquent Builder 使用 forwardCallTo">
    `Illuminate\Database\Eloquent\Builder::__call()` 会通过 `forwardCallTo($this->query, ...)` 将未解析的方法转发给内部 Query Builder，最后返回 `$this` 以保持链式调用。
  </Step>

  <Step title="Relation / Mail / Event 使用 forwardDecoratedCallTo">
    `Illuminate\Database\Eloquent\Relations\Relation`、`Illuminate\Mail\Message`、`Illuminate\Events\NullDispatcher` 使用 `forwardDecoratedCallTo`，一边委托到内部对象，一边保持外层 API 的链式调用。
  </Step>
</Steps>

## 基础 Proxy 实现（`forwardCallTo`）

```php theme={null}
use Illuminate\Support\Traits\ForwardsCalls;

class CourierProxy
{
    use ForwardsCalls;

    public function __construct(
        protected CourierDriver $driver
    ) {}

    public function __call(string $method, array $parameters): mixed
    {
        return $this->forwardCallTo($this->driver, $method, $parameters);
    }
}
```

这样，`CourierProxy` 就能透明地对外暴露 `CourierDriver` 的 public API。

## 支持链式调用的 Proxy（`forwardDecoratedCallTo`）

```php theme={null}
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Traits\ForwardsCalls;

class QueryProxy
{
    use ForwardsCalls;

    public function __construct(
        protected Builder $query
    ) {}

    public function __call(string $method, array $parameters): static
    {
        $this->forwardDecoratedCallTo($this->query, $method, $parameters);

        return $this;
    }

    public function get(): \Illuminate\Support\Collection
    {
        return $this->query->get();
    }
}
```

```php theme={null}
$users = (new QueryProxy(User::query()))
    ->where('active', true)
    ->orderByDesc('created_at')
    ->limit(10)
    ->get();
```

<Tip>
  与其手动写 `return $this`，不如利用 `forwardDecoratedCallTo` 的规则——「返回值若为转发目标自身，则替换为 `$this`」——更安全。
</Tip>

## 自动抛出 BadMethodCallException

调用不存在的方法时，`ForwardsCalls` 会用调用方类名重新抛出 `BadMethodCallException`。

```php theme={null}
try {
    (new CourierProxy($driver))->missingMethod();
} catch (\BadMethodCallException $e) {
    // 例：Call to undefined method App\Services\CourierProxy::missingMethod()
    report($e);
}
```

这样用户可以立即定位「是哪个外层 API 出了问题」。

## 与 Macroable 的对比

| 视角   | `Macroable`         | `ForwardsCalls`                              |
| ---- | ------------------- | -------------------------------------------- |
| 目的   | 向既有类新增方法            | 将方法委托给另一个对象                                  |
| 主要入口 | `macro()`、`mixin()` | `forwardCallTo()`、`forwardDecoratedCallTo()` |
| 适用场景 | 扩展 API              | Proxy / Decorator / Adapter                  |

<Warning>
  若目的是「增加方法」却使用 `ForwardsCalls`，那么委托对象未实现的方法会一直失败。若目的在于扩展 API，请选择 `Macroable`。
</Warning>

## 包开发中的应用示例

### 1) 切换 Driver 的 Manager

```php theme={null}
use Illuminate\Support\Traits\ForwardsCalls;

class SmsManager
{
    use ForwardsCalls;

    public function __construct(
        protected SmsDriver $driver
    ) {}

    public function via(string $name): static
    {
        $this->driver = app(SmsDriverFactory::class)->make($name);

        return $this;
    }

    public function __call(string $method, array $parameters): mixed
    {
        return $this->forwardCallTo($this->driver, $method, $parameters);
    }
}
```

### 2) 支持多种后端的 Adapter

可以把 HTTP / Queue / WebSocket 等不同后端统一到同一个 API 后再委托。

### 3) 用于测试的 Spy / Stub

用 Spy 代替真实 Driver 注入，调用通过 `forwardCallTo` 原样转发，从而校验调用次数与参数。

## 相关页面

<Columns cols={2}>
  <Card title="Macroable trait" icon="puzzle-piece" href="/zh/advanced/macroable">
    学习向既有类添加新方法的扩展模式。
  </Card>

  <Card title="Conditionable trait" icon="git-branch" href="/zh/advanced/conditionable">
    学习基于 `when()` / `unless()` 的条件分支链式设计。
  </Card>
</Columns>


## Related topics

- [Macroable trait](/zh/advanced/macroable.md)
- [数据库填充（Seeding）](/zh/seeding.md)
- [Conditionable trait](/zh/advanced/conditionable.md)
- [Dumpable trait](/zh/advanced/dumpable.md)
- [Fluent 类](/zh/advanced/fluent.md)
