Saltar al contenido principal

Qué son los atributos de PHP

Los atributos de PHP (PHP Attributes) son una sintaxis nativa de metadatos introducida en PHP 8.0. Permiten asignar información meta a clases, métodos, propiedades o funciones con la forma #[AttributeName]. Laravel adopta ampliamente los atributos de PHP en el framework y permite declarar de forma más expresiva la configuración de jobs y modelos Eloquent. En Laravel 13 (v13.2.0), los atributos de cola aceptan enums. En lugar de propiedades de clase o de sobrescribir métodos, puedes usar atributos para escribir código más legible y conciso.
// Forma tradicional
class ProcessOrder implements ShouldQueue
{
    public string $queue = 'orders';
    public string $connection = 'redis';
    public int $tries = 3;
    public array $backoff = [30, 60, 120];
}

// Con atributos
#[Queue('orders')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
}
Los atributos están disponibles desde PHP 8.0. Como Laravel 13 requiere PHP 8.3 o superior, puedes usarlos en cualquier entorno soportado.

Atributos relacionados con colas

Todos los atributos relacionados con jobs están en el namespace Illuminate\Queue\Attributes.

#[Queue] — nombre de la cola

Indica el nombre por defecto de la cola a la que se envía el job.
use Illuminate\Queue\Attributes\Queue;

#[Queue('emails')]
class SendWelcomeEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
Desde v13.2.0 también puedes pasar un enum en lugar de una cadena.
enum QueueName: string
{
    case Emails = 'emails';
    case Orders = 'orders';
    case Notifications = 'notifications';
}

#[Queue(QueueName::Emails)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
El atributo #[Queue] está declarado con Attribute::TARGET_CLASS, por lo que solo se puede aplicar a clases.

#[Connection] — indicar la conexión

Indica la conexión de cola por defecto que usará el job.
use Illuminate\Queue\Attributes\Connection;

#[Connection('sqs')]
class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
También admite enums.
enum QueueConnection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
}

#[Connection(QueueConnection::Sqs)]
class ProcessPayment implements ShouldQueue
{
    // ...
}

#[Backoff] — tiempo de espera entre reintentos

Indica el tiempo de espera (en segundos) hasta el siguiente reintento cuando falla el job. Aceptando varios valores, puedes configurar tiempos distintos por reintento (variadic).
use Illuminate\Queue\Attributes\Backoff;

// Tiempo fijo para todos los reintentos (60 s)
#[Backoff(60)]
class SendEmail implements ShouldQueue
{
    // ...
}

// Diferente por reintento (backoff exponencial)
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
Al ver la implementación de Backoff, se aprecia que está diseñado para aceptar argumentos variádicos.
// Implementación de 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;
    }
}
Si hay un solo valor, se guarda como int; si hay varios, como array.

#[Tries] — número de reintentos

Indica el número máximo de reintentos al fallar el job.
use Illuminate\Queue\Attributes\Tries;

#[Tries(5)]
class ProcessPayment implements ShouldQueue
{
    // ...
}

#[Timeout] — indicar el timeout

Indica el tiempo máximo de ejecución (en segundos) del job. Si se supera, se aborta forzosamente.
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(120)]
class GenerateReport implements ShouldQueue
{
    // ...
}

#[MaxExceptions] — número máximo de excepciones toleradas

Considera fallido el job si ocurren más excepciones de las indicadas. Se usa junto a #[Tries].
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Tries;

#[Tries(10)]
#[MaxExceptions(3)]
class ProcessWebhook implements ShouldQueue
{
    // ...
}

#[UniqueFor] — periodo de unicidad

Indica el periodo de bloqueo (en segundos) para evitar la ejecución duplicada del job. Se combina con ShouldBeUnique.
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\UniqueFor;

#[UniqueFor(3600)]
class SyncUserData implements ShouldQueue, ShouldBeUnique
{
    // ...
}

#[DeleteWhenMissingModels] — eliminar cuando no exista el modelo

Si el modelo Eloquent del que depende el job no existe, se trata como eliminado (se salta) en lugar de fallido.
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;

#[DeleteWhenMissingModels]
class SendOrderConfirmation implements ShouldQueue
{
    public function __construct(
        protected Order $order,
    ) {}

    public function handle(): void
    {
        // Si $order no existe, este job se elimina
    }
}

#[WithoutRelations] — excluir relaciones

Evita incluir las relaciones del modelo al serializar el job. Reduce el peso de los datos enviados a la cola.
use Illuminate\Queue\Attributes\WithoutRelations;

#[WithoutRelations]
class ExportUser implements ShouldQueue
{
    public function __construct(
        protected User $user,
    ) {}
}

#[FailOnTimeout] — marcar como fallo al agotarse el tiempo

Registra el timeout como fallo (por defecto no se registra como tal).
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(30)]
#[FailOnTimeout]
class ProcessLongTask implements ShouldQueue
{
    // ...
}

Combinar varios atributos de cola

Puedes combinar estos atributos para declarar de forma expresiva el comportamiento 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);
    }
}

Atributos relacionados con Eloquent

Los atributos para modelos Eloquent están en el namespace Illuminate\Database\Eloquent\Attributes. Laravel 13 añade muchos atributos.

#[ScopedBy] — indicar un global scope

Indica mediante atributo la clase de scope global que se aplica automáticamente al modelo. Admite herencia y, gracias al flag IS_REPEATABLE, varios scopes.
use Illuminate\Database\Eloquent\Attributes\ScopedBy;

#[ScopedBy(ActiveScope::class)]
class User extends Model
{
    // Ya no hace falta registrar el scope en booted()
}
Para varios scopes, repite el atributo o pásalos como array.
// Repitiendo (compatible con IS_REPEATABLE)
#[ScopedBy(ActiveScope::class)]
#[ScopedBy(VerifiedScope::class)]
class User extends Model
{
}

// En un solo array
#[ScopedBy([ActiveScope::class, VerifiedScope::class])]
class User extends Model
{
}
Comparación con la forma tradicional en booted().
// Forma tradicional
class User extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveScope);
        static::addGlobalScope(new VerifiedScope);
    }
}

#[ObservedBy] — indicar un observer

Indica mediante atributo la clase observer asociada al modelo. También es IS_REPEATABLE, como ScopedBy.
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
}
Puedes indicar varios observers.
#[ObservedBy(UserObserver::class)]
#[ObservedBy(AuditObserver::class)]
class User extends Model
{
}
Ya no es necesario el registro tradicional en AppServiceProvider.
// Forma tradicional (AppServiceProvider)
public function boot(): void
{
    User::observe(UserObserver::class);
}

#[UseEloquentBuilder] — indicar un query builder personalizado

Indica mediante atributo el Eloquent Builder personalizado que usará el modelo.
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;

#[UseEloquentBuilder(UserQueryBuilder::class)]
class User extends Model
{
}
namespace App\Builders;

use Illuminate\Database\Eloquent\Builder;

class UserQueryBuilder extends Builder
{
    public function active(): static
    {
        return $this->where('active', true);
    }

    public function verified(): static
    {
        return $this->whereNotNull('email_verified_at');
    }
}
// Uso (puedes llamar a los métodos personalizados con seguridad de tipos)
$users = User::query()->active()->verified()->get();

#[CollectedBy] — indicar una collection personalizada

Indica mediante atributo la clase de collection personalizada que representa las colecciones del modelo.
use Illuminate\Database\Eloquent\Attributes\CollectedBy;

#[CollectedBy(UserCollection::class)]
class User extends Model
{
}
namespace App\Collections;

use Illuminate\Database\Eloquent\Collection;

class UserCollection extends Collection
{
    public function admins(): static
    {
        return $this->filter(fn (User $user) => $user->is_admin);
    }

    public function active(): static
    {
        return $this->filter(fn (User $user) => $user->active);
    }
}

#[Table] — configuración de tabla en un solo atributo

Indica en un único atributo varios ajustes relacionados con la tabla: nombre, clave primaria, timestamps, etc.
use Illuminate\Database\Eloquent\Attributes\Table;

#[Table(name: 'system_users', key: 'user_id', timestamps: false)]
class SystemUser extends Model
{
}
Las opciones que puedes configurar con el atributo Table:
ParámetroPropiedad equivalenteDescripción
name$tableNombre de la tabla.
key$primaryKeyNombre de la columna de clave primaria.
keyType$keyTypeTipo de la clave primaria ('int', 'string'…).
incrementing$incrementingSi la clave primaria es autoincremental.
timestamps$timestampsActivar/desactivar timestamps.
dateFormat$dateFormatFormato de fecha.

#[Scope] — definir un método como local scope

Permite declarar un método como local scope de Eloquent sin necesidad del prefijo 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');
    }
}
// Antes se necesitaba el prefijo "scope"
// public function scopeActive(Builder $query): void

// Con el atributo, el nombre del método es directamente el del scope
User::query()->active()->verified()->get();

#[UseFactory] — indicar la clase factory

Indica mediante atributo la clase factory personalizada que usará el modelo.
use Illuminate\Database\Eloquent\Attributes\UseFactory;

#[UseFactory(UserFactory::class)]
class User extends Model
{
}

Otros atributos de Eloquent

AtributoDescripción
#[Fillable(...$attributes)]Columnas permitidas en asignación masiva.
#[Guarded(...$attributes)]Columnas protegidas frente a asignación masiva.
#[Unguarded]Desactiva la protección de asignación masiva.
#[Hidden(...$attributes)]Columnas excluidas al serializar.
#[Visible(...$attributes)]Columnas incluidas al serializar.
#[Appends(...$attributes)]Accesores que se añaden al serializar.
#[Touches(...$relations)]Relaciones cuyo updated_at se actualiza al modificar.
#[WithoutTimestamps]Desactiva los timestamps.
#[WithoutIncrementing]Desactiva el auto-incremento de la clave primaria.
#[DateFormat(format: '...')]Indica el formato de fecha.
#[UsePolicy(policyClass: '...')]Indica la clase policy asociada.
#[UseResource(resourceClass: '...')]Indica la clase resource API asociada.
#[UseResourceCollection(resourceCollectionClass: '...')]Indica la clase de resource collection asociada.

Soporte de enums (añadido en v13.2.0)

En v13.2.0, #[Queue] y #[Connection] aceptan enums. Así puedes indicar cola y conexión de forma tipada, en lugar de con literales string.
// Definición del enum de cola
enum Queue: string
{
    case Default = 'default';
    case High = 'high';
    case Low = 'low';
    case Emails = 'emails';
    case Orders = 'orders';
}

// Definición del enum de conexión
enum Connection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
    case Sync = 'sync';
}
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Connection;

// Especificación tipada con enums
#[Queue(Queue::Orders)]
#[Connection(Connection::Redis)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
Con enums evitas errores tipográficos en nombres de cola o conexión y aprovechas el autocompletado del IDE. Es muy útil para gestionar de forma centralizada los nombres de cola y conexión en toda la aplicación.

Comparación con las propiedades de clase tradicionales

Ventajas de los atributos

  • Declarativos — con solo mirar el encabezado de la clase entiendes el comportamiento del job.
  • Tipados — al usar enums, tienes autocompletado y comprobación de tipos.
  • Compatibles con la herencia — puedes sobrescribir en la subclase los atributos de la clase padre.
  • Menos código — no hacen falta declaraciones de propiedades ni sobrescribir métodos.

Desventajas de los atributos

  • No se puede fijar un valor dinámico — los argumentos son constantes de compilación. No aceptan variables ni valores de configuración.
  • Requiere costumbre — puede que tu equipo necesite tiempo para acostumbrarse a la sintaxis de PHP 8.

Cuando necesitas valores dinámicos

Si necesitas decidir el valor en tiempo de ejecución, sigue usando la sobrescritura de métodos.
class ProcessOrder implements ShouldQueue
{
    // Backoff dinámico definido en un método
    public function backoff(): array
    {
        return [
            config('queue.backoff.first'),
            config('queue.backoff.second'),
        ];
    }
}
Los atributos se analizan en tiempo de compilación de PHP. No puedes usar valores en tiempo de ejecución como config() o env(). Si necesitas configuración dinámica, sigue empleando propiedades o métodos.

Cómo se implementa

Internamente, Laravel usa la Reflection API para leer los atributos. Cuando el worker de la cola despacha un job, el trait ReadsQueueAttributes (incluido en InteractsWithQueue) los detecta mediante reflexión y les asigna los valores a las propiedades correspondientes.
// Imagen simplificada de la lectura interna
$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;
}
Los atributos de los modelos Eloquent se leen por reflexión de forma análoga, aproximadamente en el momento de Model::booted().

Próximos pasos

Intermedio: Colas y jobs

Aprende el uso básico del sistema de colas de Laravel.

PHP Reflection API

Explica en detalle cómo Laravel usa la Reflection API para leer los atributos.
Última modificación el 13 de julio de 2026