Saltar al contenido principal

Qué es tap()

tap() es un helper que permite «usar un valor y devolverlo tal cual». Se utiliza cuando quieres intercalar un efecto colateral. La implementación en Illuminate\Support\helpers.php es simple.
function tap($value, $callback = null)
{
    if (is_null($callback)) {
        return new HigherOrderTapProxy($value);
    }

    $callback($value);

    return $value;
}
tap() ignora el valor de retorno de la callback. El resultado es siempre el valor original.

Uso básico

El uso más habitual: recibir un valor, procesarlo y devolverlo tal cual.
use App\Models\User;

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

// Se devuelve $user, sin importar lo que devuelva update()
Si omites la callback, se devuelve un HigherOrderTapProxy para encadenar directamente llamadas a métodos.
$updatedUser = tap($user)->update([
    'name' => $name,
    'email' => $email,
]);

// Se devuelve la instancia User original, no el valor de retorno de update()

Casos de uso frecuentes

Intercalar salida de depuración

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

Insertar logs o disparar eventos en medio de una cadena

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

Ejecutar solo el efecto colateral sin cambiar el retorno

$response = tap($service->handle($request), function ($response) {
    \Illuminate\Support\Facades\Cache::increment('service_handle_success_total');
});
Si quieres transformar el valor de retorno, usa with() o simplemente asignación normal. Reservar tap() para efectos colaterales facilita la lectura.

Qué es el trait Tappable

Con use del trait Illuminate\Support\Traits\Tappable añades el método tap() a tu clase.
trait Tappable
{
    public function tap($callback = null)
    {
        return tap($this, $callback);
    }
}
Es decir, ofrece una versión de método de instancia de tap().
use Illuminate\Support\Traits\Tappable;

class ReportBuilder
{
    use Tappable;
}

$builder = new ReportBuilder();

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

Uso en el desarrollo de paquetes

Tappable es útil cuando quieres inyectar efectos colaterales en medio de una API fluida. Combinándolo con Macroable y Conditionable puedes construir builders extensibles al estilo Laravel.
1

Crea una clase fluida

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

Usa Macroable, Conditionable y Tappable juntos

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();
Páginas relacionadas:

Ejemplos en el core de Laravel

tap() / Tappable se usan también dentro del propio Laravel.
  • Illuminate\Routing\Router combina Macroable y Tappable.
  • En Router::respondWithRoute() se usa tap($route)->bind(...) sin callback, devolviendo $route tras ejecutar bind().
  • En Router::prepareResponse() se dispara un evento tras convertir la respuesta con tap(..., fn (...) => event(...)).
  • En Illuminate\Testing\Fluent\Concerns\Has se usa la cadena ->tap(...)->first(...)->etc() para asistir en las aserciones.
// De 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));
});
tap() es un helper pequeño por sí mismo, pero combinado con Macroable y Conditionable facilita construir las cadenas de métodos claras y legibles típicas de Laravel.

Próximos pasos

Trait Macroable

Aprende el diseño para añadir métodos propios a clases existentes.

Trait Conditionable

Aprende las cadenas con bifurcación condicional mediante when() / unless().
Última modificación el 13 de julio de 2026