> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP-attributes

> Hoe je met de in Laravel 13 geïntroduceerde en uitgebreide PHP-attributes de configuratie van jobs en modellen declaratiever beschrijft.

## Wat zijn PHP-attributes?

PHP-attributes zijn de native metadatasyntaxis die in PHP 8.0 is geïntroduceerd. Je kunt meta-informatie toevoegen aan klassen, methodes, properties, functies en meer in het formaat `#[AttributeName]`.

Laravel omarmt PHP-attributes actief in het framework zelf, zodat je de configuratie van jobs en Eloquent-modellen declaratief kunt beschrijven. In Laravel 13 (v13.2.0) accepteren de queue-attributes nu ook enums. In plaats van de traditionele klasseproperties of method overrides schrijf je met attributes beter leesbare en beknoptere code.

```php theme={null}
// De traditionele schrijfwijze
class ProcessOrder implements ShouldQueue
{
    public string $queue = 'orders';
    public string $connection = 'redis';
    public int $tries = 3;
    public array $backoff = [30, 60, 120];
}

// De schrijfwijze met attributes
#[Queue('orders')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
}
```

<Tip>
  Attributes zijn beschikbaar vanaf PHP 8.0. Omdat Laravel 13 minimaal PHP 8.3 vereist, kun je attributes in alle omgevingen gebruiken.
</Tip>

## Queue-gerelateerde attributes

Alle attributes voor queue-jobs zitten in de namespace `Illuminate\Queue\Attributes`.

### `#[Queue]` — de queuenaam opgeven

Geeft de standaardqueue op waarnaar de job wordt gestuurd.

```php theme={null}
use Illuminate\Queue\Attributes\Queue;

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

    public function handle(): void
    {
        // ...
    }
}
```

Vanaf v13.2.0 kun je in plaats van een string ook een enum doorgeven.

```php theme={null}
enum QueueName: string
{
    case Emails = 'emails';
    case Orders = 'orders';
    case Notifications = 'notifications';
}

#[Queue(QueueName::Emails)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
```

<Info>
  Het `#[Queue]`-attribute heeft `Attribute::TARGET_CLASS` als target en kan dus alleen op klassen worden toegepast.
</Info>

### `#[Connection]` — de connectie opgeven

Geeft de standaard-queueconnectie op die de job gebruikt.

```php theme={null}
use Illuminate\Queue\Attributes\Connection;

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

    public function handle(): void
    {
        // ...
    }
}
```

Ook hier kun je enums gebruiken.

```php theme={null}
enum QueueConnection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
}

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

### `#[Backoff]` — de backoff-tijd voor retries opgeven

Geeft de wachttijd (in seconden) op tot een retry wanneer de job faalt. Als je meerdere waarden doorgeeft, kun je per retry een andere wachttijd instellen (met variadische argumenten).

```php theme={null}
use Illuminate\Queue\Attributes\Backoff;

// Vaste wachttijd (60 seconden wachten voor alle retries)
#[Backoff(60)]
class SendEmail implements ShouldQueue
{
    // ...
}

// Verschillende wachttijd per retry (exponentiële backoff)
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
```

Als je de implementatie van de `Backoff`-klasse bekijkt, zie je dat die variadische argumenten accepteert.

```php theme={null}
// Implementatie van 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;
    }
}
```

Bij één waarde wordt het als `int` opgeslagen, bij meerdere waarden als `array`.

### `#[Tries]` — het aantal retries opgeven

Geeft het maximale aantal retries op wanneer de job faalt.

```php theme={null}
use Illuminate\Queue\Attributes\Tries;

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

### `#[Timeout]` — een timeout opgeven

Geeft de maximale uitvoeringstijd van de job op (in seconden). Wordt deze tijd overschreden, dan wordt de job geforceerd beëindigd.

```php theme={null}
use Illuminate\Queue\Attributes\Timeout;

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

### `#[MaxExceptions]` — het toegestane aantal exceptions opgeven

Als er meer exceptions dan het opgegeven aantal optreden, wordt de job als mislukt beschouwd. Je gebruikt dit in combinatie met `#[Tries]`.

```php theme={null}
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Tries;

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

### `#[UniqueFor]` — de uniciteitsperiode opgeven

Geeft de lockperiode (in seconden) op die dubbele uitvoering van de job voorkomt. Je gebruikt dit samen met `ShouldBeUnique`.

```php theme={null}
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\UniqueFor;

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

### `#[DeleteWhenMissingModels]` — verwijderen bij ontbrekend model

Als het Eloquent-model waarvan de job afhankelijk is niet wordt gevonden, wordt de job verwijderd (overgeslagen) in plaats van als mislukt behandeld.

```php theme={null}
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;

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

    public function handle(): void
    {
        // Als $order niet bestaat, wordt deze job verwijderd
    }
}
```

### `#[WithoutRelations]` — relaties uitsluiten

Zorgt ervoor dat de relaties van een model niet worden meegenomen bij het serialiseren van de job. Zo houd je de data die naar de queue gaat lichtgewicht.

```php theme={null}
use Illuminate\Queue\Attributes\WithoutRelations;

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

### `#[FailOnTimeout]` — falen bij een timeout

Registreert de job als mislukt wanneer er een timeout optreedt (standaard wordt een timeout niet als mislukking geregistreerd).

```php theme={null}
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\Timeout;

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

## Meerdere queue-attributes combineren

Je kunt deze attributes combineren om het gedrag van je job declaratief te configureren.

```php theme={null}
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);
    }
}
```

## Eloquent-gerelateerde attributes

De attributes voor Eloquent-modellen zitten in de namespace `Illuminate\Database\Eloquent\Attributes`. In Laravel 13 zijn er veel attributes toegevoegd.

### `#[ScopedBy]` — een global scope opgeven

Geeft via een attribute de global-scopeklasse op die automatisch op het model wordt toegepast. Overerving wordt ondersteund en met de `IS_REPEATABLE`-vlag kun je meerdere scopes opgeven.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\ScopedBy;

#[ScopedBy(ActiveScope::class)]
class User extends Model
{
    // Je hoeft de scope niet meer te registreren in booted()
}
```

Wil je meerdere scopes toevoegen, dan herhaal je het attribute of geef je een array door.

```php theme={null}
// Herhaald opgeven (ondersteunt IS_REPEATABLE)
#[ScopedBy(ActiveScope::class)]
#[ScopedBy(VerifiedScope::class)]
class User extends Model
{
}

// In één keer opgeven met een array
#[ScopedBy([ActiveScope::class, VerifiedScope::class])]
class User extends Model
{
}
```

Ter vergelijking met de traditionele `booted()`-methode:

```php theme={null}
// De traditionele schrijfwijze
class User extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveScope);
        static::addGlobalScope(new VerifiedScope);
    }
}
```

### `#[ObservedBy]` — een observer opgeven

Geeft via een attribute de observerklasse op die aan het model wordt gekoppeld. Net als `ScopedBy` is dit `IS_REPEATABLE`.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
}
```

Je kunt ook meerdere observers opgeven.

```php theme={null}
#[ObservedBy(UserObserver::class)]
#[ObservedBy(AuditObserver::class)]
class User extends Model
{
}
```

De traditionele registratie in de `AppServiceProvider` is niet meer nodig.

```php theme={null}
// De traditionele schrijfwijze (AppServiceProvider)
public function boot(): void
{
    User::observe(UserObserver::class);
}
```

### `#[UseEloquentBuilder]` — een custom query builder opgeven

Geeft via een attribute de custom Eloquent-builder op die het model gebruikt.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;

#[UseEloquentBuilder(UserQueryBuilder::class)]
class User extends Model
{
}
```

```php theme={null}
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');
    }
}
```

```php theme={null}
// Gebruiksvoorbeeld (custom methodes typeveilig aanroepen)
$users = User::query()->active()->verified()->get();
```

### `#[CollectedBy]` — een custom collection opgeven

Geeft via een attribute de custom collectionklasse op die als collectie van het model wordt gebruikt.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\CollectedBy;

#[CollectedBy(UserCollection::class)]
class User extends Model
{
}
```

```php theme={null}
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]` — tabelinstellingen in één keer opgeven

Met één attribute geef je meerdere tabelgerelateerde instellingen op, zoals de tabelnaam, primary key en timestamps.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\Table;

#[Table(name: 'system_users', key: 'user_id', timestamps: false)]
class SystemUser extends Model
{
}
```

De opties die je met het `Table`-attribute kunt instellen:

| Parameter      | Bijbehorende property | Toelichting                                         |
| -------------- | --------------------- | --------------------------------------------------- |
| `name`         | `$table`              | Tabelnaam                                           |
| `key`          | `$primaryKey`         | Kolomnaam van de primary key                        |
| `keyType`      | `$keyType`            | Type van de primary key (`'int'`, `'string'`, enz.) |
| `incrementing` | `$incrementing`       | Auto-increment van de primary key                   |
| `timestamps`   | `$timestamps`         | Timestamps in-/uitschakelen                         |
| `dateFormat`   | `$dateFormat`         | Datumformaat                                        |

### `#[Scope]` — een methode definiëren als local scope

Je kunt een methode zonder het `scope`-prefix definiëren als Eloquent local scope.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\Scope;

class User extends Model
{
    #[Scope]
    protected function active(Builder $query): void
    {
        $query->where('active', true);
    }

    #[Scope]
    protected function verified(Builder $query): void
    {
        $query->whereNotNull('email_verified_at');
    }
}
```

```php theme={null}
// Vroeger was het prefix "scope" in de methodenaam vereist
// public function scopeActive(Builder $query): void

// Met het attribute wordt de methodenaam meteen de scopenaam
User::query()->active()->verified()->get();
```

### `#[UseFactory]` — een factoryklasse opgeven

Geeft via een attribute de custom factoryklasse op die het model gebruikt.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\UseFactory;

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

### Overige Eloquent-attributes

| Attribute                                                  | Toelichting                                                           |
| ---------------------------------------------------------- | --------------------------------------------------------------------- |
| `#[Fillable(...$attributes)]`                              | Kolommen opgeven die zijn toegestaan bij mass assignment              |
| `#[Guarded(...$attributes)]`                               | Kolommen opgeven die worden beschermd bij mass assignment             |
| `#[Unguarded]`                                             | De bescherming van mass assignment uitschakelen                       |
| `#[Hidden(...$attributes)]`                                | Kolommen opgeven die bij serialisatie worden uitgesloten              |
| `#[Visible(...$attributes)]`                               | Kolommen opgeven die bij serialisatie worden meegenomen               |
| `#[Appends(...$attributes)]`                               | Accessors opgeven die bij serialisatie worden toegevoegd              |
| `#[Touches(...$relations)]`                                | Relaties opgeven waarvan `updated_at` bij een update wordt bijgewerkt |
| `#[WithoutTimestamps]`                                     | Timestamps uitschakelen                                               |
| `#[WithoutIncrementing]`                                   | Auto-increment van de primary key uitschakelen                        |
| `#[DateFormat(format: '...')]`                             | Het datumformaat opgeven                                              |
| `#[UsePolicy(policyClass: '...')]`                         | De bijbehorende policyklasse opgeven                                  |
| `#[UseResource(resourceClass: '...')]`                     | De bijbehorende API-resourceklasse opgeven                            |
| `#[UseResourceCollection(resourceCollectionClass: '...')]` | De bijbehorende resource-collectionklasse opgeven                     |

## Enum-ondersteuning (toegevoegd in v13.2.0)

In v13.2.0 accepteren `#[Queue]` en `#[Connection]` nu enums. Daardoor kun je queues en connecties typeveilig opgeven met PHP-enums in plaats van stringliterals.

```php theme={null}
// Enum-definitie voor queuenamen
enum Queue: string
{
    case Default = 'default';
    case High = 'high';
    case Low = 'low';
    case Emails = 'emails';
    case Orders = 'orders';
}

// Enum-definitie voor connecties
enum Connection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
    case Sync = 'sync';
}
```

```php theme={null}
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Connection;

// Typeveilig opgeven met enums
#[Queue(Queue::Orders)]
#[Connection(Connection::Redis)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
```

<Tip>
  Met enums voorkom je typefouten in queue- en connectienamen en profiteer je van IDE-autocomplete. Handig om queue- en connectienamen centraal te beheren voor de hele applicatie.
</Tip>

## Vergelijking met traditionele klasseproperties

### Voordelen van attributes

* **Declaratief** — één blik op het begin van de klasse laat zien hoe de job zich gedraagt
* **Typeveilig** — met enums profiteer je van IDE-autocomplete en typechecks
* **Goede samenwerking met overerving** — attributes van de ouderklasse kun je in de kindklasse overschrijven
* **Minder code** — geen property-declaraties of method overrides nodig

### Nadelen van attributes

* **Geen dynamische waarden mogelijk** — argumenten van attributes zijn uitsluitend compile-time constanten. Variabelen of waarden uit configuratiebestanden kun je niet gebruiken
* **Gewenning nodig** — je team moet mogelijk wennen aan de attribute-syntaxis van PHP 8

### Wanneer je dynamische waarden nodig hebt

Wil je een waarde tijdens runtime bepalen, dan gebruik je de traditionele method override.

```php theme={null}
class ProcessOrder implements ShouldQueue
{
    // Een dynamische backoff definieer je met een methode
    public function backoff(): array
    {
        return [
            config('queue.backoff.first'),
            config('queue.backoff.second'),
        ];
    }
}
```

<Warning>
  Attributes worden geanalyseerd tijdens het compileren van PHP. Runtime-waarden zoals `config()` of `env()` kun je niet gebruiken. Heb je dynamische configuratie nodig, blijf dan klasseproperties of methodes gebruiken.
</Warning>

## Hoe de implementatie werkt

Laravel gebruikt intern de Reflection API om attributes uit te lezen. Wanneer de queue worker een job dispatcht, detecteert de trait `ReadsQueueAttributes` (onderdeel van `InteractsWithQueue`) de attributes via reflectie en zet de waarden op de bijbehorende properties.

```php theme={null}
// Beeld van het interne uitlezen (vereenvoudigd)
$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;
}
```

Ook de attributes van Eloquent-modellen worden op een vergelijkbaar moment — vergelijkbaar met `Model::booted()` — via reflectie uitgelezen.

## Volgende stappen

<Columns cols={2}>
  <Card title="Gemiddeld niveau: queues en jobs" icon="layer-group" href="/nl/queues">
    Leer het basisgebruik van het queuesysteem van Laravel.
  </Card>

  <Card title="PHP Reflection API" icon="magnifying-glass" href="/nl/advanced/php-reflection">
    Een gedetailleerde uitleg van de Reflection API die Laravel gebruikt om attributes uit te lezen.
  </Card>
</Columns>


## Related topics

- [PHP-attributes voor controllers](/nl/advanced/controller-attributes.md)
- [PHP Reflection API](/nl/advanced/php-reflection.md)
- [Eloquent bootable traits](/nl/advanced/eloquent-bootable-traits.md)
- [Laravel-updates van maart 2026](/nl/blog/changelog/202603.md)
- [Laravel AI SDK](/nl/ai-sdk.md)
