Gli attributi PHP sono una sintassi nativa di metadati introdotta con PHP 8.0. Permettono di applicare meta-informazioni a classi, metodi, proprietà, funzioni ecc. nella forma #[AttributeName].Laravel adotta attivamente gli attributi PHP nel framework, e ti permette di configurare in modo dichiarativo job e modelli Eloquent. In Laravel 13 (v13.2.0) gli attributi per le queue accettano enum. Al posto delle proprietà di classe o degli override di metodi puoi scrivere codice più leggibile e conciso.
// scrittura tradizionaleclass ProcessOrder implements ShouldQueue{ public string $queue = 'orders'; public string $connection = 'redis'; public int $tries = 3; public array $backoff = [30, 60, 120];}// scrittura con attributi#[Queue('orders')]#[Connection('redis')]#[Tries(3)]#[Backoff(30, 60, 120)]class ProcessOrder implements ShouldQueue{}
Gli attributi sono disponibili da PHP 8.0. Laravel 13 richiede PHP 8.3 o superiore, quindi puoi usare gli attributi in tutti gli ambienti.
Indica il nome della coda di default in cui il job viene inviato.
use Illuminate\Queue\Attributes\Queue;#[Queue('emails')]class SendWelcomeEmail implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle(): void { // ... }}
Da v13.2.0 puoi passare anche un enum al posto della stringa.
enum QueueName: string{ case Emails = 'emails'; case Orders = 'orders'; case Notifications = 'notifications';}#[Queue(QueueName::Emails)]class SendWelcomeEmail implements ShouldQueue{ // ...}
L’attributo #[Queue] è impostato con target Attribute::TARGET_CLASS, quindi è applicabile solo alle classi.
Indica la connection di coda di default usata dal job.
use Illuminate\Queue\Attributes\Connection;#[Connection('sqs')]class ProcessPayment implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle(): void { // ... }}
Anche qui puoi usare un enum.
enum QueueConnection: string{ case Redis = 'redis'; case Sqs = 'sqs'; case Database = 'database';}#[Connection(QueueConnection::Sqs)]class ProcessPayment implements ShouldQueue{ // ...}
Indica il tempo di attesa (in secondi) prima di ritentare quando il job fallisce. Passando più valori puoi impostare un tempo diverso per ogni retry (supporta argomenti variadic).
use Illuminate\Queue\Attributes\Backoff;// attesa fissa (60 secondi comune a tutti i retry)#[Backoff(60)]class SendEmail implements ShouldQueue{ // ...}// attese diverse per ogni retry (exponential backoff)#[Backoff(30, 60, 120)]class ProcessOrder implements ShouldQueue{ // ...}
Guardando l’implementazione della classe Backoff è progettata per ricevere argomenti variadic.
// implementazione di Illuminate\Queue\Attributes\Backoff#[Attribute(Attribute::TARGET_CLASS)]class Backoff{ public array|int $backoff; public function __construct(array|int ...$backoff) { $this->backoff = count($backoff) === 1 ? $backoff[0] : $backoff; }}
Un singolo valore viene memorizzato come int, più valori come array.
#[DeleteWhenMissingModels] — cancella se il modello non esiste
Se un modello Eloquent da cui dipende il job non viene trovato, il job viene cancellato (saltato) anziché fallito.
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;#[DeleteWhenMissingModels]class SendOrderConfirmation implements ShouldQueue{ public function __construct( protected Order $order, ) {} public function handle(): void { // se $order non esiste, questo job viene cancellato }}
Fa in modo che, al momento della serializzazione, le relation del modello non vengano incluse. Alleggerisce i dati inviati in coda.
use Illuminate\Queue\Attributes\WithoutRelations;#[WithoutRelations]class ExportUser implements ShouldQueue{ public function __construct( protected User $user, ) {}}
Combinando questi attributi configuri in modo dichiarativo il comportamento del job.
use Illuminate\Queue\Attributes\Backoff;use Illuminate\Queue\Attributes\Connection;use Illuminate\Queue\Attributes\DeleteWhenMissingModels;use Illuminate\Queue\Attributes\FailOnTimeout;use Illuminate\Queue\Attributes\MaxExceptions;use Illuminate\Queue\Attributes\Queue;use Illuminate\Queue\Attributes\Timeout;use Illuminate\Queue\Attributes\Tries;#[Queue('payments')]#[Connection('redis')]#[Tries(3)]#[Backoff(30, 60, 120)]#[Timeout(60)]#[MaxExceptions(2)]#[DeleteWhenMissingModels]class ProcessPayment implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct( protected Order $order, ) {} public function handle(PaymentService $payment): void { $payment->charge($this->order); }}
Indica con un attributo la classe di global scope da applicare automaticamente al modello. Supporta l’ereditarietà; con il flag IS_REPEATABLE puoi indicare più scope.
use Illuminate\Database\Eloquent\Attributes\ScopedBy;#[ScopedBy(ActiveScope::class)]class User extends Model{ // non serve più registrare lo scope in booted()}
Per più scope puoi ripetere l’attributo o passare un array.
// specifica ripetuta (supporta IS_REPEATABLE)#[ScopedBy(ActiveScope::class)]#[ScopedBy(VerifiedScope::class)]class User extends Model{}// specifica raggruppata in array#[ScopedBy([ActiveScope::class, VerifiedScope::class])]class User extends Model{}
Confronto con il metodo booted() tradizionale.
// scrittura tradizionaleclass User extends Model{ protected static function booted(): void { static::addGlobalScope(new ActiveScope); static::addGlobalScope(new VerifiedScope); }}
Puoi definire un metodo come local scope Eloquent senza prefisso scope.
use Illuminate\Database\Eloquent\Attributes\Scope;class User extends Model{ #[Scope] public function active(Builder $query): void { $query->where('active', true); } #[Scope] public function verified(Builder $query): void { $query->whereNotNull('email_verified_at'); }}
// in passato serviva il prefisso "scope" nel nome// public function scopeActive(Builder $query): void// con l'attributo il nome del metodo è direttamente il nome dello scopeUser::query()->active()->verified()->get();
In v13.2.0 #[Queue] e #[Connection] accettano enum. Al posto delle stringhe letterali puoi indicare queue e connection in modo type-safe con enum PHP.
// definizione enum del nome codaenum Queue: string{ case Default = 'default'; case High = 'high'; case Low = 'low'; case Emails = 'emails'; case Orders = 'orders';}// definizione enum della connectionenum Connection: string{ case Redis = 'redis'; case Sqs = 'sqs'; case Database = 'database'; case Sync = 'sync';}
use Illuminate\Queue\Attributes\Queue;use Illuminate\Queue\Attributes\Connection;// specifica type-safe con enum#[Queue(Queue::Orders)]#[Connection(Connection::Redis)]class ProcessOrder implements ShouldQueue{ // ...}
Con gli enum eviti errori di battitura in nomi di code e connection e sfrutti l’autocompletamento dell’IDE. È utile per gestire in modo centralizzato nomi di coda e connection nell’intera applicazione.
Se vuoi decidere i valori a runtime, usa il tradizionale override di metodo.
class ProcessOrder implements ShouldQueue{ // backoff dinamico definito come metodo public function backoff(): array { return [ config('queue.backoff.first'), config('queue.backoff.second'), ]; }}
Gli attributi vengono analizzati al momento della compilazione di PHP. Non puoi usare valori a runtime come config() o env(). Se ti servono impostazioni dinamiche, continua a usare proprietà o metodi di classe.
Internamente Laravel usa la Reflection API per leggere gli attributi. Quando il queue worker fa dispatch del job, il trait ReadsQueueAttributes (incluso in InteractsWithQueue) individua gli attributi via reflection e imposta i valori sulle proprietà corrispondenti.
// idea della lettura interna (semplificata)$reflection = new ReflectionClass($job);$attributes = $reflection->getAttributes(Queue::class);foreach ($attributes as $attribute) { $instance = $attribute->newInstance(); $job->queue = $instance->queue instanceof UnitEnum ? $instance->queue->value : $instance->queue;}
Anche gli attributi dei modelli Eloquent vengono letti in modo analogo, al momento equivalente a Model::booted().