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

# Trait Conditionable

> Découvrez l'implémentation interne des méthodes when()/unless() et leurs patterns d'utilisation.

## Qu'est-ce que le trait Conditionable

Le trait `Illuminate\Support\Traits\Conditionable` ajoute les méthodes `when()` et `unless()` à un objet. Sa particularité est de permettre de continuer une chaîne de méthodes tout en dérivant le traitement en fonction d'une condition.

<Info>
  Les sources réelles se trouvent dans `src/Illuminate/Conditionable/Traits/Conditionable.php`. Elles sont référencées via l'alias `Illuminate\Support\Traits\Conditionable`.
</Info>

De nombreuses classes Laravel utilisent ce trait : QueryBuilder, EloquentBuilder, Mail, Notification, etc.

## Utilisation de base

### when() — s'exécute lorsque la condition est vraie

```php theme={null}
use Illuminate\Support\Collection;

$results = collect([1, 2, 3, 4, 5])
    ->when(true, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n > 2);
    });
// [3, 4, 5]
```

Quand la valeur du premier argument est vraie, le callback du second argument est exécuté. Sinon, le callback (par défaut) du troisième argument est exécuté.

```php theme={null}
$results = collect([1, 2, 3, 4, 5])
    ->when(false, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n > 2);
    }, function (Collection $collection) {
        return $collection->filter(fn ($n) => $n < 3);
    });
// [1, 2]
```

### unless() — s'exécute lorsque la condition est fausse

`unless()` est l'inverse de `when()`. Le callback est exécuté lorsque la condition est fausse.

```php theme={null}
$results = collect([1, 2, 3, 4, 5])
    ->unless(false, function (Collection $collection) {
        return $collection->take(3);
    });
// [1, 2, 3]
```

## Pourquoi le chaînage continue

Si la valeur de retour du callback est `null`, c'est `$this` (l'objet qui utilise le trait) qui est retourné. Si le callback ne retourne pas `null`, c'est sa valeur de retour qui est retournée.

```php theme={null}
// $this est retourné donc la chaîne continue
$query = User::query()
    ->when($request->has('active'), function ($query) {
        $query->where('active', true); // retourne void / null
    })
    ->when($request->filled('name'), function ($query) use ($request) {
        $query->where('name', 'like', "%{$request->name}%");
    })
    ->orderBy('created_at', 'desc');
```

Dans le code source, cela est implémenté ainsi :

```php theme={null}
if ($value) {
    return $callback($this, $value) ?? $this;
} elseif ($default) {
    return $default($this, $value) ?? $this;
}

return $this;
```

Si le callback retourne explicitement une valeur, cette valeur est passée au maillon suivant de la chaîne. S'il ne retourne rien (`null`), `$this` est retourné.

## Appel sans argument — HigherOrderWhenProxy

Appeler `when()` sans argument retourne un `HigherOrderWhenProxy`. Cela permet de définir la condition ultérieurement.

```php theme={null}
$query = User::query()
    ->when()->isActive()  // évalue isActive() comme condition
    ->where('role', 'admin');
```

Avec un seul argument, un proxy contenant cette valeur comme condition est retourné.

```php theme={null}
// Un argument : passer uniquement la condition pour obtenir un proxy
$proxy = collect([1, 2, 3])->when($request->has('filter'));
// $proxy->methodName() n'appelle methodName() que si la condition est vraie
```

## Passer une closure comme valeur

Passer une closure en premier argument utilise la valeur de retour de l'exécution de cette closure comme condition.

```php theme={null}
$results = User::query()
    ->when(
        fn ($query) => $request->filled('role'),
        fn ($query) => $query->where('role', $request->role)
    )
    ->get();
```

Cela permet d'extraire la logique d'évaluation de la condition dans un callback.

## Pattern typique avec QueryBuilder

La construction dynamique de requêtes avec `when()` est le cas d'usage le plus courant.

```php theme={null}
public function index(Request $request)
{
    $users = User::query()
        ->when($request->filled('search'), function ($query) use ($request) {
            $query->where('name', 'like', "%{$request->search}%")
                  ->orWhere('email', 'like', "%{$request->search}%");
        })
        ->when($request->filled('role'), fn ($q) => $q->where('role', $request->role))
        ->when($request->boolean('verified'), fn ($q) => $q->whereNotNull('email_verified_at'))
        ->when(
            $request->filled('sort'),
            fn ($q) => $q->orderBy($request->sort, $request->get('direction', 'asc')),
            fn ($q) => $q->latest()
        )
        ->paginate();

    return UserResource::collection($users);
}
```

## Appliquer Conditionable à votre propre classe

Il suffit d'un `use` du trait pour disposer de `when()` / `unless()`.

```php theme={null}
namespace App\Services;

use Illuminate\Support\Traits\Conditionable;

class ReportBuilder
{
    use Conditionable;

    protected array $filters = [];
    protected bool $includeArchived = false;
    protected ?string $groupBy = null;

    public function withArchived(): static
    {
        $this->includeArchived = true;

        return $this;
    }

    public function groupBy(string $column): static
    {
        $this->groupBy = $column;

        return $this;
    }

    public function addFilter(string $column, mixed $value): static
    {
        $this->filters[$column] = $value;

        return $this;
    }

    public function build(): \Illuminate\Database\Eloquent\Builder
    {
        return Report::query()
            ->when($this->includeArchived, fn ($q) => $q->withTrashed())
            ->when($this->groupBy, fn ($q) => $q->groupBy($this->groupBy))
            ->when($this->filters, function ($q) {
                foreach ($this->filters as $column => $value) {
                    $q->where($column, $value);
                }
            });
    }
}
```

```php theme={null}
// Exemple d'utilisation
$query = (new ReportBuilder)
    ->when($request->boolean('archived'), fn ($b) => $b->withArchived())
    ->when($request->filled('group'), fn ($b) => $b->groupBy($request->group))
    ->addFilter('status', 'published')
    ->build();
```

## Utilisation avec Mail, Notification, Response

`when()` est également utilisable pour la construction d'e-mails, de notifications et de réponses.

```php theme={null}
use Illuminate\Mail\Mailable;
use Illuminate\Support\Traits\Conditionable;

class OrderConfirmation extends Mailable
{
    public function build(): static
    {
        return $this
            ->subject('Votre commande a bien été reçue')
            ->view('emails.order.confirmation')
            ->when($this->order->hasDiscount(), function (Mailable $mail) {
                $mail->attach(storage_path('discounts/coupon.pdf'));
            })
            ->when(app()->environment('production'), function (Mailable $mail) {
                $mail->bcc('archive@example.com');
            });
    }
}
```

## Différence avec tap()

`tap()` et `when()` se ressemblent mais ont des objectifs différents.

|                  | `tap()`                    | `when()`                                                      |
| ---------------- | -------------------------- | ------------------------------------------------------------- |
| Objectif         | Effet de bord (log, debug) | Branchement conditionnel                                      |
| Valeur de retour | Toujours `$this`           | `$this` ou la valeur de retour du callback selon la condition |
| Condition        | Aucune                     | Oui                                                           |

```php theme={null}
// tap : à utiliser pour un effet de bord. Retourne toujours $this
$user = User::find($id)
    ->tap(fn ($user) => Log::info("User {$user->id} loaded"));

// when : à utiliser pour un branchement conditionnel
$user = User::query()
    ->when($isAdmin, fn ($q) => $q->where('role', 'admin'))
    ->first();
```

<Tip>
  Si vous voulez juste faire quelque chose au milieu d'une chaîne pour du debug ou un effet de bord, utilisez `tap()`. Pour changer le traitement selon une condition, utilisez `when()` / `unless()`.
</Tip>

## Étapes suivantes

<Card title="Messages d'ordre supérieur de Collection" icon="layers" href="/fr/advanced/higher-order-messages">
  Apprenez le mécanisme de la syntaxe `$collection->map->method()` et son utilisation pratique.
</Card>


## Related topics

- [Classe Fluent](/fr/advanced/fluent.md)
- [Trait Dumpable](/fr/advanced/dumpable.md)
- [Helper tap() et trait Tappable](/fr/advanced/tap.md)
- [Trait InteractsWithData](/fr/advanced/interacts-with-data.md)
- [Trait InteractsWithTime](/fr/advanced/interacts-with-time.md)
