此文章同步刊登於我的部落格
有的時候,會希望在Laravel原生的class新增功能的時候,大多我們都會寫一個新的class並繼承,但是其實Laravel提供了一個不同的方式,讓我們可以在常用的class上,直接新增想要的function,那就是macro。
我們可以在Provider的boot()裡面使用macro來添加自訂的function,這邊以Collection為例子:
Collection::macro('remove', function (string $filterValue, ?string $filterKey = null) {
foreach ($this as $key => $item) {
// 根據是否有$filterKey決定如何取值
$value = is_null($filterKey) ? $item : data_get($item, $filterKey);
// 如果值與$filterValue相同就移除該item
if ($value == $filterValue) {
$this->forget($key);
}
}
return $this;
});
現在Collection就被註冊了一個名為remove的function了,可以直接透過value移除指定的元素:
$collection = collect([
'標題1', '標題2', '標題3', '標題4'
]);
$collection->remove('標題3');
或是像data_get一樣取得巢狀物件或陣列的方式,移除指定的元素,例如:
$collection = collect([
[
'title' => '標題1',
'content' => '內容1',
'writer' => [
'name' => '作者1',
'gender' => 'man',
],
],
[
'title' => '標題2',
'content' => '內容2',
'writer' => [
'name' => '作者2',
'gender' => 'woman',
],
],
[
'title' => '標題3',
'content' => '內容3',
'writer' => [
'name' => '作者3',
'gender' => 'man',
],
],
]);
$collection->remove('作者3', 'writer.name');
在Laravel中只要是有使用Illuminate\Support\Traits\Macroable
這個trait的class,就可以使用macro,而Laravel是如何實現這個功能的呢?其實也很簡單,就是利用魔術方法中的call function,讓我們看一下原始碼寫了一些甚麼吧。
public static function macro($name, $macro)
{
static::$macros[$name] = $macro;
}
macro做的事情非常單純,就是把我們放進來的function name跟Closure儲存下來,而等到我們使用自訂function的時候就會觸發call function的機制:
public function __call($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}
$macro = static::$macros[$method];
if ($macro instanceof Closure) {
$macro = $macro->bindTo($this, static::class);
}
return $macro(...$parameters);
}
這樣我們的自訂functin就註冊給了這個class,也就可以像內建的function來使用了。
在Laravel中,有使用Macroable
的class非常的多,包括但不限於以下:
但凡只要有引用Macroable
的calss都可以使用carce來註冊自己的function,筆者這邊在Laravel 8內搜尋了一下就發現有51個class有引用Macroable
,這還不包含這些class的子class,可以說非常的廣泛,大家不妨可以自己試試看。