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

# Lottery-klasse

> Leer hoe je met Illuminate\Support\Lottery op elegante wijze probabilistische operaties implementeert. Inclusief use cases voor pakketontwikkeling en het fixeren van resultaten in tests.

## Wat is de Lottery-klasse

`Illuminate\Support\Lottery` is een utility-klasse waarmee je kansgebaseerde operaties in een vloeiende API kunt uitdrukken. Patronen als "voer een bewerking maar eens per 100 requests uit" of "log alleen bij een deel van de requests gedetailleerd" schrijf je er eenvoudig mee.

<Info>
  De implementatie staat in `src/Illuminate/Support/Lottery.php`. Laravel gebruikt deze klasse ook intern in het framework, bijvoorbeeld voor de session-GC en het prunen van cachelocks.
</Info>

```mermaid theme={null}
flowchart LR
  A["Lottery::odds(1, 100)"] --> B{"Willekeurige beslissing"}
  B -->|"Winst (1%)"| C["winner-callback uitvoeren"]
  B -->|"Verlies (99%)"| D["loser-callback uitvoeren"]
```

## Basisgebruik

### De kans opgeven als gehele verhouding

Met `Lottery::odds($chances, $outOf)` geef je de kans op als "$chances keer winst per $outOf keer".

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

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

### De kans opgeven als decimaal

Als je `$outOf` weglaat en een decimaal tussen `0.0` en `1.0` meegeeft, wordt die rechtstreeks als kans gebruikt.

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

<Warning>
  Als de decimale waarde groter is dan `1.0`, wordt een `RuntimeException` gegooid.
</Warning>

### Een boolean teruggeven zonder callbacks

Als je geen winner / loser instelt, geeft `choose()` `true` terug bij winst en `false` bij verlies.

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

### Meerdere keren uitvoeren

Geef je aan `choose($times)` een aantal mee, dan krijg je een array met resultaten terug.

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

// Bijv.: ['win', 'lose', 'win', 'win', 'lose', ...]
```

### Doorgeven als callable

Een `Lottery`-instantie implementeert `__invoke` en kun je dus rechtstreeks doorgeven aan API's die een callable verwachten.

```php theme={null}
// Voorbeeld: doorgeven als tweede argument van DB::whenQueryingForLongerThan
DB::whenQueryingForLongerThan(
    Interval::seconds(5),
    Lottery::odds(1, 5)->winner(function ($connection) {
        // Bij het detecteren van een slow query met kans 1/5 een alert sturen
        Alert::send("Slow query on {$connection->getName()}");
    })
);
```

## Praktische use cases

### 1. Cache prunen (maar eens per 100 keer uitvoeren)

Ideaal voor onderhoudstaken die niet bij elke aanroep hoeven te draaien, zoals het verwijderen van verlopen records.

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

### 2. Telemetrie-sampling (alleen bij een deel van de requests gedetailleerd loggen)

Als het te duur is om alle requests te loggen, kun je dit voor sampling gebruiken.

```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. A/B-test-achtig gedrag

Verdeel gebruikers probabilistisch over twee codepaden.

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

### 4. Periodieke taken willekeurig uitvoeren als aanvulling op de scheduler

Handig als je een taak willekeurig wilt uitvoeren en tegelijk dubbele uitvoering op meerdere servers wilt vermijden.

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

## Probabilistische patronen binnen het Laravel-framework

Laravel gebruikt ook intern in het framework op grote schaal probabilistische onderhoudstaken. Sommige implementaties zijn geschreven vóór de `Lottery`-klasse bestond en gebruiken daarom rechtstreeks `random_int()`, maar ze zijn gebaseerd op hetzelfde idee.

<Steps>
  <Step title="Session: garbage collection">
    `Illuminate\Session\Middleware\StartSession::configHitsLottery()` gebruikt de `lottery`-instelling uit `config/session.php` en beslist met `random_int` of de GC wordt uitgevoerd.

    ```php theme={null}
    // config/session.php
    'lottery' => [2, 100], // 2 keer per 100 requests

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

  <Step title="DatabaseLock: verlopen locks prunen">
    `Illuminate\Cache\DatabaseLock::acquire()` verwijdert bij elke lock-acquisitie volgens hetzelfde verhoudingspatroon verlopen locks.

    ```php theme={null}
    // config/cache.php (database-driver)
    'lock_lottery' => [2, 100], // 2 keer per 100 keer prunen

    // Binnen DatabaseLock::acquire() (gebruikt random_int rechtstreeks)
    if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
        $this->pruneExpiredLocks();
    }
    ```
  </Step>

  <Step title="DB::whenQueryingForLongerThan — een Lottery-instantie doorgeven">
    Omdat een `Lottery`-instantie als callable kan worden doorgegeven, kun je hem rechtstreeks gebruiken als callback voor slow-query-detectie.

    ```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>
  Waar Session en DatabaseLock rechtstreeks `random_int()` gebruiken, heeft de `Lottery`-klasse als voordeel dat je met `alwaysWin()` / `alwaysLose()` / `fix()` het resultaat in tests kunt sturen. Kies bij pakketontwikkeling voor de `Lottery`-klasse: dat verbetert de testbaarheid.
</Tip>

## Gebruik in tests

Voor het testen van code met randomness gebruik je de test-API's die `Lottery` aanbiedt.

### `Lottery::alwaysWin()` — altijd laten winnen

```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()` — altijd laten verliezen

```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()` — resultaten fixeren met een sequentie

Je kunt de resultaten van meerdere aanroepen sturen met een array van `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(); // Na de test altijd terugzetten
}
```

<Warning>
  `alwaysWin()` / `alwaysLose()` / `fix()` wijzigen een globale statische property. Roep in de `tearDown()` van je test altijd `Lottery::determineResultNormally()` aan.
</Warning>

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

    parent::tearDown();
}
```

### `Lottery::setResultFactory()` — een eigen factory injecteren

Als je fijnmazigere controle nodig hebt, gebruik je een eigen factory.

```php theme={null}
Lottery::setResultFactory(function ($chances, $outOf) {
    // Eigen logica die altijd laat winnen
    return true;
});

// Na de test resetten
Lottery::determineResultNormally();
```

## Toepassing in pakketontwikkeling

### Registratie in de service provider

Als je onderhoudstaken inbouwt in de service provider van je pakket, gebruik je `Lottery` om de belasting te spreiden.

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

### De odds uit de configuratie lezen

Door de kans configureerbaar te maken via een configuratiebestand, kunnen gebruikers hem makkelijk bijstellen.

```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 [
    // [aantal keer winst, aantal pogingen] = 1 keer prunen per 100 requests
    'prune_lottery' => [1, 100],
];
```

### Sampling in 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;
    }
}
```

## API-referentie

| Methode                               | Beschrijving                                                |
| ------------------------------------- | ----------------------------------------------------------- |
| `Lottery::odds($chances, $outOf)`     | Statische factory die een Lottery-instantie maakt           |
| `->winner(callable $callback)`        | Stelt de callback bij winst in                              |
| `->loser(callable $callback)`         | Stelt de callback bij verlies in                            |
| `->choose($times = null)`             | Voert de Lottery uit. Met `$times` krijg je een array terug |
| `Lottery::alwaysWin($callback)`       | Voor tests: altijd winst                                    |
| `Lottery::alwaysLose($callback)`      | Voor tests: altijd verlies                                  |
| `Lottery::fix($sequence)`             | Voor tests: resultaten fixeren met een sequentie            |
| `Lottery::determineResultNormally()`  | Reset de testfixatie                                        |
| `Lottery::setResultFactory(callable)` | Injecteert eigen beslislogica                               |

## Gerelateerde pagina's

<Columns cols={2}>
  <Card title="Macroable-trait" icon="puzzle-piece" href="/nl/advanced/macroable">
    Leer het uitbreidingspatroon waarmee je nieuwe methodes toevoegt aan bestaande klassen.
  </Card>

  <Card title="Conditionable-trait" icon="git-branch" href="/nl/advanced/conditionable">
    Leer hoe je voorwaardelijke chains ontwerpt met `when()` / `unless()`.
  </Card>
</Columns>


## Related topics

- [Fluent-klasse](/nl/advanced/fluent.md)
- [Strings bewerken (de Str-klasse)](/nl/strings.md)
- [E-mail versturen](/nl/mail.md)
- [Notificaties (Notifications)](/nl/notifications.md)
- [Validatie](/nl/validation.md)
