# [哲煜文章] 客製化你的Laravel: macro ###### tags: `哲煜文章` `Laravel` > 此篇文章也同時寫在我的[部落格](https://hankz1108.github.io/posts/20220825-laravel-macro/), 歡迎來逛逛 ### 前言 有的時候,會希望在Laravel原生的class新增功能的時候,大多我們都會寫一個新的class並繼承,但是其實Laravel提供了一個不同的方式,讓我們可以在常用的class上,直接新增想要的function,那就是macro。 ### 一、使用 我們可以在Provider的boot()裡面使用macro來添加自訂的function,這邊以Collection為例子: ```php 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移除指定的元素: ```php $collection = collect([ '標題1', '標題2', '標題3', '標題4' ]); $collection->remove('標題3'); ``` 或是像data_get一樣取得巢狀物件或陣列的方式,移除指定的元素,例如: ```php $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,讓我們看一下原始碼寫了一些甚麼吧。 ```php public static function macro($name, $macro) { static::$macros[$name] = $macro; } ``` macro做的事情非常單純,就是把我們放進來的function name跟Closure儲存下來,而等到我們使用自訂function的時候就會觸發call function的機制: ```php 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非常的多,包括但不限於以下: - Response - Request - Collection - HTML - Form - Str - Arr - Translator - File - Lang 但凡只要有引用`Macroable`的calss都可以使用carce來註冊自己的function,筆者這邊在Laravel 8內搜尋了一下就發現有51個class有引用`Macroable`,這還不包含這些class的子class,可以說非常的廣泛,大家不妨可以自己試試看。 參考資料: - https://laravel.com/docs/8.x/collections#extending-collections - https://learnku.com/laravel/t/2915/how-to-use-the-macro-method-to-extend-the-function-of-the-base-class-of-laravel