> ## 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 等「wrapper 物件」皆會使用。

<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
```

## Core API

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

將方法原封不動轉發至指定物件。典型上會由 `__call()` 呼叫。

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

用於在 Builder 或 Decorator 中維持鏈式呼叫。若轉發目標方法的回傳值為「轉發目標物件本身」，則會替換為「呼叫端物件（`$this`）」回傳。

### 與 `__call()` / `__callStatic()` 的組合

`ForwardsCalls` 本身不提供魔術方法。應由您的類別實作 `__call()`（必要時 `__callStatic()`），並在其中呼叫 `forwardCallTo` 系列方法。

## 實際使用範例（Laravel 內部）

<Steps>
  <Step title="Facade 提供 static proxy">
    `Illuminate\Support\Facades\Facade` 以 `__callStatic()` 直接委派給 root instance。Facade 由於是 static 呼叫，實作上是直接轉發而非使用 `ForwardsCalls`。
  </Step>

  <Step title="Eloquent Builder 使用 forwardCallTo">
    `Illuminate\Database\Eloquent\Builder::__call()` 將未解析的方法以 `forwardCallTo($this->query, ...)` 傳給內部 Query Builder，最後回傳 `$this` 以維持 fluent chain。
  </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-TW/advanced/macroable">
    學習為既有類別加入新方法的擴充模式。
  </Card>

  <Card title="Conditionable trait" icon="git-branch" href="/zh-TW/advanced/conditionable">
    學習透過 `when()` / `unless()` 進行條件分支鏈的設計。
  </Card>
</Columns>


## Related topics

- [Macroable trait](/zh-TW/advanced/macroable.md)
- [資料庫 Seeding](/zh-TW/seeding.md)
- [Conditionable trait](/zh-TW/advanced/conditionable.md)
- [Dumpable trait](/zh-TW/advanced/dumpable.md)
- [Fluent 類別](/zh-TW/advanced/fluent.md)
