How to use Macro in Laravel?

We all know that Laravel has many features that we not even see in other frameworks. One of the most powerful feature is Macro. Macro allow us to add new custom functionality to internal Laravel Component and custom services and classes. If I simply the term Macro allow us to extends the Laravel Classes. Now we will see How to use Macro in Laravel?

How to use Macro in Laravel?

How to use Macro in Laravel?

There are different ways to use Macro in Laravel Project but we will see the simplest solution that you can easily understand and try in your project ASAP.

First we will see that how we can add new functionality in existing Laravel components.

We giving a list that can use macro and easily to extends the Laravel Classes.

  • 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

Here we take some example to extends Laravel Functionality.

Symlink Laravel Storage Folder on Shared Hosting

We add some code in boot() method of App\Providers\AppServiceProvider.php

PHPCopy
<?php

namespace App\Providers;

use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Str::macro("checkLength", function (string $str, int $length) {
            return static::length($str) === $length;
        });
    }
}

Now we have new custom method named checkLength() that accept two arguments on method call.

Lets check how we use checkLength() method. Here we use closure function in routes.

PHPCopy
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    \Illuminate\Support\Str::checkLength("This is Test", 12); // return true if length match
});

We can use custom functionality where Macroable trait used. So first we take a look at trait .

Macroable Trait

Macroable trait is responsible to extends Laravel and Custom classes functionality where we or Laravel Core uses Macroable trait.

Lets see the Str class first.

Illuminate\Support\Str.php

PHPCopy
<?php

namespace Illuminate\Support;

use Illuminate\Support\Traits\Macroable;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Ramsey\Uuid\Codec\TimestampFirstCombCodec;
use Ramsey\Uuid\Generator\CombGenerator;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidFactory;
use voku\helper\ASCII;

class Str
{
    use Macroable;

   .........
}

Now Have a look at Macroable Trait.

Illuminate\Support\Traits\Macroable.php

PHPCopy
<?php

namespace Illuminate\Support\Traits;

use BadMethodCallException;
use Closure;
use ReflectionClass;
use ReflectionMethod;

trait Macroable
{
    /**
     * The registered string macros.
     *
     * @var array
     */
    protected static $macros = [];

    /**
     * Register a custom macro.
     *
     * @param  string  $name
     * @param  object|callable  $macro
     * @return void
     */
    public static function macro($name, $macro)
    {
        static::$macros[$name] = $macro;
    }

    /**
     * Mix another object into the class.
     *
     * @param  object  $mixin
     * @param  bool  $replace
     * @return void
     *
     * @throws \ReflectionException
     */
    public static function mixin($mixin, $replace = true)
    {
        $methods = (new ReflectionClass($mixin))->getMethods(
            ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
        );

        foreach ($methods as $method) {
            if ($replace || ! static::hasMacro($method->name)) {
                $method->setAccessible(true);
                static::macro($method->name, $method->invoke($mixin));
            }
        }
    }

    /**
     * Checks if macro is registered.
     *
     * @param  string  $name
     * @return bool
     */
    public static function hasMacro($name)
    {
        return isset(static::$macros[$name]);
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public static function __callStatic($method, $parameters)
    {
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException(sprintf(
                'Method %s::%s does not exist.', static::class, $method
            ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            $macro = $macro->bindTo(null, static::class);
        }

        return $macro(...$parameters);
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException(sprintf(
                'Method %s::%s does not exist.', static::class, $method
            ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            $macro = $macro->bindTo($this, static::class);
        }

        return $macro(...$parameters);
    }
}

We already used macro() method that provided by Macroable trait. Now we take a look on mixin() method.

When we have to add multiple functionality to a Laravel Component we use mixin().

Lets take example.

First we create a custom class that have some methods because mixin() except a object.

App\Macro\StringMacro.php

PHPCopy
<?php 

namespace App\Macro;

use Illuminate\Support\Str;

class StringMacro{

    public function checkLength(string $str, int $length):bool
   {
          return function (string $str, int $length) {
            return Static::length($str) === $length;
        };
   }

    public function countVowel(string $str):int
   {
          return function (string $str) {
            $vowels = 0;
            $length = strlen($str);
            for($i=0; $i =$length; $i++ ){
                  if(strstr('aeiouAEIOU', $str[$i])) $vowels++;
            }
            return $vowels;
        };
   }

}

Now we add App\Macro\StringMacro.php class object into Str Class through mixin() method that available in Macroable trait.

PHPCopy
<?php

namespace App\Providers;

use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

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 App\Macro\StringMacro); // method like checkLength() or countVowel() method now available with Str 
    }
}

Lets have a look that how you can use these method.

PHPCopy
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    \Illuminate\Support\Str::checkLength("This is Test", 12); // return true or false
    \Illuminate\Support\Str::countVowel("This is Test"); // return total vowels
});

Its not limited we can use Macroable to our custom Classes. Lets have a Look.

App\Service\CustomService.php

PHPCopy
<?php


namespace App\Service;


use Illuminate\Support\Traits\Macroable;

class CustomService
{
    use Macroable;
}

Just One trait we can easily extends CustomService Class Functionality .

PHPCopy
<?php

namespace App\Providers;

use App\Macro\RequestMacro;
use App\Service\CustomService;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        CustomService::macro("getBaseURL", function () {
            return "https://dumpcoder.com";
        });
    }
}

Now Take a look how we use This method with our CustomService Class.

PHPCopy
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    $service = new \App\Service\CustomService();
    echo $service ->getBaseURL();
}

Conclusion

Small Thing but powerful feature of Laravel is Macro. We checked simple to advance example that we can use any of our projects. Its have one drawback that if someone new with Laravel he/she will confuse where are extra methods are coming from. But Its has one more benefits that we don’t need to create extra classes for a single method.

Hope you enjoy this article comment to show your love ❤️❤️❤️❤️❤️

Leave a Comment