Laravel Macros – Extending Laravel’s Core Classes

Laravel Macros - Extending Laravel's Core Classes

Laravel Macros are a great way of extending Laravel’s core classes and add extra functionality required for your application. Obviously, we can extend the 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 word, Laravel Macro is a way to add some missing functionality to Laravel’s internal component with a piece of code which 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 locating 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’s 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.

  1. Request: Illuminate\Http\Request
  2. Response: Illuminate\Http\Response
  3. Collection: Illuminate\Support\Collection
  4. Str: Illuminate\Support\Str
  5. Router: Illuminate\Routing\Router
  6. UrlGenerator: Illuminate\Routing\UrlGenerator
  7. Cache: Illuminate\Cache\Repository
  8. Filesystem: Illuminate\Filesystem\Filesystem
  9. Arr: Illuminate\Support\Arr
  10. Rule: Illuminate\Validation\Rule

There 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 use 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 class which simply compare the length of first parameter with second one.

Macros can have a number of parameters that you decide. 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 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 Mutliple 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 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 the Laravel’s core classes using Laravel Macros (not all classes use the trait so you should check that first). If you have any question about Macros, please leave them in the comments box below.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

When sharing a code snippet please wrap you code with pre tag and add a class code-block to it like below.
<pre class="code-block">you code here</pre>

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.