The Macroable trait lets you attach new methods to a class at runtime without modifying the class itself. Many of Laravel’s core classes use this trait, so you can extend them freely without forking or overriding core code.The implementation lives in Illuminate\Support\Traits\Macroable. Registered macros are stored in the static $macros property and invoked through the __call / __callStatic magic methods.
// In a controllerpublic function index(Request $request){ if ($request->isFromMobile()) { return response()->json($this->getMobileData()); } $locale = $request->preferredLocale(['en', 'fr', 'de']); // ...}
Grouping common column patterns into a macro keeps your schema definitions consistent.
use Illuminate\Database\Schema\Blueprint;Blueprint::macro('addTimestampsWithTimezone', function () { /** @var Blueprint $this */ $this->timestampsTz(); $this->softDeletesTz();});Blueprint::macro('addUserTracking', function () { /** @var Blueprint $this */ $this->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); $this->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();});
// In a migrationSchema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body'); $table->addTimestampsWithTimezone(); $table->addUserTracking();});
Use this in tests when you need a clean slate between test cases.
use Illuminate\Support\Collection;Collection::flushMacros();
flushMacros() removes every macro registered on that class. If you call it in tearDown(), macros registered by other tests or service providers will also be removed.
// __call implementation (instance method calls)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) { // bindTo makes $this point to the calling instance $macro = $macro->bindTo($this, static::class); } return $macro(...$parameters);}
Closures are bound using Closure::bindTo(), which makes $this refer to the object that called the macro. Non-closure callables (invokable objects) are not bound.
For IDE auto-completion, annotate your macros with @method docblocks, or use the Laravel IDE Helper package to auto-generate helper files.