Saltar al contenido principal

Qué es el trait ForwardsCalls

Illuminate\Support\Traits\ForwardsCalls es un trait que unifica la delegación de métodos hacia otro objeto. En el propio Laravel se usa en objetos “envoltorio” como los de Eloquent y Mail.
La implementación está en src/Illuminate/Support/Traits/ForwardsCalls.php. Cuando se reenvía un método inexistente, relanza el error como una BadMethodCallException que incluye el nombre de la clase que hizo la llamada.

API principal

forwardCallTo($object, $method, $parameters)

Reenvía la llamada al objeto indicado tal cual. Normalmente se invoca desde __call().

forwardDecoratedCallTo($object, $method, $parameters)

Se usa en builders o decorators cuando quieres mantener la cadena. Si el valor devuelto por el método reenviado es el «propio objeto destino», lo sustituye por el «objeto de origen ($this)» y lo devuelve.

Combinación con __call() / __callStatic()

ForwardsCalls no aporta los métodos mágicos. Tú debes implementar __call() (y si hace falta __callStatic()) en tu clase y llamar dentro a los métodos forwardCallTo*.

Ejemplos reales (interior de Laravel)

1

Los Facades ofrecen un static proxy

Illuminate\Support\Facades\Facade delega directamente en la root instance mediante __callStatic(). Como el Facade se invoca de forma estática, no usa ForwardsCalls, sino un reenvío directo.
2

Eloquent Builder usa forwardCallTo

Illuminate\Database\Eloquent\Builder::__call() pasa los métodos no resueltos al query builder interno con forwardCallTo($this->query, ...) y devuelve $this al final para mantener la fluent chain.
3

Relation / Mail / Event usan forwardDecoratedCallTo

En Illuminate\Database\Eloquent\Relations\Relation, Illuminate\Mail\Message y Illuminate\Events\NullDispatcher se emplea forwardDecoratedCallTo, delegando en el objeto interno pero preservando la cadena de la API exterior.

Implementación básica de un proxy (forwardCallTo)

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);
    }
}
Con esta forma, CourierProxy expone de forma transparente toda la API pública de CourierDriver.

Proxy con method chaining (forwardDecoratedCallTo)

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();
    }
}
$users = (new QueryProxy(User::query()))
    ->where('active', true)
    ->orderByDesc('created_at')
    ->limit(10)
    ->get();
Es más seguro apoyarse en la regla de forwardDecoratedCallTo de «si el retorno es el objeto destino, se sustituye por $this» que escribir manualmente return $this.

Lanzamiento automático de BadMethodCallException

Cuando llamas a un método inexistente, ForwardsCalls relanza una BadMethodCallException usando el nombre de la clase que originó la llamada.
try {
    (new CourierProxy($driver))->missingMethod();
} catch (\BadMethodCallException $e) {
    // p. ej.: Call to undefined method App\Services\CourierProxy::missingMethod()
    report($e);
}
Así, los usuarios pueden localizar de inmediato en qué API externa se produjo el fallo.

Comparación con Macroable

AspectoMacroableForwardsCalls
ObjetivoAñadir métodos nuevos a una clase existente.Delegar métodos a otro objeto.
Entradas principalesmacro(), mixin()forwardCallTo(), forwardDecoratedCallTo()
Casos adecuadosExtensión de API.Proxy / Decorator / Adapter.
Si lo que quieres es «añadir métodos» y usas ForwardsCalls, cualquier método que no exista en el objeto destino fallará siempre. Si tu objetivo es extender la API, elige Macroable.

Ejemplos de uso en el desarrollo de paquetes

1) Manager que cambia de driver

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 con varios backends

Puedes unificar backends distintos (HTTP / Queue / WebSocket) tras una única API común y delegar en cada uno.

3) Spy / Stub para tests

En lugar del driver real, inyectas un Spy y dejas pasar las llamadas con forwardCallTo para verificar cuántas veces se llamó y con qué argumentos.

Páginas relacionadas

Trait Macroable

Aprende el patrón para extender clases existentes con nuevos métodos.

Trait Conditionable

Aprende a diseñar cadenas con bifurcaciones condicionales mediante when() / unless().
Última modificación el 13 de julio de 2026