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

> Explica la implementación interna de los métodos when()/unless() y patrones prácticos para su uso.

## Qué es el trait Conditionable

El trait `Illuminate\Support\Traits\Conditionable` añade a un objeto los métodos `when()` y `unless()`. Su característica principal es que permite bifurcar el comportamiento en función de una condición sin romper el encadenamiento de métodos.

<Info>
  El código fuente real se encuentra en `src/Illuminate/Conditionable/Traits/Conditionable.php`. Se referencia mediante el alias `Illuminate\Support\Traits\Conditionable`.
</Info>

Muchas clases de Laravel, como QueryBuilder, EloquentBuilder, Mail o Notification, utilizan este trait.

## Uso básico

### when() — se ejecuta cuando la condición es verdadera

```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]
```

Cuando el valor del primer argumento es verdadero, se ejecuta la callback del segundo argumento. Cuando es falso, se ejecuta la callback (por defecto) del tercer argumento.

```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() — se ejecuta cuando la condición es falsa

`unless()` es el opuesto de `when()`: la callback se ejecuta cuando la condición es falsa.

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

## Por qué la cadena de métodos continúa

Si la callback devuelve `null`, se devuelve `$this` (el objeto que utiliza el trait). Si la callback devuelve algo distinto de `null`, se devuelve ese valor.

```php theme={null}
// Se devuelve $this, así que la cadena continúa
$query = User::query()
    ->when($request->has('active'), function ($query) {
        $query->where('active', true); // devuelve void / null
    })
    ->when($request->filled('name'), function ($query) use ($request) {
        $query->where('name', 'like', "%{$request->name}%");
    })
    ->orderBy('created_at', 'desc');
```

Así se implementa en el código fuente:

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

return $this;
```

Si la callback devuelve un valor explícito, ese valor se propaga al siguiente eslabón de la cadena. Si no devuelve nada (`null`), se devuelve `$this`.

## Llamada sin argumentos — HigherOrderWhenProxy

Si llamas a `when()` sin argumentos, se devuelve un `HigherOrderWhenProxy`. Con él puedes definir la condición a posteriori.

```php theme={null}
$query = User::query()
    ->when()->isActive()  // evalúa isActive() como condición
    ->where('role', 'admin');
```

Si lo llamas con un solo argumento, obtienes un proxy que conserva ese valor como condición.

```php theme={null}
// Un argumento: solo pasa la condición y obtiene un proxy
$proxy = collect([1, 2, 3])->when($request->has('filter'));
// Con $proxy->methodName() se llama a methodName() solo si la condición es verdadera
```

## Pasar un closure como valor

Si pasas un closure como primer argumento, el resultado de ejecutarlo se utiliza como condición.

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

De esta forma puedes extraer la lógica de evaluación de la condición a una callback.

## Patrón típico en QueryBuilder

La construcción de consultas dinámicas con `when()` es el caso de uso más habitual.

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

## Aplicar Conditionable a tus propias clases

Basta con hacer `use` del trait para que `when()` y `unless()` estén disponibles.

```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}
// Ejemplo de uso
$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();
```

## Uso con Mail, Notification y Response

`when()` también resulta útil para construir correos, notificaciones y respuestas.

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

class OrderConfirmation extends Mailable
{
    public function build(): static
    {
        return $this
            ->subject('Hemos recibido tu pedido')
            ->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');
            });
    }
}
```

## Cuándo usar `tap()` y cuándo `when()`

`tap()` y `when()` se parecen, pero tienen propósitos distintos.

|                  | `tap()`                          | `when()`                                                        |
| ---------------- | -------------------------------- | --------------------------------------------------------------- |
| Objetivo         | Efectos colaterales (log, debug) | Bifurcación condicional                                         |
| Valor de retorno | Siempre `$this`                  | `$this` o el valor devuelto por la callback, según la condición |
| Condición        | No tiene                         | Sí                                                              |

```php theme={null}
// tap: se usa para efectos colaterales. Siempre devuelve $this
$user = User::find($id)
    ->tap(fn ($user) => Log::info("User {$user->id} loaded"));

// when: se usa para bifurcaciones condicionales
$user = User::query()
    ->when($isAdmin, fn ($q) => $q->where('role', 'admin'))
    ->first();
```

<Tip>
  Si solo quieres hacer algo en medio de la cadena por efecto colateral (debug, logging), usa `tap()`. Si quieres cambiar el comportamiento según una condición, usa `when()` / `unless()`.
</Tip>

## Próximos pasos

<Card title="Mensajes de orden superior en colecciones" icon="layers" href="/es/advanced/higher-order-messages">
  Aprende cómo funciona una sintaxis como `$collection->map->method()` y cómo usarla en la práctica.
</Card>


## Related topics

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