Il trait Macroable permette di aggiungere dinamicamente metodi a una classe, senza modificarla. Molte delle classi core di Laravel usano questo trait, quindi puoi estenderne le funzionalità senza toccare il codice del core.Il trait vero e proprio si trova in Illuminate\Support\Traits\Macroable. Internamente memorizza le macro registrate nella proprietà statica $macros e le richiama tramite le magic method __call / __callStatic.
Il $this all’interno della closure viene collegato all’istanza che ha invocato la macro. In questo modo puoi accedere direttamente a proprietà e metodi della classe.
Se raggruppi in macro le definizioni delle colonne, mantieni un design del DB uniforme.
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();});
// utilizzo in una migrazioneSchema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body'); $table->addTimestampsWithTimezone(); $table->addUserTracking();});
use Illuminate\Support\Collection;// reset dentro un testCollection::flushMacros();
flushMacros() rimuove tutte le macro della classe. Talvolta viene chiamato in tearDown() per garantire l’indipendenza tra i test, ma attenzione: cancella anche macro registrate da altri test.
// implementazione di __call (chiamata a metodo d'istanza)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) { // con bindTo collega $this all'istanza $macro = $macro->bindTo($this, static::class); } return $macro(...$parameters);}
La closure viene collegata all’istanza con Closure::bindTo(). In questo modo $this fa riferimento all’oggetto che ha chiamato la macro. Per elementi non closure (ad esempio oggetti invokable) il bind non viene eseguito.
Per ottenere supporto dall’IDE puoi definire le annotazioni delle macro con un Docblock che usa @mixin, oppure generare automaticamente un file helper con il pacchetto Laravel IdeHelper.