Laravel Macros Postmortem

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

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

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 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 scene is that if you call a method on a class object which doesn’t exist and you have defined __call() magic method then control will go to this method and here you can capture request and perform any task you want, for example, may be you want to log it and throw an exception.

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

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

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

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 following

 

    • Call first goes to Response Class and PHP Engine will check for the definition of called method. In our case, it will check for caps function definition in Response Class (Illuminate/Routing/ResponseFactory);
    • If method is found, then it will be executed, else PHP Engine will check for __call() & __callStatic() Magic Methods depending whether the function is called statically or not.
  • In our case, Response Class (Illuminate/Routing/ResponseFactory) does not have caps method defined, so PHP Engine will forward the control to the either __call() & __callStatic() ( depending on the called type) defined in the Macroable Trait. Here we are first checking whether the called function is registered with our macro or not. In case of no, BadMethodCallException is thrown. In case of yes, we are binding the macro callback to the class reference and we are executing that function.

What if I want to define multiple Macros?

Laravel again to the rescue! We can define multiple macros at once by using Macroable::mixin($mixin, $replace) method. $mixin argument is a class object which contains methods which returns callables.

Scope of Macros

One important thing to note in the case of Macros are that these functions are bound to that class scope in which Macroable Trait is used in and doesn’t bound to the class where they are registered from. For example let us add whoami method in our Laravel Response Class ( (Illuminate/Routing/ResponseFactory) to check the scope of $this.

When we call this in our test route we get a result of Illuminate\Routing\ResponseFactory and not AppService Provider.

The secret behind this is usage of bindTo method of **PHP Closure Class.** bindTo method bounds the callback with called class scope and inside your macro callback you have $this as called class scope and you can access other accessible methods and attribbutes.

Where Macros can be defined?

You can define Macros inside Laravels’s Service Providers. App\Providers\AppServiceProvider boot() is a good starting point. If you have many macros in your application then recommended approach or best practice would be to create your own Service Provider Class for Macros and register it in config/app.php

Contact Us

Address

What Market Says About Us!