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

# Attributs PHP des contrôleurs

> Découvrez comment configurer de manière déclarative les middlewares et l'autorisation sur vos contrôleurs à l'aide des attributs #[Middleware], #[WithoutMiddleware] et #[Authorize] ajoutés dans Laravel 13.

## Aperçu

Laravel 13 permet désormais de déclarer l'affectation de middlewares et les vérifications d'autorisation d'un contrôleur à l'aide d'attributs PHP. À la place de la méthode `middleware()` traditionnelle ou du middleware `can`, il suffit d'ajouter un attribut à la classe ou à la méthode pour tout configurer.

```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>
  Tous les attributs de contrôleur se trouvent dans l'espace de noms `Illuminate\Routing\Attributes\Controllers`.
</Tip>

## `#[Middleware]` — assigner un middleware

### Application à la classe

Lorsque `#[Middleware]` est appliqué au niveau de la classe, le middleware est appliqué à toutes les actions du contrôleur.

```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 { /* ... */ }
}
```

Pour attacher plusieurs middlewares, il suffit de répéter l'attribut.

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

### Application aux méthodes

Un middleware appliqué au niveau d'une méthode est fusionné avec ceux définis au niveau de la classe.

```php theme={null}
#[Middleware('auth')]
class UserController
{
    // Uniquement auth pour tout le monde
    public function index(): View { /* ... */ }

    // auth + subscribed sont tous les deux appliqués
    #[Middleware('subscribed')]
    public function create(): View { /* ... */ }
}
```

### Filtrage avec `only` / `except`

En précisant `only` ou `except` sur un attribut appliqué au niveau de la classe, vous pouvez restreindre les méthodes ciblées.

```php theme={null}
#[Middleware('auth')]
#[Middleware('subscribed', only: ['create', 'store', 'edit', 'update'])]
class ArticleController
{
    // uniquement 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 { /* ... */ }

    // uniquement auth (équivalent à l'exclusion par except)
    public function destroy(Article $article): Response { /* ... */ }
}
```

### Middleware sous forme de closure

Les attributs acceptent aussi des closures. C'est pratique lorsque vous souhaitez écrire le traitement en ligne.

```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
    {
        // ...
    }
}
```

### Comparaison avec la méthode `middleware()` traditionnelle

```php theme={null}
// Ancienne écriture (implémentation de l'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']),
        ];
    }
}

// Écriture à base d'attributs
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // Pas besoin d'implémenter HasMiddleware
}
```

<Info>
  Lorsque vous utilisez les attributs, il n'est pas nécessaire d'implémenter l'interface `HasMiddleware`. Toutefois, mélanger la méthode `middleware()` et les attributs peut conduire à des comportements imprévus : il est recommandé d'utiliser exclusivement l'une ou l'autre approche.
</Info>

## `#[WithoutMiddleware]` — exclure un middleware

Cet attribut permet d'exclure un middleware appliqué au niveau de la classe pour une méthode spécifique ou pour toute la classe.

### Application aux méthodes

```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 et EnsureTokenIsValid s'appliquent
    public function show(Resource $resource): JsonResponse { /* ... */ }

    // auth s'applique, mais EnsureTokenIsValid est exclu
    #[WithoutMiddleware(EnsureTokenIsValid::class)]
    public function index(): JsonResponse { /* ... */ }
}
```

### Application à la classe avec `only` / `except`

Lorsque `#[WithoutMiddleware]` est appliqué au niveau de la classe, le middleware est exclu de toutes les actions, y compris dans les classes filles. Utilisez `only` / `except` pour restreindre la portée de l'exclusion.

```php theme={null}
#[Middleware('auth')]
#[WithoutMiddleware('subscribed', except: ['index'])]
class AdminController
{
    // subscribed s'applique (non exclu grâce à except: ['index'])
    public function index(): View { /* ... */ }

    // subscribed est exclu
    public function dashboard(): View { /* ... */ }
    public function settings(): View { /* ... */ }
}
```

<Warning>
  `#[WithoutMiddleware]` n'agit que sur les **middlewares de route**. Les middlewares globaux enregistrés dans `app/Http/Kernel.php` ne peuvent pas être exclus.
</Warning>

## `#[Authorize]` — autorisation via les policies

En tant que sucre syntaxique du middleware `can`, cet attribut permet de déclarer une vérification d'autorisation basée sur une policy.

### Utilisation de base

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

class PostController
{
    // Vérifie l'ability 'viewAny' (méthode viewAny de la policy Post)
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    // Vérifie l'ability 'view' (le paramètre de route 'post' est passé)
    #[Authorize('view', 'post')]
    public function show(Post $post): View { /* ... */ }

    // Vérifie l'ability 'create'
    #[Authorize('create', Post::class)]
    public function create(): View { /* ... */ }

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

    // Vérifie l'ability 'update' (le paramètre de route est passé)
    #[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 { /* ... */ }
}
```

### Arguments de la policy

Le deuxième argument accepte les valeurs suivantes :

| Valeur passée              | Description                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------- |
| `Post::class`              | Classe de modèle (pour des abilities comme `viewAny` qui ne nécessitent pas d'instance) |
| `'post'`                   | Nom du paramètre de route (l'instance résolue via le model binding est transmise)       |
| `[Comment::class, 'post']` | Arguments multiples (passés à la méthode de la policy sous forme de tableau)            |

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

class CommentController
{
    // Passe $post (paramètre de route) et la classe Comment à la policy
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post, Request $request): Response
    {
        // ...
    }

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

### Comparaison avec le middleware `can`

```php theme={null}
// Ancienne écriture (façade Route)
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// Écriture à base d'attributs
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

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

Vous n'avez plus besoin de déclarer les middlewares dans le fichier de routes : la logique d'autorisation est centralisée dans le contrôleur.

## Exemples pratiques

### Application à un contrôleur de ressource

```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');
    }
}
```

### Application à un contrôleur d'API

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

#[Middleware('auth:sanctum')]
#[Middleware('throttle:api')]
class ApiPostController
{
    // La liste est accessible sans authentification
    #[WithoutMiddleware('auth:sanctum')]
    public function index(): JsonResponse
    {
        return response()->json(Post::paginate());
    }

    public function store(Request $request): JsonResponse
    {
        // Uniquement pour les utilisateurs authentifiés
    }
}
```

## Ordre de traitement des attributs

Lorsque plusieurs attributs sont appliqués, l'ordre de traitement est le suivant :

```mermaid theme={null}
flowchart LR
    A["Requête"] --> B["#[Middleware]<br>au niveau de la classe"]
    B --> C["#[Middleware]<br>au niveau de la méthode"]
    C --> D["Exclusions<br>via #[WithoutMiddleware]"]
    D --> E["Vérification d'autorisation<br>via #[Authorize]"]
    E --> F["Exécution de la<br>méthode du contrôleur"]
```

<Info>
  En interne, `#[Authorize]` fonctionne comme un middleware `can` et est donc traité dans le même pipeline que `#[Middleware]`.
</Info>

## Récapitulatif : laquelle choisir ?

| Situation                                                | Approche recommandée                      |
| -------------------------------------------------------- | ----------------------------------------- |
| Nouveau contrôleur (Laravel 13)                          | Utiliser les attributs                    |
| Contrôleur existant possédant une méthode `middleware()` | Migrer progressivement vers les attributs |
| Configuration dynamique de middlewares                   | Conserver la méthode `middleware()`       |
| Gestion centralisée dans le fichier de routes            | Continuer avec `Route::middleware()`      |

## Étapes suivantes

<Columns cols={2}>
  <Card title="Attributs PHP (queue et Eloquent)" icon="code" href="/fr/advanced/php-attributes">
    Présentation des attributs PHP utilisables sur les jobs de queue et les modèles Eloquent.
  </Card>

  <Card title="Niveau intermédiaire : contrôleurs" icon="layer-group" href="/fr/controllers">
    Apprenez les bases de l'utilisation des contrôleurs Laravel.
  </Card>
</Columns>


## Related topics

- [Les nouveautés de Laravel 13](/fr/blog/laravel-13-new-features.md)
- [Mises à jour Laravel — mars 2026](/fr/blog/changelog/202603.md)
- [Contrôleurs](/fr/controllers.md)
- [Attributs PHP](/fr/advanced/php-attributes.md)
- [API PHP Reflection](/fr/advanced/php-reflection.md)
