Vai al contenuto principale

Cos’è il trait ForwardsCalls

Illuminate\Support\Traits\ForwardsCalls è un trait che centralizza la delega dei metodi verso un altro oggetto. Nel core di Laravel è utilizzato dai “wrapper” come Eloquent o Mail.
L’implementazione si trova in src/Illuminate/Support/Traits/ForwardsCalls.php. Se inoltri un metodo inesistente, rilancia una BadMethodCallException formattata con il nome della classe chiamante.

API principali

forwardCallTo($object, $method, $parameters)

Inoltra un metodo così com’è all’oggetto specificato. Tipicamente viene chiamato da __call().

forwardDecoratedCallTo($object, $method, $parameters)

Usato in builder e decorator per mantenere la catena. Se il metodo di destinazione restituisce “l’oggetto di destinazione stesso”, il valore viene sostituito con “l’oggetto chiamante ($this)”.

Combinazione con __call() / __callStatic()

ForwardsCalls non fornisce metodi magic. Sei tu ad implementare __call() (e se serve __callStatic()) nella tua classe e da lì chiami i metodi forwardCallTo*.

Esempi d’uso reali (interno a Laravel)

1

I Facade forniscono uno static proxy

Illuminate\Support\Facades\Facade delega direttamente all’istanza root con __callStatic(). Poiché i facade sono chiamate statiche, l’implementazione è un forwarding diretto e non usa ForwardsCalls.
2

L'Eloquent Builder usa forwardCallTo

Illuminate\Database\Eloquent\Builder::__call() passa i metodi non risolti al Query Builder interno con forwardCallTo($this->query, ...) e infine restituisce $this per mantenere la fluent chain.
3

Relation / Mail / Event usano forwardDecoratedCallTo

Illuminate\Database\Eloquent\Relations\Relation, Illuminate\Mail\Message e Illuminate\Events\NullDispatcher usano forwardDecoratedCallTo per delegare all’oggetto interno preservando la catena dell’API esterna.

Implementazione base di 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);
    }
}
In questa forma, CourierProxy espone in modo trasparente l’API pubblica di CourierDriver.

Proxy compatibile 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();
È più sicuro sfruttare la regola di forwardDecoratedCallTo (“se il valore di ritorno è l’oggetto di destinazione, lo sostituisce con $this”) piuttosto che scrivere manualmente return $this.

Lancio automatico di BadMethodCallException

Se chiami un metodo inesistente, ForwardsCalls rilancia una BadMethodCallException usando il nome della classe chiamante.
try {
    (new CourierProxy($driver))->missingMethod();
} catch (\BadMethodCallException $e) {
    // esempio: Call to undefined method App\Services\CourierProxy::missingMethod()
    report($e);
}
In questo modo l’utente identifica subito “quale API esterna è fallita”.

Confronto con Macroable

AspettoMacroableForwardsCalls
ScopoAggiungere nuovi metodi a una classe esistenteDelegare metodi a un altro oggetto
Ingressi principalimacro(), mixin()forwardCallTo(), forwardDecoratedCallTo()
Casi adattiEstensione di APIProxy / Decorator / Adapter
Se vuoi “aggiungere metodi”, usare ForwardsCalls fallirà sempre per i metodi non presenti sul target. Se il tuo obiettivo è estendere un’API, scegli Macroable.

Esempi d’uso nello sviluppo di pacchetti

1) Manager che cambia 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 più backend

Backend diversi come HTTP / Queue / WebSocket possono essere delegati a un’unica API unificata.

3) Spy / Stub per i test

Al posto del driver reale puoi iniettare uno spy, lasciando che le chiamate passino attraverso forwardCallTo e verificando numero di chiamate e argomenti.

Pagine correlate

Trait Macroable

Impara il pattern di estensione per aggiungere metodi a classi esistenti.

Trait Conditionable

Impara a progettare catene condizionali con when() / unless().
Ultima modifica il 13 luglio 2026