Laravel Macros Postmortem

Laravel Macros Postmortem

Laravel gives you the ability to add methods to a class at runtime by using the Macroable (Illuminate\Support\Traits\Macroable) Trait. You need to use the Macroable trait inside the class in which you want to add methods at run time.

Here is an example from the Laravel Documentation. The following code adds a toUpper method to the Collection class:

collectionExample


How to Register Your Macros?

Laravel’s Macroable trait offers 2 ways to register your macros.

  1. Using Macroable::macro($name, $macro) method.
  2. Using Macroable::mixin($mixin, $replace) method.

How the Magic Stuff Works?

For the magic stuff to work, you need to have Magic Methods. Macros magic is based on two magic methods provided by PHP: __call() and __callStatic().

How it all works behind the scenes is that if you call a method on a class object which doesn’t exist, and you have defined the __call() magic method, control will go to this method. Here you can capture the request and perform any task you want, for example log it and throw an exception.

Laravel uses this concept very intelligently and neatly. Laravel has defined these magic methods inside the Macroable trait. First the Macroable trait is used inside a class where we want to add dynamic methods.

macroWithMagicStuff

Then we register our method by calling the macro static method. For example, let’s add functionality in our Laravel Response class to capitalize a value. We can do that with the following piece of code, an example taken from the Laravel Documentation.

macroStatic

Implementation of the macro function is pretty straightforward. It maintains a mapping of the name of the newly added function and the callback for it.

registerCustomMacro

After registering your macro, it is time to call it!

Once you call your registered macro, for example Response::caps('laravel'), the request flow will be the following:

  • The call first goes to the Response class, and the PHP engine checks for the definition of the called method. In our case it checks for a caps function definition in the Response class (Illuminate/Routing/ResponseFactory).
  • If the method is found, it is executed. Otherwise the PHP engine checks for the __call() and __callStatic() magic methods, depending on whether the function was called statically or not.
  • In our case, the Response class (Illuminate/Routing/ResponseFactory) does not have a caps method defined, so the PHP engine forwards control to either __call() or __callStatic() (depending on the call type) defined in the Macroable trait. Here we first check whether the called function is registered as a macro. If not, a BadMethodCallException is thrown. If it is, we bind the macro callback to the class reference and execute it.

dynamicHandle


What if I Want to Define Multiple Macros?

Laravel to the rescue again! You can define multiple macros at once using the Macroable::mixin($mixin, $replace) method. The $mixin argument is a class object whose methods return callables.

defineMultipleMacros


Scope of Macros

One important thing to note about macros is that they are bound to the class scope in which the Macroable trait is used, not the scope from which they are registered. For example, let’s add a whoami method to our Laravel Response class (Illuminate/Routing/ResponseFactory) to check the scope of $this.

scopeOfMacros

When we call this in our test route, we get a result of Illuminate\Routing\ResponseFactory, not AppServiceProvider.

responseFactory

The secret behind this is the bindTo method of the PHP Closure class. The bindTo method binds the callback to the called class scope. Inside your macro callback, $this is the called class scope, and you can access other accessible methods and attributes.


Where Can Macros Be Defined?

You can define macros inside Laravel’s Service Providers. App\Providers\AppServiceProvider‘s boot() method is a good starting point. If you have many macros in your application, the recommended approach is to create your own Service Provider class for macros and register it in config/app.php.

Founded
2014
Years in business
12
People
66
Headquarters
Lahore, Pakistan
Projects delivered
250+
AI systems in production
10