Peter Fox
Posted on June 13, 2019
Macros in Laravel are something that I think are still not talked about enough in the framework. They’re really power and really useful. I don’t think there’s a project I’ve created in the last year or two that isn’t using a macro somewhere.
To cover what a macro is briefly, it’s a way of extending a class’ methods but without using inheritance and instead it works by adding a Closure to a class. This then means that all instances of that class has that newly added method. Any class that has uses the Macroable trait found in the Laravel framework can have macros applied to them.
In this article I want to cover a range of simple use cases that will improve things for you, reducing duplicating code, making code more readable or simply getting around problems you might have in testing.
How to make them and where to put them
This isn’t an obvious thing from the get go. There’s one of two places I would advise putting your macros. The first is by using a simple PHP file loaded up via Composer. I generally make this new file in the app folder and call it macros.php Then you simply edit the autoloader key in composer.json to have a files property and within that array list you should add the relative path to the fileapp/macros.php. This then just requires running composer dump-autoloader so the newly added file will be loaded and executed at runtime, setting up our macros for the entire application.
The other place to put Macros is in the boot method of service providers. This can get a bit messy to be honest as your application grows and you add more macros but there will be the odd Macro that can’t be configured in the macros.php file, namely when adding macros to the Route class as it’s tied to the instance stored in the service container. We won’t be using the Route class in this article though.
Collections
Ok, this is a boring one, but it’s where most people will have been introduced to Macros in Laravel so I wanted to cover this one quickly. A nice little example of one macro I’ve had to use before is converting keys for array, something that could be quite tedious if you wrote it out as a function.
Instead we can add it as a macro. First we can make a macro to do all the mapping for us and manage it recursively.
Then we’ll make another just to wrap it up nicely.
That’s it. Now we can use it like the following example.
Eloquent Queries
This is probably one of the most important places where macros really make a difference. For example it would be near impossible to extend the query builder yourself but with a macros you don’t have to.
For example, often you might want to make direct use of functionality in your database server and often you’ll do this with raw queries.
But this can become very repetitive if you’re doing this in your application a lot, let alone when mixing trying to do multiple wheres in one statement.
One way to get around this might be to use a scope but then this still requires added the scope to any model you need to use it with. We can really just make a macro for this. In this example we’ll make a macro for filter results in MySQL using the built in geospatial functions of the server.
Then we can add another macro just so we can do orWhere statements with it as well.
Now when we want to do filtering by MySQL we can call it directly like any other where statement.
Testing Responses
Writing feature tests are common and really effective but it can get quite repetitive. Worst yet, maybe you have something custom, like an API that always returns the HTTP status as 200 but returns something like
{"error"=\>true,"message"=\>"bad API call"}
You might end up needing write tests like this
Which is fine for one test, but what about all of them. Maybe a macro could solve this, and guess what it can.
Firstly, we’re going to want to make a tests/macros.php this time and add it to composer.json instead under the autoload-dev then files array that we need to likely add ourselves. We also need to reload the autoloader with composer dump-autoloader afterwards.
Then in the new macro file tests/macros.php file we can add the following.
Now we can use it our tests.
It’s a very simple snippet but it really can make your tests far more readable when you start reducing repetitive behaviors into reusable macros.
File operations (for mocking)
Another time where a macro is useful is when you want to make use of Facades and their ability to be mocked swifty for your unit tests. No better scenario exists for mocking than using the built in PHP classes that will touch the file system during a test.
For example, see this code using the ZipArchive class.
I can’t mock this, it’s a totally new instance of a ZipArchive class. At best I could make some ZipArchive factory to make the class but it seems a bit overkill just to use a simple bit of PHP’s built in functionality. Instead we’ll macro it (in our app/macros.php file).
So when it comes to production code all we need to do is use the facade.
But for our tests we just mock the method using the Facade.
This means we not longer have to worry about cleaning up the file system for our tests. We’ve neatly wrapped up our Zip Archive logic into something reusable but also something easily mockable.
Validation Rules
The last of the examples is really simple but again will help hugely when it comes to managing your code.
For example, often I’ve come across this and it’s no surprising because I used to do it too.
It’s not very clean looking on it’s own, let alone when there’s multiple fields and multiple rules per field like this.
Instead we’re be much better off with something like the following
And with a macro we can.
Bonus Tip: What to do when you’ve got too many macros in one really long file?
You’ve seen the example now but what about when you now have so many macros that you find it hard to actually find or just organise them? The instant go to might be to just make more files like app/validation_macros.php but you’d be wrong. This is where Mixins come in (not to be confused with Traits which are sometimes thought of as Mixins due to other languages having mixins as a concept).
A mixin is a class that defines lots of Macros that can then be provided to your macroable class to implement all of them in one go.
Lets look at an example by creating a new class app/RulesMixin.php.
As you can see from the code above, all the mixin has to go is implement a method with no arguments that returns the closure that will be our macro, using the method name for the macro as well.
Now in our app/macros.php we can just set up the class to use the macro like the following
This then means the Rule class will not only still have the before method, it’ll now have the after, beforeOrEqual and afterOrEqual validation rules in a more accessible format.
Conclusions
Overall I really enjoy the use of Macros in Laravel and can’t emphasise enough how useful they can be for extending the functionality of the framework to suit your needs without lots of work to do so.
If you want to see more classes that use the Macroable trait I’ve put together a quick list for Laravel 5.8.
If you want to see the project implementation you can view the code on GitHub.
I’m Peter Fox, a software developer in the UK who works with Laravel among other things. If you want to know more about me you can at https://www.peterfox.me and feel free to follow me @SlyFireFox on twitter for more Laravel tips and tutorials.
Posted on June 13, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.