Laravel 中強大的tap你用過麼

如來神掌發表於2019-02-16

在本文,我們將討論 Laravel 中的 tap。我們將詳細討論 tap 幫助函式和 collection 中的 tap 方法。

Tap 幫助函式

舊的實現方式

Laravel提出了一個 tap 功能。這是一個非常奇怪的功能,受Ruby的啟發。這是 tap 助手功能的基本實現。

function tap($value, $callback)
{
    $callback($value);

    return $value;
}

上面的程式碼將接受一個引數,它將使用該引數呼叫一個匿名函式。在呼叫回撥函式後,它將返回引數。
讓我們看看我們如何以有意義的方式使用它。例如:

<?php

$photo = AppPhoto::find(1);

return tap($photo, function($photo) {
    $photo->validated = true;
    $photo->save();
});

在上面的例子中,我們傳遞一個引數(照片模型)和一個回撥函式,該函式簡單地將 validated 設定為 true 並儲存模型。這個函式然後將照片模型例項返回給呼叫者。

新的實現方式

在最新版本的Laravel 5.4和Laravel 5.5中,更高階的 tap 來了。它引入了更短的使用方式。這裡是 tap 函式的新實現。

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

    $callback($value);

    return $value;
}

回撥函式現在是可選的。你還可以鏈式使用引數中的多個方法,這裡其實也就是照片Model中支援的方法。例如

<?php

$photo = AppPhoto::find(1);

return tap($photo)->update([
    `validated` => `true`,
])

我們能夠將任何模型的方法通過 tap 鏈式呼叫。此更新方法通常返回 truefalse,但是這裡使用了 tap 函式。在這種情況下,它將返回照片模型。tap 可以幫助你返回作為引數傳遞的物件。

它是如何工作的

tap 是一個非常有用的功能,但有時它很難理解它是如何工作的。 這裡來解釋它是如何工作的。

如果沒有給出回撥函式,因為它是可選的,Laravel將返回 HigherOrderTapProxy 的新例項。 在 HigherOrderTapProxy 類中定義了呼叫魔術方法。 呼叫魔術方法是由語言動態呼叫的(所謂的方法在類中沒有定義)。 因為除了呼叫魔術方法,HigherOrderTapProxy 類中沒有定義方法,所以每次使用 tap 函式任何方法呼叫時都會呼叫它。 在呼叫魔術方法中,我們的更新方法或任何我們呼叫的方法將被引數呼叫,並且它將返回我們最初傳遞給 tap 函式的引數。

這裡是 HigherOrderTapProxy 類中呼叫魔術方法的實際內容。

// vendor/laravel/framework/src/Illuminate/Support/HigherOrderTapProxy.php
public function __call($method, $parameters)
{
    $this->target->{$method}(...$parameters);

    return $this->target;
}

在上面的程式碼中,target 屬性是我們在tap中傳遞的引數。

Laravel collection 中的 tap 方法

Laravel還在 collection 類中有一個 tap 方法,可讓你在特定的地方傳入引數到 tap中,並對這些結果進行處理。tap 不會影響主要 collection 的結果。 這對除錯程式碼和查詢在處理集合時出現錯誤的地方很有幫助。
我們用一個例子來解釋這個方法。 初始化以下陣列。

$photos = [
    [`file_name` => `wallpaper`, `validated` => true, `extension` => `jpg`],
    [`file_name` => `spring`, `validated` => true, `extension` => `png`],
    [`file_name` => `flowers`, `validated` => false, `extension` => `jpg`],
    [`file_name` => `mac`, `validated` => true, `extension` => `png`],
    [`file_name` => `books`, `validated` => false, `extension` => `jpg`],
    [`file_name` => `mobiles`, `validated` => false, `extension` => `jpg`],
    [`file_name` => `glass`, `validated` => false, `extension` => `png`],
    [`file_name` => `fruit`, `validated` => true, `extension` => `jpg`],
];

現在讓我們嘗試在這個陣列上使用 tap 方法。首先,我們必須將這個陣列轉換為一個集合,然後在特定點處 tap 這個集合。

return collect($photos)
    ->where(`validated`, true)
    ->tap(function ($validated) {
        return var_dump($validated->pluck(`file_name`));
    });
});

上面的程式碼將會輸出以下結果:

wallpaper
spring
mac
fruit

tap VS Pipe(管道)

在Laravel中,也有類似的方法叫管道。 它們在某種意義上是相似的,因為它們都在集合管道中使用。 tappipe 之間有一個區別。 tap 允許你使用資料,但不會修改原始返回值。 另一方面,pipe 根據返回值修改資料。
例如:

return collect($photos)
    ->where(`validated`, true)
    ->pipe(function ($validated) {
        return $validated->where(`extension`, `jpg`)->pluck(`file_name`);
    });
});

輸出結果為

wallpaper
fruit

另一方面,如果我們像這樣使用上面的程式碼:

return collect($photos)
    ->where(`validated`, true)
    ->tap(function ($validated) {
        return $validated->where(`extension`, `jpg`)->pluck(`file_name`);
    });
});

它將返回驗證設定為true的所有照片陣列。

結果為

0: {
    file_name: "wallpaper",
    validated: true,
    extension: "jpg"
},
1: {
    file_name: "spring",
    validated: true,
    extension: "png"
},
3: {
    file_name: "mac",
    validated: true,
    extension: "png"
},
7: {
    file_name: "fruit",
    validated: true,
    extension: "jpg"
}

更多PHP知識,請前往PHPCasts

相關文章