Sitemap

Laravel Macroable: Understanding macros and mixin

4 min readOct 12, 2022

--

Laravel Macroable is a package, that allows adding extra functionality to a class that is missing in the class definition through a simple trait.

It must not have to be in Laravel’s internal classes. Any class that uses Illuminate\Support\Traits\Macroable will allow it to extend its functionality.

How to use it?

If you’re not using Laravel, you will have to install the following package.

composer require illuminate/macroable

And require the autoload.php file from the vendor directory.

Let’s define a class.

<?phpuse Illuminate\Support\Traits\Macroable;class Greeting
{
use Macroable;

public function __construct(protected string $name)
{
}

public function sayHello()
{
return sprintf('Hello %s%s', $this->name, PHP_EOL);
}
}

In the above class, we have used the Macroable trait. That’s it in the class definition.

Now to extend the functionality, in our codebase, we can do as

Greeting::macro('greet', function (?string $greeting = null) {
return !is_null($greeting)
? sprintf('%s %s%s', $greeting, $this->name, PHP_EOL)
: $this->sayHello();
});

Here, the ::macro method allows us to extend the functionality. The first parameter in the macro method is the name of the function that we will use in our code. And the second parameter is a callable.

As you see in the above snippet, $this keyword was used inside our closure. By using macro, we will have access to the Class/Object we are adding those macros.

And, whatever parameters we add in the closure, we will have to provide those values when we call it.

Check the usage

$greeting = new Greeting('Anik');// Calling class's method
echo $greeting->sayHello(); // "Hello Anik"
// Calling the greet method, we defined using Class::macro
echo $greeting->greet('Good evening'); // "Good evening Anik"
echo $greeting->greet('Goodbye'); // "Goodbye Anik"
echo $greeting->greet(); // "Hello Anik"

We are just not limited to using macros from an Object context. It is also possible to call as a static method.

Greeting::macro('whatTimeIsIt', function () {
return sprintf(
'Hey, it is %s',
(new DateTimeImmutable())->format('H:i:s')
);
});
echo Greeting::whatTimeIsIt(); // "Hey, it is 06:47:52"

When you’re calling those as static methods, you will have access to the class static properties and methods as well.

A method added through the macros can be called from both the class and object context. Based on how you’re calling those methods, it will either have access to the $this keyword or not.

What is a mixin?

So far, we have added new methods using the ::macro method and closures. But, if you want to add a few more methods, the ::macro will go messy, and keeping track of these methods can be cumbersome. Another approach to add macros is using mixin. With mixin, you can tell that “I have XYZ class, and you add all the methods available in that class.”

Let’s try the previous examples using Mixin.

Firstly, let us define a class that will hold all the extended functionality.

<?phpclass GreetingMixin
{
public function greet()
{
return function (?string $greeting = null) {
return $greeting
? sprintf('%s %s%s', $greeting, $this->name, PHP_EOL)
: $this->sayHello();
};
}

public function whatTimeIsIt()
{
return function () {
return sprintf(
'Hey, it is %s',
(new DateTimeImmutable())->format('H:i:s')
);
};
}
}

We named our class GreetingMixin but you can name it whatever you want. Mixin class methods should either be Public or Protected. Private methods are not considered to be added on macros. These methods should not have any parameters and they should return a Closure/Anonymous method. Closures can have any parameters you want and these closures are going to be bound to the actual class to extend the functionality.

Now, we have to connect theGreetingMixin class to the Greeting class using the ::mixin method.

Greeting::mixin(new GreetingMixin());

The second parameter in ::mixin method receives a bool flag, default set to true, determining if you want to replace any existing macro defined previously.

Now, if we call these methods, just like before and will get the exact same result.

$greeting = new Greeting('Anik');// Class method
echo $greeting->sayHello(); // "Hello Anik"
//Macro methods, added through ::mixin
echo $greeting->greet('Good evening'); // "Good evening Anik"
echo $greeting->greet('Goodbye'); // "Goodbye Anik"
echo $greeting->greet(); // "Hello Anik"

echo Greeting::whatTimeIsIt(); // "Hey, it is 07:02:01"

Where to define these macros?

If you’re going to use it in Laravel, you can put it anywhere you want. You can put in the routes file, or in the config files or even in the controller just before using it. You are free to do it. But just make sure that the definitions are executed before you start using the macro methods.

But, what is the point if you add them in the controller? Maybe you’re duplicating it in the next method? Or isn’t it a bad practice to put these things in route files or in config? I just gave you an example.

Putting these definitions in the service provider should be a good practice. If your macros are limited then maybe in the AppServiceProvider is a good place to start. If not, then you can add a new Provider something like AdditionalMacroServiceProvider and add them in the boot method and finally add in your service providers array.

Actual use cases?

Have you ever been through the Laravel’s internal codebase? If yes, then you might have seen it already. Otherwise,

Can be a good place to check. You can find more by looking through the code.

That’s all for this article.

Happy coding. ❤

Laravel macroable, Laravel Macros, Laravel Mixin, Understanding Laravel Macros, Understanding Laravel Mixin, Use Laravel Macro outside Laravel.

--

--

Syed Sirajul Islam Anik
Syed Sirajul Islam Anik

Written by Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote

Responses (2)