Comprendiendo al helper tap() cover image

Comprendiendo al helper tap()

Kenny Horna Lectura de 3 minutos

laravel php programming tips

El método tap() es muy útil pero puede que lo hayas pasado por encima o no comprendas del todo sobre cómo funciona. Descúbrelo hoy mediante algunos ejemplos prácticos.


El helper tap()

Laravel 5.3 introdujo este útil método que básicamente sirve para ejecutar una acción sobre un valor y devolver este valor evitando la necesidad de emplear variables temporales. Puede que no tenga mucho sentido descrito de esa manera.. pero vayamos con un ejemplo. Imaginemos que queremos actualizar el título de un post en específico desde el controlador. Normalmente haríamos lo siguiente:

$post = Post::find(25);
$post->update(['title' => 'Mi nuevo título']);

return $post->fresh();

¿Cómo podríamos hacer utilizando tap()?, pues:

return tap(Post::find(25), function ($post)
    $post->update(['title' => 'Mi nuevo título']);
);

Entonces, ¿Cuál es realmente el beneficio? Ahorrarnos el crear una variable temporal para almacenar $post. Si es que nos fijamos, en el primer argumento colocamos Post::find(25) y este se pasará al callback que estamos pasando como segundo argumento. Lo interesante es que en este segundo método podemos hacer cualquier operación pues siempre se retornará el valor del primer argumento.

¿Cómo funciona?

Analicemos como funciona esto, ignoremos las líneas comentadas por el momento, luego explicaremos estas:

 function tap($value, $callback = null)
 {         
     // if (is_null($callback)) {
     //    return new HigherOrderTapProxy($value);
     // }

     $callback($value);

     return $value;
 }

Como puedes ver, simplemente toma la función $callback que pasamos como segundo parámetro y lo aplica sobre la variable que pasamos como primer parámetro, retornando finalmente esta variable en cuestión.

Mensaje de orden superior

Ahora, ¿qué son esas líneas comentadas? Lo que hacen es verificar si es que el segundo parámetro es null:

function tap($value, $callback = null)
 {         
     if (is_null($callback)) {                  // <--
        return new HigherOrderTapProxy($value); // <--
     }                                          // <--

     $callback($value);

     return $value;
 }

Si esto es así, se retornará uan instancia de la clase HigherOrderTapProxy (Laravel 5.4+) que no es más que una implementación de los mensajes de orden superior que ya habíamos visto en las colecciones en el artículo anterior. Entonces, aplicando esto a nuestro ejemplo inicial veremos que podemos simplificarlo a lo siguiente:

return tap(Post::find(25))->update(['title' => 'Mi nuevo título']);

¿Interesante cierto? Principalmente porque la función update() retorna un booleano, sin embargo, dado que estamos usando tap() se retornará el valor inicial, por ende, una instancia de Post con id=25.

Ejemplos de uso

A lo largo de su código fuente, Laravel lo implementa en muchos lugares. Lo podemos encontrar, por ejemplo, en el método create() de Eloquent:

public function create(array $attributes = [])
{
    return tap($this->newModelInstance($attributes), function ($instance) {
        $instance->save();
    });
}

Así es como lucía antes de implementar tap():

public function create(array $attributes = [])
{
    $instance = $this->newModelInstance($attributes);

    $instance->save();

    return $instance;
}

Otra aplicación de este útil helper es en el middleware AuthenticationSession:

public function handle($request, Closure $next)
{
    return tap($next($request), function () use ($request) {
        $this->storePasswordHashInSession($request);
    });
}

Así es como lucía antes de tap():

public function handle($request, Closure $next)
{
    $response = $next($request);

    $this->storePasswordHashInSession($request);

    return $response;
}

Este caso es interesante pues aquí no se está efectuando ninguna operación sobre el valor pasado a la función tap. Esto nos dice que podemos hacer lo que deseemos dentro del $callback que pasamos a tap, aún con el beneficio de ahorrarnos el utilizar variables temporales (en este caso la de $response).

tap() en colecciones

Este método también está disponible para las colecciones. Es particularmente útil aquí para efectuar operaciones en la colección sin necesidad de interrumpir el flujo de este. Veamos un ejemplo tomado de la documentación:

collect([2, 4, 3, 1, 5])
    ->sort()
    ->tap(function ($collection) {
        Log::debug('Valores después de ordenar', $collection->values()->toArray());
    })
    ->shift();

// 1

Como podemos apreciar, estamos incluyendo tap() para loggear los valores de la colección luego de haber sido ordenados. Esto no afecta para nada el resto del proceso que hemos detallado, en este caso la aplicación del método shift().


Cierre

Personalmente, considero que el empleo de métodos/formas/técnicas para la escritura de un código más eficiente y/o legible vale la pena implementar. Hay algunas personas que consideran a la implementación de tap como un paso atrás, pues aparentemente dificulta la legibilidad y hace la tarea más difícil para los IDEs; sin embargo, este no ha sido mi caso y espero poder aplicarlo cada vez que pueda.