Extending Laravel Core Class Using Laravel Macros

Hey artisan, in this tutorial we are going to learn about laravel macros. Macro is the powerful feature of the laravel framework. Macros allow you to add on custom functionality to internal Laravel components. This laravel macro also work with laravel 5.8 , 5.7 and 5.6 .

Macros provide a way to add functionality to the classes in which you don’t have that function. It helps you to create a small reusable component that will be possible to use it anywhere in your application.

laravel-macro

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 All 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.

  • Request: Illuminate\Http\Request
  • Response: Illuminate\Http\Response
  • Collection: Illuminate\Support\Collection
  • Str: Illuminate\Support\Str
  • Router: Illuminate\Routing\Router
  • UrlGenerator: Illuminate\Routing\UrlGenerator
  • Cache: Illuminate\Cache\Repository
  • Filesystem: Illuminate\Filesystem\Filesystem
  • Arr: Illuminate\Support\Arr
  • 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.

 

Example of 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.

app/Providers/AppServiceProvider.php

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;

Route::get('/', function(){

   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.

 

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.

App\Mixins\StrMixin.php

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.

app/Providers/AppServiceProvider.php

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. Now you know how to extend the Laravel’s core classes using Laravel Macros. Hope it will help you.