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

# Artisan-console

> Leer hoe je eigen CLI-commando's maakt met de Artisan-console van Laravel.

## Wat is Artisan

Artisan is de command-line interface (CLI) die bij Laravel wordt meegeleverd.
Het bestaat als het `artisan`-script in de projectroot en biedt talloze commando's die ontwikkeling en beheer efficiënter maken.

Gebruik het `list`-commando om een overzicht van de beschikbare commando's te bekijken.

```shell theme={null}
php artisan list
```

Om te zien hoe je een commando gebruikt, zet je er `help` voor.

```shell theme={null}
php artisan help migrate
```

<Info>
  Gebruik je Laravel Sail, gebruik dan `sail artisan` in plaats van `php artisan`.
  Het commando wordt dan uitgevoerd binnen de Docker-container.

  ```shell theme={null}
  ./vendor/bin/sail artisan list
  ```
</Info>

## Tinker

[Laravel Tinker](https://github.com/laravel/tinker) is een REPL-omgeving waarmee je de hele applicatie — Eloquent-modellen, jobs, events en meer — interactief kunt bedienen vanaf de command line.

```shell theme={null}
php artisan tinker
```

Na het starten kun je direct PHP-code uitvoeren.

```php theme={null}
// Een gebruiker ophalen en bekijken
>>> App\Models\User::find(1)
// Een record aanmaken met een factory
>>> App\Models\User::factory()->create()
// Een serviceklasse rechtstreeks aanroepen
>>> app(App\Services\OrderService::class)->process(1)
```

<Tip>
  Tinker wijzigt data daadwerkelijk. Wees zeer voorzichtig met uitvoeren in productie.
  Het is vooral geschikt voor het testen en debuggen in lokale en staging-omgevingen.
</Tip>

## Eigen commando's maken

### Een commando genereren

Met `make:command` genereer je een sjabloon voor een commandoklasse.

```shell theme={null}
php artisan make:command ImportProducts
```

Er wordt een bestand `app/Console/Commands/ImportProducts.php` gegenereerd.

### De basisstructuur van een commando

De gegenereerde commandoklasse heeft drie hoofdonderdelen: `$signature`, `$description` en `handle()`.

```php theme={null}
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ImportProducts extends Command
{
    /**
     * Definitie van de commandonaam, argumenten en opties
     */
    protected $signature = 'import:products';

    /**
     * Beschrijving van het commando (getoond in php artisan list)
     */
    protected $description = 'Importeert productdata uit een CSV';

    /**
     * De uitvoering van het commando
     */
    public function handle(): void
    {
        $this->info('Import wordt gestart...');
        // Schrijf hier je verwerking
        $this->info('Klaar.');
    }
}
```

<Info>
  In Laravel 13 kun je ook een schrijfwijze met PHP-attributen gebruiken.

  ```php theme={null}
  use Illuminate\Console\Attributes\Description;
  use Illuminate\Console\Attributes\Signature;

  #[Signature('import:products')]
  #[Description('Importeert productdata uit een CSV')]
  class ImportProducts extends Command
  {
      public function handle(): void
      {
          // ...
      }
  }
  ```
</Info>

### Argumenten en opties definiëren

In `$signature` definieer je de commandonaam, argumenten en opties samen.

```php theme={null}
protected $signature = 'import:products
                        {file : Pad naar het te importeren CSV-bestand}
                        {--limit= : Maximaal aantal te importeren records}
                        {--dry-run : Niets opslaan in de DB (ter controle)}';
```

| Notatie               | Type                         | Beschrijving                                   |
| --------------------- | ---------------------------- | ---------------------------------------------- |
| `{file}`              | Verplicht argument           | Weglaten geeft een fout                        |
| `{file?}`             | Optioneel argument           | Bij weglaten `null`                            |
| `{file=products.csv}` | Argument met standaardwaarde | Bij weglaten wordt de standaardwaarde gebruikt |
| `{--queue}`           | Flag-optie                   | `true` indien opgegeven, anders `false`        |
| `{--limit=}`          | Optie met waarde             | Geef een waarde door zoals `--limit=100`       |
| `{--limit=50}`        | Optie met standaardwaarde    | Bij weglaten `50`                              |
| `{--Q\|queue}`        | Met shortcut                 | Ook op te geven als `-Q`                       |

### Argumenten en opties ophalen

In de `handle()`-methode haal je de waarden op met `argument()` en `option()`.

```php theme={null}
public function handle(): void
{
    $file    = $this->argument('file');       // Argument ophalen
    $limit   = $this->option('limit');        // Optie ophalen
    $dryRun  = $this->option('dry-run');      // Flag-optie (bool)

    $this->info("Bestand: {$file}");

    if ($dryRun) {
        $this->warn('Dry-run-modus: opslaan in de DB wordt overgeslagen');
    }
}
```

### `input()` — getypeerde accessors

Met de `input()`-methode kun je argumenten en opties van een commando ophalen via dezelfde getypeerde accessors als bij HTTP-requests.

```php theme={null}
use App\Enums\ReportType;

public function handle(): void
{
    // Argumenten en opties ophalen als CommandInput-instantie
    $from = $this->input()->date('from');
    $type = $this->input()->enum('type', ReportType::class);
    $limit = $this->input()->integer('limit');
}
```

Wil je alleen een specifieke naam ophalen, dan geef je de naam rechtstreeks door (er wordt gezocht in zowel argumenten als opties).

```php theme={null}
$queue = $this->input('queue', 'default');
```

<Tip>
  Waar `argument()` en `option()` strings teruggeven, converteren de getypeerde accessors van `input()` naar het juiste type. Handig als je datums, getallen of enums typesafe wilt behandelen.
</Tip>

## Interactie met de gebruiker

### Berichten uitvoeren

Er zijn methoden beschikbaar om gekleurde berichten naar de console te schrijven.

```php theme={null}
$this->info('Verwerking succesvol afgerond');    // groen
$this->warn('Deze actie kan niet ongedaan worden gemaakt');    // geel
$this->error('Er is een fout opgetreden');       // rood
$this->line('Gewone tekst');             // zonder kleur
$this->comment('Aanvullende info');               // lichtgrijs
```

### Vragen stellen aan de gebruiker

Je kunt interactief invoer van de gebruiker ontvangen.

```php theme={null}
// Tekstinvoer
$name = $this->ask('Voer de naam van de contactpersoon in');

// Met standaardwaarde
$env = $this->ask('Kies de uitvoeringsomgeving', 'production');

// Geheime informatie zoals wachtwoorden (invoer wordt niet op het scherm getoond)
$password = $this->secret('Voer de API-sleutel in');

// Ja/nee-bevestiging
if (! $this->confirm('Importeren in de productie-DB?')) {
    $this->info('Geannuleerd.');
    return;
}

// Kiezen uit opties
$format = $this->choice('Kies het uitvoerformaat', ['csv', 'json', 'xml'], 0);
```

### Voortgangsbalk

Tijdens het verwerken van grote hoeveelheden data kun je de voortgang overzichtelijk tonen.

```php theme={null}
use App\Models\Product;

// Geef een collection door en de voortgangsbalk verschijnt automatisch
$this->withProgressBar(Product::cursor(), function (Product $product) {
    $this->processProduct($product);
});
```

Wil je alles handmatig fijner aansturen, gebruik dan de volgende aanpak.

```php theme={null}
$total = Product::count();
$bar = $this->output->createProgressBar($total);
$bar->start();

Product::cursor()->each(function (Product $product) use ($bar) {
    $this->processProduct($product);
    $bar->advance();
});

$bar->finish();
$this->newLine();
```

## Praktische use cases

### Een data-importcommando

Een praktisch voorbeeld van een commando dat productdata importeert uit een CSV-bestand.

<Steps>
  <Step title="Genereer het commando">
    ```shell theme={null}
    php artisan make:command ImportProducts
    ```
  </Step>

  <Step title="Implementeer het commando">
    ```php theme={null}
    <?php

    namespace App\Console\Commands;

    use App\Models\Product;
    use Illuminate\Console\Command;

    class ImportProducts extends Command
    {
        protected $signature = 'import:products
                                {file : Pad naar het CSV-bestand}
                                {--limit= : Maximaal aantal te importeren records}
                                {--dry-run : Alleen controleren (niet opslaan in de DB)}';

        protected $description = 'Importeert productdata uit een CSV-bestand';

        public function handle(): void
        {
            $filePath = $this->argument('file');
            $limit    = $this->option('limit') ? (int) $this->option('limit') : null;
            $dryRun   = $this->option('dry-run');

            if (! file_exists($filePath)) {
                $this->error("Bestand niet gevonden: {$filePath}");
                return;
            }

            // De CSV inlezen met ingebouwde PHP-functies
            $handle = fopen($filePath, 'r');
            $headers = fgetcsv($handle); // De eerste regel als header ophalen
            $rows = [];
            while (($row = fgetcsv($handle)) !== false) {
                $rows[] = array_combine($headers, $row);
                if ($limit !== null && count($rows) >= $limit) {
                    break;
                }
            }
            fclose($handle);

            $count = 0;

            $this->withProgressBar($rows, function (array $row) use ($dryRun, &$count) {
                if (! $dryRun) {
                    Product::updateOrCreate(
                        ['sku' => $row['sku']],
                        [
                            'name'  => $row['name'],
                            'price' => $row['price'],
                        ]
                    );
                }
                $count++;
            });

            $this->newLine();

            if ($dryRun) {
                $this->warn("{$count} records zouden worden verwerkt (dry-run: opslaan overgeslagen)");
            } else {
                $this->info("Import van {$count} records voltooid.");
            }
        }
    }
    ```
  </Step>

  <Step title="Voer het commando uit">
    ```shell theme={null}
    # Normale import
    php artisan import:products storage/products.csv

    # Het aantal beperken
    php artisan import:products storage/products.csv --limit=100

    # Controleren met een dry run
    php artisan import:products storage/products.csv --dry-run
    ```
  </Step>
</Steps>

### Een periodiek onderhoudscommando

Een voorbeeld van een onderhoudscommando dat je periodiek uitvoert, zoals het verwijderen van oude data.

```php theme={null}
<?php

namespace App\Console\Commands;

use App\Models\Order;
use Illuminate\Console\Command;

class PruneOldOrders extends Command
{
    protected $signature = 'orders:prune {--days=90 : Vanaf hoeveel dagen oud data wordt verwijderd}';

    protected $description = 'Verwijdert voltooide bestellingen die ouder zijn dan het opgegeven aantal dagen';

    public function handle(): void
    {
        $days = (int) $this->option('days');

        if (! $this->confirm("Voltooide bestellingen ouder dan {$days} dagen verwijderen?")) {
            $this->info('Geannuleerd.');
            return;
        }

        $deleted = Order::where('status', 'completed')
            ->where('created_at', '<', now()->subDays($days))
            ->delete();

        $this->info("{$deleted} bestellingen verwijderd.");
    }
}
```

### Combineren met geplande uitvoering

Gemaakte commando's kun je inplannen in `routes/console.php`.
Zie [taakplanning](/nl/scheduling) voor meer details.

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

// Elke dag om 2:00 uur 's nachts uitvoeren
Schedule::command('orders:prune --days=90')->dailyAt('2:00');

// Elke maandag om 9:00 uur uitvoeren
Schedule::command('import:products storage/weekly.csv')->weeklyOn(1, '9:00');
```

## Commando's testen

Met de testfunctionaliteit van Laravel kun je de werking van Artisan-commando's verifiëren.

```php theme={null}
<?php

namespace Tests\Feature\Commands;

use App\Models\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PruneOldOrdersTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_deletes_old_completed_orders(): void
    {
        // Een afgeronde bestelling van meer dan 90 dagen geleden aanmaken
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(100),
        ]);

        // Een recente afgeronde bestelling (zou niet verwijderd mogen worden)
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(10),
        ]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Voltooide bestellingen ouder dan 90 dagen verwijderen?', 'yes')
            ->expectsOutput('1 bestellingen verwijderd.')
            ->assertExitCode(0);

        $this->assertDatabaseCount('orders', 1);
    }

    public function test_it_cancels_when_user_declines(): void
    {
        Order::factory()->create(['status' => 'completed', 'created_at' => now()->subDays(100)]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Voltooide bestellingen ouder dan 90 dagen verwijderen?', 'no')
            ->expectsOutput('Geannuleerd.')
            ->assertExitCode(0);

        $this->assertDatabaseCount('orders', 1);
    }
}
```

<Info>
  Voorbeelden van assertion-methoden die je met de `artisan()`-methode kunt gebruiken:

  | Methode                            | Beschrijving                                       |
  | ---------------------------------- | -------------------------------------------------- |
  | `expectsOutput('...')`             | Verifieert dat de opgegeven tekst wordt uitgevoerd |
  | `expectsQuestion('?', 'antwoord')` | Simuleert de invoer van `ask()`                    |
  | `expectsConfirmation('?', 'yes')`  | Simuleert het antwoord van `confirm()`             |
  | `expectsChoice('?', 'keuze')`      | Simuleert de keuze van `choice()`                  |
  | `assertExitCode(0)`                | Verifieert de exitcode (0 = succes)                |
  | `assertFailed()`                   | Verifieert dat de exitcode niet 0 is               |
</Info>

## Het `dev`-commando

Het Artisan-commando `dev` start de verschillende processen die je nodig hebt voor lokale ontwikkeling samen in één terminalvenster. Standaard draaien de PHP-ontwikkelserver, de queue worker, logbewaking via [Pail](/nl/logging) en het compileren van assets met Vite parallel.

```shell theme={null}
php artisan dev
```

Intern worden de processen beheerd met het npm-pakket [`@laravel/multiplex`](https://github.com/laravel/multiplex). Elk proces krijgt een eigen tab, waarin je de uitvoer kunt doorzoeken en scrollen. Crasht een proces, dan wordt het automatisch herstart, en bij afsluiten wordt de uitvoer teruggeschreven naar de scrollback van de terminal, zodat je geen logs verliest.

Het `dev`-commando vereist Node.js 22.13 of hoger. Op Windows wordt teruggevallen op het npm-pakket `concurrently`, waardoor de UI met tabs niet beschikbaar is.

De processen die standaard worden uitgevoerd zijn:

| Naam     | Commando                                         |
| -------- | ------------------------------------------------ |
| `server` | `php artisan serve --host=localhost`             |
| `queue`  | `php artisan queue:listen --tries=1 --timeout=0` |
| `logs`   | `php artisan pail --timeout=0`                   |
| `vite`   | `npm run dev`                                    |

<Info>
  Het `vite`-proces detecteert automatisch welke Node-pakketmanager je gebruikt (npm, pnpm, Yarn of Bun) en gebruikt het bijbehorende uitvoercommando.
</Info>

Processen worden maximaal 5 keer herstart. Een proces dat direct na de start (binnen 1 seconde) stopt, wordt niet herstart, omdat dat waarschijnlijk duidt op een fout in het commando of een poortconflict. Om herstarten voor één uitvoering uit te schakelen, geef je `--no-restart` op.

```shell theme={null}
php artisan dev --no-restart
```

### De `dev`-processen aanpassen

De processen die het `dev`-commando uitvoert, kun je aanpassen met de klasse `DevCommands`. Meestal registreer je ze in de `boot`-methode van de `AppServiceProvider` van je applicatie. De `register`-methode ontvangt een commandostring en optioneel een procesnaam.

```php theme={null}
use Illuminate\Foundation\DevCommands;

/**
 * Bootstrappen van applicatieservices
 */
public function boot(): void
{
    DevCommands::register('some-command --flag', 'my-process');
}
```

Wil je een Artisan-commando registreren, dan kun je de `artisan`-methode gebruiken, die automatisch `php artisan` vooraan zet.

```php theme={null}
DevCommands::artisan('horizon', 'horizon');
```

Op dezelfde manier zet de `node`-methode het runcommando van de gedetecteerde pakketmanager (bijv. `npm run`) vooraan, en de `nodeExec`-methode het exec-commando van de pakketmanager (bijv. `npx`).

```php theme={null}
DevCommands::node('storybook', 'storybook');

DevCommands::nodeExec('tailwindcss -i resources/css/app.css -o public/css/app.css --watch', 'tailwind');
```

Registreer je een proces met dezelfde naam als een standaardproces, dan vervangt het dat standaardproces. Zo kun je bijvoorbeeld het serverproces aanpassen om een andere poort te gebruiken.

```php theme={null}
DevCommands::artisan('serve --host=localhost --port=9000', 'server');
```

Ook de kleur van het proceslabel in de terminal kun je aanpassen. De beschikbare kleurmethoden zijn `blue`, `purple`, `pink`, `orange`, `green` en `yellow`. Je kunt ook een eigen hexkleur doorgeven aan de `color`-methode.

```php theme={null}
DevCommands::register('my-command', 'my-process')->green();

DevCommands::register('my-command', 'my-process')->color('#ff6347');
```

Wil je de geregistreerde `dev`-processen bekijken zonder ze daadwerkelijk te starten, gebruik dan het `dev:list`-commando.

```shell theme={null}
php artisan dev:list
```

### De `dev`-processen filteren

Met de `only`-methode geef je aan dat bij het uitvoeren van het `dev`-commando alleen specifieke processen moeten starten. Evenzo kun je met de `except`-methode specifieke processen uitsluiten.

```php theme={null}
// Alleen de processen server en vite starten...
DevCommands::only('server', 'vite');

// Alle processen behalve de queue worker starten...
DevCommands::except('queue');
```

Wil je `dev`-commando's uitsluiten die door pakketten zijn geregistreerd, of de standaardcommando's van Laravel, gebruik dan de methoden `withoutVendorCommands` en `withoutDefaultCommands`.

```php theme={null}
DevCommands::withoutVendorCommands();

DevCommands::withoutDefaultCommands();
```

<Tip>
  Gebruik je bij lokale ontwikkeling ook Horizon of Reverb, registreer ze dan met `DevCommands::artisan()`. Dan start je alles handig in één keer met `php artisan dev`.
</Tip>

## Overzicht van veelgebruikte commando's

```shell theme={null}
# Een nieuw commando maken
php artisan make:command CommandName

# Overzicht van beschikbare commando's
php artisan list

# Het gebruik van een commando bekijken
php artisan help command:name

# Tinker starten
php artisan tinker
```


## Related topics

- [Laravel Prompts](/nl/prompts.md)
- [Processen](/nl/processes.md)
- [Uitgestelde service providers](/nl/advanced/deferred-provider.md)
- [Laravel Console Starter](/nl/packages/laravel-console-starter/index.md)
- [Tutorial - Laravel Console Starter](/nl/packages/laravel-console-starter/tutorial.md)
