> ## 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 voor controllers

> Hoe je met de in Laravel 13 toegevoegde attributes #[Middleware], #[WithoutMiddleware] en #[Authorize] middleware en autorisatie voor controllers declaratief instelt.

## Overzicht

In Laravel 13 kun je de toewijzing van middleware en autorisatiecontroles voor controllers declareren met PHP-attributes. In plaats van de traditionele `middleware()`-methode of de `can`-middleware zet je simpelweg attributes op de klasse of methode en is de configuratie compleet.

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class PostController
{
    #[Middleware('subscribed')]
    #[Authorize('create', Post::class)]
    public function store(Request $request): Response
    {
        // ...
    }
}
```

<Tip>
  Alle controller-attributes staan in de namespace `Illuminate\Routing\Attributes\Controllers`.
</Tip>

## `#[Middleware]` — middleware toewijzen

### Toepassing op de klasse

Zet je `#[Middleware]` op klasseniveau, dan wordt de middleware toegepast op alle acties van die controller.

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class UserController
{
    public function index(): View { /* ... */ }
    public function show(User $user): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
}
```

Wil je meerdere middleware toevoegen, dan herhaal je het attribute.

```php theme={null}
#[Middleware('auth')]
#[Middleware('verified')]
class ProfileController
{
    // ...
}
```

### Toepassing op een methode

Middleware op methodeniveau wordt samengevoegd met de middleware op klasseniveau.

```php theme={null}
#[Middleware('auth')]
class UserController
{
    // Voor iedereen alleen auth
    public function index(): View { /* ... */ }

    // Zowel auth als subscribed wordt toegepast
    #[Middleware('subscribed')]
    public function create(): View { /* ... */ }
}
```

### Inperken met `only` / `except`

Geef je bij een attribute op klasseniveau `only` of `except` op, dan kun je de doelmethodes inperken.

```php theme={null}
#[Middleware('auth')]
#[Middleware('subscribed', only: ['create', 'store', 'edit', 'update'])]
class ArticleController
{
    // Alleen auth
    public function index(): View { /* ... */ }
    public function show(Article $article): View { /* ... */ }

    // auth + subscribed
    public function create(): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
    public function edit(Article $article): View { /* ... */ }
    public function update(Request $request, Article $article): Response { /* ... */ }

    // Alleen auth (gelijk aan uitsluiten via except)
    public function destroy(Article $article): Response { /* ... */ }
}
```

### Closure-middleware

Je kunt in attributes ook closures gebruiken. Handig wanneer je de verwerking inline wilt schrijven.

```php theme={null}
use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\Attributes\Controllers\Middleware;

class ReportController
{
    #[Middleware(static function (Request $request, Closure $next) {
        if (! $request->user()->hasRole('analyst')) {
            abort(403);
        }

        return $next($request);
    })]
    public function generate(): Response
    {
        // ...
    }
}
```

### Vergelijking met de traditionele `middleware()`-methode

```php theme={null}
// Traditionele schrijfwijze (implementeert de interface HasMiddleware)
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class UserController implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['index']),
            new Middleware('subscribed', except: ['store']),
        ];
    }
}

// Schrijfwijze met attributes
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // Implementatie van HasMiddleware niet nodig
}
```

<Info>
  Gebruik je attributes, dan hoef je de interface `HasMiddleware` niet te implementeren. Het mengen van de `middleware()`-methode en attributes kan echter tot onbedoeld gedrag leiden, dus we raden aan één van beide consequent te gebruiken.
</Info>

## `#[WithoutMiddleware]` — middleware uitsluiten

Sluit middleware die op klasseniveau is toegepast uit voor een specifieke methode of voor de hele klasse.

### Toepassing op een methode

```php theme={null}
use App\Http\Middleware\EnsureTokenIsValid;
use Illuminate\Routing\Attributes\Controllers\Middleware;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;

#[Middleware('auth')]
#[Middleware(EnsureTokenIsValid::class)]
class ApiController
{
    // auth en EnsureTokenIsValid worden toegepast
    public function show(Resource $resource): JsonResponse { /* ... */ }

    // auth wordt toegepast, maar EnsureTokenIsValid wordt uitgesloten
    #[WithoutMiddleware(EnsureTokenIsValid::class)]
    public function index(): JsonResponse { /* ... */ }
}
```

### Toepassing op de klasse en `only` / `except`

Zet je `#[WithoutMiddleware]` op klasseniveau, dan wordt de middleware uitgesloten voor alle acties, inclusief die van kindklassen. Met `only` / `except` beperk je het bereik van de uitsluiting.

```php theme={null}
#[Middleware('auth')]
#[WithoutMiddleware('subscribed', except: ['index'])]
class AdminController
{
    // subscribed wordt toegepast (niet uitgesloten dankzij except: ['index'])
    public function index(): View { /* ... */ }

    // subscribed wordt uitgesloten
    public function dashboard(): View { /* ... */ }
    public function settings(): View { /* ... */ }
}
```

<Warning>
  `#[WithoutMiddleware]` werkt alleen voor **routemiddleware**. Globale middleware die geregistreerd staat in `app/Http/Kernel.php` kun je niet uitsluiten.
</Warning>

## `#[Authorize]` — autorisatie via policies

Als suikersyntaxis voor de `can`-middleware kun je autorisatiecontroles via policies declareren met een attribute.

### Basisgebruik

```php theme={null}
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class PostController
{
    // Controleert de ability 'viewAny' (de viewAny-methode van de Post-policy)
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    // Controleert de ability 'view' (geeft de routeparameter 'post' door)
    #[Authorize('view', 'post')]
    public function show(Post $post): View { /* ... */ }

    // Controleert de ability 'create'
    #[Authorize('create', Post::class)]
    public function create(): View { /* ... */ }

    #[Authorize('create', Post::class)]
    public function store(Request $request): Response { /* ... */ }

    // Controleert de ability 'update' (geeft de routeparameter door)
    #[Authorize('update', 'post')]
    public function edit(Post $post): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }

    #[Authorize('delete', 'post')]
    public function destroy(Post $post): Response { /* ... */ }
}
```

### Argumenten van de policy

Als tweede argument kun je het volgende doorgeven:

| Waarde                     | Beschrijving                                                                                |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `Post::class`              | De modelklasse (wanneer geen modelinstantie nodig is, zoals bij `viewAny`)                  |
| `'post'`                   | De naam van de routeparameter (de via model binding opgehaalde instantie wordt doorgegeven) |
| `[Comment::class, 'post']` | Meerdere argumenten (worden als array doorgegeven aan de policymethode)                     |

```php theme={null}
use App\Models\Comment;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class CommentController
{
    // Geeft $post (routeparameter) en de Comment-klasse door aan de policy
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post, Request $request): Response
    {
        // ...
    }

    #[Authorize('delete', 'comment')]
    public function destroy(Comment $comment): Response
    {
        // ...
    }
}
```

### Vergelijking met de `can`-middleware

```php theme={null}
// Traditionele schrijfwijze (Route-facade)
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// Schrijfwijze met attributes
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }
}
```

Je hoeft geen middleware meer in het routebestand te zetten en de autorisatielogica wordt gebundeld in de controller.

## Praktijkvoorbeelden

### Toepassing op een resource controller

```php theme={null}
use App\Models\Article;
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class ArticleController
{
    #[Authorize('viewAny', Article::class)]
    public function index(): View
    {
        return view('articles.index', [
            'articles' => Article::paginate(),
        ]);
    }

    #[Authorize('view', 'article')]
    public function show(Article $article): View
    {
        return view('articles.show', compact('article'));
    }

    #[Authorize('create', Article::class)]
    public function create(): View
    {
        return view('articles.create');
    }

    #[Authorize('create', Article::class)]
    public function store(StoreArticleRequest $request): RedirectResponse
    {
        $article = Article::create($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('update', 'article')]
    public function edit(Article $article): View
    {
        return view('articles.edit', compact('article'));
    }

    #[Authorize('update', 'article')]
    public function update(UpdateArticleRequest $request, Article $article): RedirectResponse
    {
        $article->update($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('delete', 'article')]
    public function destroy(Article $article): RedirectResponse
    {
        $article->delete();

        return redirect()->route('articles.index');
    }
}
```

### Toepassing op een API-controller

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Middleware;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;

#[Middleware('auth:sanctum')]
#[Middleware('throttle:api')]
class ApiPostController
{
    // De lijst ophalen kan zonder authenticatie
    #[WithoutMiddleware('auth:sanctum')]
    public function index(): JsonResponse
    {
        return response()->json(Post::paginate());
    }

    public function store(Request $request): JsonResponse
    {
        // Alleen geauthenticeerde gebruikers
    }
}
```

## Verwerkingsvolgorde van attributes

Zijn er meerdere attributes toegepast, dan is de verwerkingsvolgorde als volgt:

```mermaid theme={null}
flowchart LR
    A["Request"] --> B["#[Middleware]<br>op klasseniveau"]
    B --> C["#[Middleware]<br>op methodeniveau"]
    C --> D["Uitsluiting via<br>#[WithoutMiddleware]"]
    D --> E["Autorisatiecontrole via<br>#[Authorize]"]
    E --> F["Uitvoering van de<br>controllermethode"]
```

<Info>
  `#[Authorize]` werkt intern als de `can`-middleware en wordt daarom in dezelfde pipeline verwerkt als `#[Middleware]`.
</Info>

## Samenvatting: welke aanpak kies je?

| Situatie                                            | Aanbevolen aanpak                         |
| --------------------------------------------------- | ----------------------------------------- |
| Nieuwe controller (Laravel 13)                      | Gebruik attributes                        |
| Bestaande controller met een `middleware()`-methode | Stapsgewijs migreren naar attributes      |
| Dynamische middlewareconfiguratie nodig             | Blijf de `middleware()`-methode gebruiken |
| Centraal beheer in het routebestand gewenst         | Blijf `Route::middleware()` gebruiken     |

## Volgende stappen

<Columns cols={2}>
  <Card title="PHP-attributes (queues en Eloquent)" icon="code" href="/nl/advanced/php-attributes">
    Uitleg over PHP-attributes die je kunt gebruiken bij queue jobs en Eloquent-modellen.
  </Card>

  <Card title="Gevorderd: controllers" icon="layer-group" href="/nl/controllers">
    Leer de basis van Laravel-controllers.
  </Card>
</Columns>


## Related topics

- [Controllers](/nl/controllers.md)
- [Laravel-updates van maart 2026](/nl/blog/changelog/202603.md)
- [PHP-attributes](/nl/advanced/php-attributes.md)
- [Nieuwe functies in Laravel 13](/nl/blog/laravel-13-new-features.md)
- [PHP Reflection API](/nl/advanced/php-reflection.md)
