Laravel Macros – Extending Laravel’s Core Classes
Laravel Macros are a great way of extending Laravel’s core classes and adding extra functionality required for your application. Obviously, we can extend Laravel’s core classes but macros provide a way to add functionality to classes you don’t own.
In this post, we will be looking at how we can use Laravel Macros and which classes can be used to define macros.
What is Laravel Macro?
In simple words, Laravel Macro is a way to add some missing functionality to Laravel’s internal component with a piece of code that doesn’t exist in the Laravel class. To implement a Laravel Macro, Laravel provides a PHP trait called Macroable. You can check for example Response class of Laravel located at Illuminate\Http\Response
which implements the Macroable
trait, which means you can extend the Response class using a Macro.
Macroable Laravel’s Classes
The following Laravel classes allow for macros to be created by using the Illuminate\Support\Traits\Macroable trait. Here are some of the most commonly used classes to create macros.
- Illuminate\Auth\RequestGuard
- Illuminate\Auth\SessionGuard
- Illuminate\Cache\Repository
- Illuminate\Console\Command
- Illuminate\Console\Scheduling\Event
- Illuminate\Cookie\CookieJar
- Illuminate\Database\Eloquent\FactoryBuilder
- Illuminate\Database\Eloquent\Relations\Relation
- Illuminate\Database\Grammar
- Illuminate\Database\Query\Builder
- Illuminate\Database\Schema\Blueprint
- Illuminate\Filesystem\Filesystem
- Illuminate\Foundation\Testing\TestResponse
- Illuminate\Http\JsonResponse
- Illuminate\Http\RedirectResponse
- Illuminate\Http\Request
- Illuminate\Http\Response
- Illuminate\Http\UploadedFile
- Illuminate\Mail\Mailer
- Illuminate\Routing\PendingResourceRegistration
- Illuminate\Routing\Redirector
- Illuminate\Routing\ResponseFactory
- Illuminate\Routing\Route
- Illuminate\Routing\Router
- Illuminate\Routing\UrlGenerator
- Illuminate\Support\Arr
- Illuminate\Support\Collection
- Illuminate\Support\LazyCollection
- Illuminate\Support\Str
- Illuminate\Support\Testing\Fakes\NotificationFake
- Illuminate\Translation\Translator
- Illuminate\Validation\Rule
- Illuminate\View\Factory
- Illuminate\View\View
There are other classes and facades which use the Macroable
trait. You can find all the classes and facades in the codebase of Laravel.
Creating a Laravel Macro
Before creating a Laravel Macro, you have to make sure your targeted class uses the Macroable trait. Here we will be creating a macro on Illuminate\Support\Str class which will check the length of a given string named isLength
. You can define the macro in your AppServiceProvider class’s boot()
method.
namespace App\Providers; use Illuminate\Support\Str; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Str::macro('isLength', function ($str, $length) { return static::length($str) == $length; }); } }
Within the boot method of AppServiceProvider, we are defining a isLength
macro on Str
the class which simply compares the length of the first parameter with the second one.
Macros can have a number of parameters that you decide on. It’s important to note that we are calling static::length
in the macro, as macros still have full access to methods on the original class.
Now you can use this macro anywhere in your application.
use Illuminate\Support\Str; dd(Str::isLength('This is a Laravel Macro', 23)); // true
The Macroable trait’s internal mechanisms allow macros to be called from both static and instance contexts.
Let’s add another macro to Str
the class which will append the given character to a given string.
Str::macro('appendTo', function ($str, $char) { return $char.$str; });
Now you can call the above macro like below:
use Illuminate\Support\Str; dd(Str::appendTo('LaraShout', '@')); // @LaraShout
Creating Multiple Macros
We have added two macros to the Str class using the AppServiceProvider. But as soon as your application will start growing, your AppServiceProvider will start becoming messy. From Laravel 5.5 onward, we can define class-based macros which will make our code less messy.
We will continue with our previous example and move our two macros into a new class. We will create a new class called StrMixin and store it in app/Mixins
folder.
Add the below code:
namespace App\Mixins; class StrMixin { /** * @return \Closure */ public function isLength() { return function($str, $length) { return static::length($str) == $length; }; } /** * @return \Closure */ public function appendTo() { return function($str, $char) { return $char.$str; }; } }
Now in your AppServiceProvider, you can remove the previous macros declaration. We can use mixin()
method to initialize all your macros for a given class. Your AppServiceProvider will look like the below after making modifications.
namespace App\Providers; use App\Mixins\StrMixin; use Illuminate\Support\Str; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Str::mixin(new StrMixin); } }
Now if you test isLength()
and appendTo()
method on Str
class, you will have the same results.
Conclusion
Now you know how to extend Laravel’s core classes using Laravel Macros (not all classes use the trait so you should check that first). If you have any questions about Macros, please leave them in the comments box below.