Vai al contenuto principale

Cos’è tap()

tap() è un helper che permette di “usare un valore e restituirlo così com’è”. Lo si utilizza quando si vuole intercalare un side effect. L’implementazione in Illuminate\Support\helpers.php è semplice.
function tap($value, $callback = null)
{
    if (is_null($callback)) {
        return new HigherOrderTapProxy($value);
    }

    $callback($value);

    return $value;
}
tap() ignora il valore di ritorno del callback. Il valore restituito è sempre il valore originale.

Uso di base

L’uso più elementare consiste nel ricevere un valore, elaborarlo e restituirlo così com’è.
use App\Models\User;

$user = tap(User::query()->latest()->firstOrFail(), function (User $model) {
    $model->update(['last_seen_at' => now()]);
});

// indipendentemente dal valore di ritorno di update() viene restituito $user
Omettendo il callback ottieni un HigherOrderTapProxy, quindi puoi concatenare direttamente le chiamate di metodo.
$updatedUser = tap($user)->update([
    'name' => $name,
    'email' => $email,
]);

// non viene restituito il valore di ritorno di update(), ma l'istanza User originale

Casi d’uso frequenti

Inserire output di debug

$result = tap($query->get(), function ($users) {
    logger()->debug('Fetched users', ['count' => $users->count()]);
});

Inserire log o eventi all’interno di una catena

$order = tap(Order::create($payload), function (Order $order) {
    event(new OrderCreated($order));
    logger()->info('Order created', ['id' => $order->id]);
});

Eseguire solo un side effect senza cambiare il valore restituito

$response = tap($service->handle($request), function ($response) {
    \Illuminate\Support\Facades\Cache::increment('service_handle_success_total');
});
Se vuoi trasformare il valore di ritorno, usa with() o una normale assegnazione a una variabile. Limitando tap() ai side effect il codice resta più leggibile.

Cos’è il trait Tappable

Usando Illuminate\Support\Traits\Tappable puoi aggiungere alla tua classe il metodo tap().
trait Tappable
{
    public function tap($callback = null)
    {
        return tap($this, $callback);
    }
}
In pratica, offre solo la versione a livello di istanza di tap().
use Illuminate\Support\Traits\Tappable;

class ReportBuilder
{
    use Tappable;
}

$builder = new ReportBuilder();

$builder = $builder->tap(function (ReportBuilder $instance) {
    logger()->debug('builder initialized');
});

Utilizzo nello sviluppo di pacchetti

Tappable è utile quando vuoi inserire un side effect all’interno di un’API fluent. Combinandolo con Macroable e Conditionable puoi creare builder estendibili in perfetto stile Laravel.
1

Creare una classe fluent

use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Support\Traits\Tappable;

class QueryPresetBuilder
{
    use Macroable;
    use Conditionable;
    use Tappable;

    protected array $filters = [];

    public function where(string $key, mixed $value): static
    {
        $this->filters[$key] = $value;

        return $this;
    }

    public function toArray(): array
    {
        return $this->filters;
    }
}
2

Combinare Macroable, Conditionable e Tappable

QueryPresetBuilder::macro('forActiveUsers', function () {
    /** @var QueryPresetBuilder $this */
    return $this->where('active', true);
});

$filters = (new QueryPresetBuilder)
    ->forActiveUsers()
    ->when($request->filled('role'), fn ($builder) => $builder->where('role', $request->role))
    ->tap(fn ($builder) => logger()->debug('current filters', $builder->toArray()))
    ->toArray();
Pagine correlate:

Esempi d’uso nel core di Laravel

Anche il core di Laravel utilizza tap() / Tappable.
  • Illuminate\Routing\Router combina Macroable e Tappable
  • In Router::respondWithRoute() viene usato tap($route)->bind(...) senza callback, restituendo la $route originale anche dopo l’esecuzione di bind()
  • In Router::prepareResponse(), dopo la conversione della risposta, viene emesso un evento con tap(..., fn (...) => event(...))
  • In Illuminate\Testing\Fluent\Concerns\Has il pattern ->tap(...)->first(...)->etc() viene usato per implementare helper di assertion
// da Illuminate\Routing\Router
$route = tap($this->routes->getByName($name))->bind($this->currentRequest);

return tap(static::toResponse($request, $response), function ($response) use ($request) {
    $this->events->dispatch(new ResponsePrepared($request, $response));
});
Da solo tap() è un piccolo helper, ma combinato con Macroable e Conditionable rende più facile scrivere le catene di metodi leggibili tipiche di Laravel.

Prossimi passi

Trait Macroable

Impara come progettare l’aggiunta di metodi personalizzati a classi esistenti.

Trait Conditionable

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