The Magic of Laravel Macros

Feature image: The Magic of Laravel Macros

Ever wanted a piece of functionality in a part of Laravel that doesn’t exist? Let me introduce you to Laravel macros. Macros allow you to add on custom functionality to internal Laravel components.

Let’s start with a simple example on the Request facade. php

Request::macro('introduce', function ($name) {
echo 'Hello ' . $name . '!';
});
 
Request::introduce('Caleb'); // outputs "Hello Caleb!"

A more practical example of a Request macro would be detecting the current TLD (Top Level Domain: .com, .net, .org, etc…).

Request::macro('tldIs', function ($tld) {
return Str::is('*.' . $tld, $this->root());
});
 
Request::tldIs('com') // returns true for app.com
Request::tldIs('dev') // returns false for app.com

You’ll notice Laravel automatically binds $this to the context of Request rather than the class where the macro is defined. For example:

class AppServiceProvider
{
public function boot()
{
Request::macro('context', function () {
return get_class($this);
}
}
...
 
Request::context();
// returns 'Illuminate\Http\Request'
// instead of 'App\AppServiceProvider'

Let’s look at a more advanced example. This macro conditionally adds a where statement on the model based on the current TLD.

Builder::macro('whenTldMatches', function($tld, $callback) {
if (Request::tldIs($tld)) {
call_user_func($callback->bindTo($this));
}
return $this;
});
 
SomeModel::whenTldMatches('org', function () {
$this->where('id', '>', 5);
})->get();
 
// applies ->where() on app.org but not app.com

Where should I define them?

Service providers are a great place to define macros for your app. App\Providers\AppServiceProvider boot() is a good starting point, but can quickly become bloated. The next step is to create a App\Providers\MacrosServiceProvider and register it in config/app.php. If certain macros are related, such as the examples above, I might create a App\Providers\TldAwareServiceProvider to house all TLD related macros.

Which components are “Macroable”?

Macros can be defined on any class with the Macroable trait. Below is a list of Macroable facades & classes:

Facades

  • Cache
  • File
  • Lang
  • Request
  • Response
  • Route
  • URL

Illuminate Classes

  • Illuminate\Cache\Repository
  • Illuminate\Console\Scheduling\Event
  • Illuminate\Database\Eloquent\Builder
  • Illuminate\Database\Eloquent\Relation
  • Illuminate\Database\Query\Builder
  • Illuminate\Filesystem\Filesystem
  • Illuminate\Foundation\Testing\TestResponse
  • Illuminate\Http\RedirectResponse
  • Illuminate\Http\Request
  • Illuminate\Http\UploadedFile
  • Illuminate\Routing\ResponseFactory
  • Illuminate\Routing\Router
  • Illuminate\Routing\UrlGenerator
  • Illuminate\Support\Arr
  • Illuminate\Support\Collection
  • Illuminate\Support\Str
  • Illuminate\Translation\Translator
  • Illuminate\Validation\Rule

Takeaway

If you find yourself repeating performing logic on Laravel components throughout your system, think about using a macro for better expression and reuse. Trust me, they’re addicting.

Good Luck!

Get our latest insights in your inbox:

By submitting this form, you acknowledge our Privacy Notice.

Hey, let’s talk.

By submitting this form, you acknowledge our Privacy Notice.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Thank you!

We appreciate your interest. We will get right back to you.