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

# Clase Lottery

> Explica cómo implementar operaciones probabilísticas de forma elegante con Illuminate\Support\Lottery. Presenta casos de uso en desarrollo de paquetes y cómo fijar el resultado en tests.

## Qué es la clase Lottery

`Illuminate\Support\Lottery` es una utilidad que expresa operaciones basadas en probabilidad con una API fluida. Permite describir de forma sencilla patrones como «ejecuta este proceso solo 1 vez cada 100 peticiones» o «registra logs detallados solo en una fracción de las peticiones».

<Info>
  La implementación está en `src/Illuminate/Support/Lottery.php`. Laravel también la utiliza internamente, por ejemplo en el GC de sesiones o para podar locks de caché.
</Info>

```mermaid theme={null}
flowchart LR
  A["Lottery::odds(1, 100)"] --> B{"Sorteo aleatorio"}
  B -->|"Gana (1%)"| C["Ejecuta callback winner"]
  B -->|"Pierde (99%)"| D["Ejecuta callback loser"]
```

## Uso básico

### Indicar la probabilidad mediante enteros

Con `Lottery::odds($chances, $outOf)` indicas la probabilidad «gana `$chances` de cada `$outOf` intentos».

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

Lottery::odds(1, 100)           // 1 vez de cada 100
    ->winner(fn () => $this->runMaintenance())
    ->loser(fn () => null)
    ->choose();
```

### Indicar la probabilidad con un decimal

Si omites `$outOf` y pasas un decimal entre `0.0` y `1.0`, se usa directamente como probabilidad.

```php theme={null}
Lottery::odds(0.01)             // 1 % de probabilidad
    ->winner(fn () => $this->sample())
    ->choose();
```

<Warning>
  Con la especificación decimal, si el valor supera `1.0` se lanza `RuntimeException`.
</Warning>

### Devolver un booleano sin callbacks

Si no configuras winner ni loser, `choose()` devuelve `true` si gana y `false` si pierde.

```php theme={null}
$shouldSample = Lottery::odds(1, 50)->choose(); // bool
```

### Ejecutar varias veces

Pasando el número de veces a `choose($times)`, se devuelve un array con los resultados.

```php theme={null}
$results = Lottery::odds(1, 2)
    ->winner(fn () => 'win')
    ->loser(fn () => 'lose')
    ->choose(10);

// p. ej.: ['win', 'lose', 'win', 'win', 'lose', ...]
```

### Pasarlo como callable

Como la instancia de `Lottery` implementa `__invoke`, la puedes pasar directamente a APIs que reciban un callable.

```php theme={null}
// Ejemplo pasándolo como segundo argumento de DB::whenQueryingForLongerThan
DB::whenQueryingForLongerThan(
    Interval::seconds(5),
    Lottery::odds(1, 5)->winner(function ($connection) {
        // Cuando se detecta una consulta lenta, envía alerta con probabilidad 1/5
        Alert::send("Slow query on {$connection->getName()}");
    })
);
```

## Casos de uso prácticos

### 1. Poda de caché (una vez cada 100)

Ideal para tareas de mantenimiento como el borrado de registros expirados, que no hace falta ejecutar en cada petición.

```php theme={null}
Lottery::odds(1, 100)
    ->winner(fn () => Cache::store('database')->flush())
    ->choose();
```

### 2. Sampling de telemetría (log detallado solo en algunas peticiones)

Cuando registrar el log de todas las peticiones es caro, resulta útil para muestreo.

```php theme={null}
Lottery::odds(1, 20)
    ->winner(function () use ($request) {
        Log::channel('telemetry')->info('Request sampled', [
            'url'      => $request->url(),
            'duration' => microtime(true) - LARAVEL_START,
            'memory'   => memory_get_peak_usage(true),
        ]);
    })
    ->choose();
```

### 3. Comportamiento estilo A/B testing

Reparte a los usuarios entre dos caminos de código de forma probabilística.

```php theme={null}
$result = Lottery::odds(1, 2)
    ->winner(fn ($user) => $this->newCheckoutFlow($user))
    ->loser(fn ($user) => $this->legacyCheckoutFlow($user))
    ->choose();
```

### 4. Ejecución aleatoria de tareas periódicas junto al scheduler

Cuando quieres ejecutar una tarea de vez en cuando evitando duplicados entre varios servidores.

```php theme={null}
// app/Console/Kernel.php
$schedule->call(function () {
    Lottery::odds(1, 3)
        ->winner(fn () => Artisan::call('cache:prune-stale-tags'))
        ->choose();
})->everyMinute();
```

## Patrones probabilísticos dentro del framework Laravel

Laravel utiliza ampliamente el patrón probabilístico para tareas de mantenimiento internas. Parte de esas implementaciones se escribieron antes de que existiera la clase `Lottery`, así que usan `random_int()` directamente, pero comparten la misma idea.

<Steps>
  <Step title="Session: garbage collection">
    `Illuminate\Session\Middleware\StartSession::configHitsLottery()` utiliza el ajuste `lottery` de `config/session.php` y decide con `random_int` si ejecutar el GC.

    ```php theme={null}
    // config/session.php
    'lottery' => [2, 100], // 2 veces por cada 100 peticiones

    // Interior de StartSession (usa random_int directamente)
    protected function configHitsLottery(array $config): bool
    {
        return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
    }
    ```
  </Step>

  <Step title="DatabaseLock: poda de locks caducados">
    `Illuminate\Cache\DatabaseLock::acquire()` aplica el mismo patrón para eliminar locks expirados en cada adquisición.

    ```php theme={null}
    // config/cache.php (driver database)
    'lock_lottery' => [2, 100], // ejecuta la poda 2 veces por cada 100

    // Interior de DatabaseLock::acquire() (usa random_int directamente)
    if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
        $this->pruneExpiredLocks();
    }
    ```
  </Step>

  <Step title="DB::whenQueryingForLongerThan — ejemplo pasando una Lottery">
    Como una instancia de `Lottery` es un callable, se puede pasar directamente como callback de detección de consultas lentas.

    ```php theme={null}
    DB::whenQueryingForLongerThan(
        Interval::seconds(5),
        Lottery::odds(1, 5)->winner(function ($connection) {
            Log::warning("Slow query on {$connection->getName()}");
        })
    );
    ```
  </Step>
</Steps>

<Tip>
  Frente a Session y DatabaseLock, que usan `random_int()` directamente, la clase `Lottery` te permite controlar el resultado durante los tests con `alwaysWin()` / `alwaysLose()` / `fix()`. En el desarrollo de paquetes, optar por `Lottery` mejora la testabilidad.
</Tip>

## Uso en pruebas

Para testear código con aleatoriedad, usa las APIs de test que ofrece `Lottery`.

### `Lottery::alwaysWin()` — gana siempre

```php theme={null}
public function test_maintenance_runs_on_win(): void
{
    $ranMaintenance = false;

    Lottery::alwaysWin(function () use (&$ranMaintenance) {
        Lottery::odds(1, 100)
            ->winner(function () use (&$ranMaintenance) {
                $ranMaintenance = true;
            })
            ->choose();
    });

    $this->assertTrue($ranMaintenance);
}
```

### `Lottery::alwaysLose()` — pierde siempre

```php theme={null}
public function test_maintenance_skipped_on_lose(): void
{
    $ranMaintenance = false;

    Lottery::alwaysLose(function () use (&$ranMaintenance) {
        Lottery::odds(1, 100)
            ->winner(function () use (&$ranMaintenance) {
                $ranMaintenance = true;
            })
            ->choose();
    });

    $this->assertFalse($ranMaintenance);
}
```

### `Lottery::fix()` — fija los resultados con una secuencia

Puedes controlar el resultado de varias llamadas con un array de `true`/`false`.

```php theme={null}
public function test_alternating_results(): void
{
    Lottery::fix([true, false, true, false]);

    $results = Lottery::odds(1, 100)
        ->winner(fn () => 'winner')
        ->loser(fn () => 'loser')
        ->choose(4);

    $this->assertSame(['winner', 'loser', 'winner', 'loser'], $results);

    Lottery::determineResultNormally(); // restablece tras el test
}
```

<Warning>
  `alwaysWin()` / `alwaysLose()` / `fix()` alteran una propiedad estática global. Llama siempre a `Lottery::determineResultNormally()` en el `tearDown()` de tu test.
</Warning>

```php theme={null}
protected function tearDown(): void
{
    Lottery::determineResultNormally();

    parent::tearDown();
}
```

### `Lottery::setResultFactory()` — inyectar una factory personalizada

Si necesitas un control más fino, usa una factory personalizada.

```php theme={null}
Lottery::setResultFactory(function ($chances, $outOf) {
    // Lógica personalizada que gana siempre
    return true;
});

// Restablece después del test
Lottery::determineResultNormally();
```

## Uso en el desarrollo de paquetes

### Registro en el service provider

Si integras una tarea de mantenimiento en el service provider de un paquete, distribuye la carga con `Lottery`.

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

class AcmeServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->app->booted(function () {
            Lottery::odds(1, 100)
                ->winner(fn () => $this->pruneExpiredRecords())
                ->choose();
        });
    }

    protected function pruneExpiredRecords(): void
    {
        $this->app['db']->table('acme_logs')
            ->where('expires_at', '<', now())
            ->delete();
    }
}
```

### Leer las odds desde configuración

Facilita al usuario ajustar la probabilidad exponiéndola en el archivo de configuración.

```php theme={null}
$lottery = config('acme.prune_lottery', [1, 100]);

Lottery::odds(...$lottery)
    ->winner(fn () => $this->pruneExpiredRecords())
    ->choose();
```

```php theme={null}
// config/acme.php
return [
    // [ganadores, intentos] = ejecuta poda 1 vez por cada 100 peticiones
    'prune_lottery' => [1, 100],
];
```

### Sampling en un middleware

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

class SampleTelemetryMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        Lottery::odds(1, 50)
            ->winner(fn () => $this->recordTelemetry($request, $response))
            ->choose();

        return $response;
    }
}
```

## Referencia de la API

| Método                                | Descripción                                         |
| ------------------------------------- | --------------------------------------------------- |
| `Lottery::odds($chances, $outOf)`     | Factory estática que crea una instancia de Lottery. |
| `->winner(callable $callback)`        | Define la callback si gana.                         |
| `->loser(callable $callback)`         | Define la callback si pierde.                       |
| `->choose($times = null)`             | Ejecuta la lotería. Con `$times` devuelve un array. |
| `Lottery::alwaysWin($callback)`       | Para tests: gana siempre.                           |
| `Lottery::alwaysLose($callback)`      | Para tests: pierde siempre.                         |
| `Lottery::fix($sequence)`             | Para tests: fija los resultados con una secuencia.  |
| `Lottery::determineResultNormally()`  | Restablece la fijación de resultados.               |
| `Lottery::setResultFactory(callable)` | Inyecta lógica personalizada de decisión.           |

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Trait Macroable" icon="puzzle-piece" href="/es/advanced/macroable">
    Aprende el patrón de extensión para añadir métodos nuevos a clases existentes.
  </Card>

  <Card title="Trait Conditionable" icon="git-branch" href="/es/advanced/conditionable">
    Aprende cómo diseñar cadenas con bifurcaciones condicionales usando `when()` / `unless()`.
  </Card>
</Columns>


## Related topics

- [Clase Fluent](/es/advanced/fluent.md)
- [Manipulación de cadenas (clase Str)](/es/strings.md)
- [Envío de correo](/es/mail.md)
- [Facades](/es/facades.md)
- [GeneratorCommand — implementación de comandos make: personalizados](/es/advanced/generator-command.md)
