在本文,我们将讨论 Laravel 中的 tap。我们将详细讨论 tap 帮助函数和 collection 中的 tap 方法。
Tap 帮助函数
旧的实现方式
Laravel提出了一个 tap 功能。这是一个非常奇怪的功能,受Ruby的启发。这是 tap 助手功能的基本实现。
1function tap($value, $callback) 2{ 3 $callback($value); 4 5 return $value; 6}
上面的代码将接受一个参数,它将使用该参数调用一个匿名函数。在调用回调函数后,它将返回参数。 让我们看看我们如何以有意义的方式使用它。例如:
1<?php 2 3$photo = App\Photo::find(1); 4 5return tap($photo, function($photo) { 6 $photo->validated = true; 7 $photo->save(); 8});
在上面的例子中,我们传递一个参数(照片模型)和一个回调函数,该函数简单地将 validated 设置为 true 并保存模型。这个函数然后将照片模型实例返回给调用者。
新的实现方式
在最新版本的Laravel 5.4和Laravel 5.5中,更高级的 tap 来了。它引入了更短的使用方式。这里是 tap 函数的新实现。
1function tap($value, $callback = null) 2{ 3 if (is_null($callback)) { 4 return new HigherOrderTapProxy($value); 5 } 6 7 $callback($value); 8 9 return $value; 10}
回调函数现在是可选的。你还可以链式使用参数中的多个方法,这里其实也就是照片Model中支持的方法。例如
1<?php 2 3$photo = App\Photo::find(1); 4 5return tap($photo)->update([ 6 'validated' => 'true', 7])
我们能够将任何模型的方法通过 tap 链式调用。此更新方法通常返回 true 或 false,但是这里使用了 tap 函数。在这种情况下,它将返回照片模型。tap 可以帮助你返回作为参数传递的对象。
它是如何工作的
tap 是一个非常有用的功能,但有时它很难理解它是如何工作的。 这里来解释它是如何工作的。
如果没有给出回调函数,因为它是可选的,Laravel将返回 HigherOrderTapProxy 的新实例。 在 HigherOrderTapProxy 类中定义了调用魔术方法。 调用魔术方法是由语言动态调用的(所谓的方法在类中没有定义)。 因为除了调用魔术方法,HigherOrderTapProxy 类中没有定义方法,所以每次使用 tap 函数任何方法调用时都会调用它。 在调用魔术方法中,我们的更新方法或任何我们调用的方法将被参数调用,并且它将返回我们最初传递给 tap 函数的参数。
这里是 HigherOrderTapProxy 类中调用魔术方法的实际内容。
1// vendor/laravel/framework/src/Illuminate/Support/HigherOrderTapProxy.php 2public function __call($method, $parameters) 3{ 4 $this->target->{$method}(...$parameters); 5 6 return $this->target; 7}
在上面的代码中,target 属性是我们在tap中传递的参数。
Laravel collection 中的 tap 方法
Laravel还在 collection 类中有一个 tap 方法,可让你在特定的地方传入参数到 tap中,并对这些结果进行处理。tap 不会影响主要 collection 的结果。 这对调试代码和查找在处理集合时出现错误的地方很有帮助。 我们用一个例子来解释这个方法。 初始化以下数组。
1$photos = [ 2 ['file_name' => 'wallpaper', 'validated' => true, 'extension' => 'jpg'], 3 ['file_name' => 'spring', 'validated' => true, 'extension' => 'png'], 4 ['file_name' => 'flowers', 'validated' => false, 'extension' => 'jpg'], 5 ['file_name' => 'mac', 'validated' => true, 'extension' => 'png'], 6 ['file_name' => 'books', 'validated' => false, 'extension' => 'jpg'], 7 ['file_name' => 'mobiles', 'validated' => false, 'extension' => 'jpg'], 8 ['file_name' => 'glass', 'validated' => false, 'extension' => 'png'], 9 ['file_name' => 'fruit', 'validated' => true, 'extension' => 'jpg'], 10];
现在让我们尝试在这个数组上使用 tap 方法。首先,我们必须将这个数组转换为一个集合,然后在特定点处 tap 这个集合。
1return collect($photos) 2 ->where('validated', true) 3 ->tap(function ($validated) { 4 return var_dump($validated->pluck('file_name')); 5 }); 6});
上面的代码将会输出以下结果:
1wallpaper 2spring 3mac 4fruit
tap VS Pipe(管道)
在Laravel中,也有类似的方法叫管道。 它们在某种意义上是相似的,因为它们都在集合管道中使用。 tap 和 pipe 之间有一个区别。 tap 允许你使用数据,但不会修改原始返回值。 另一方面,pipe 根据返回值修改数据。 例如:
1return collect($photos) 2 ->where('validated', true) 3 ->pipe(function ($validated) { 4 return $validated->where('extension', 'jpg')->pluck('file_name'); 5 }); 6});
输出结果为
1wallpaper 2fruit
另一方面,如果我们像这样使用上面的代码:
1return collect($photos) 2 ->where('validated', true) 3 ->tap(function ($validated) { 4 return $validated->where('extension', 'jpg')->pluck('file_name'); 5 }); 6});
它将返回验证设置为true的所有照片数组。
结果为
10: { 2 file_name: "wallpaper", 3 validated: true, 4 extension: "jpg" 5}, 61: { 7 file_name: "spring", 8 validated: true, 9 extension: "png" 10}, 113: { 12 file_name: "mac", 13 validated: true, 14 extension: "png" 15}, 167: { 17 file_name: "fruit", 18 validated: true, 19 extension: "jpg" 20}
更多PHP知识,请前往PHPCasts