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

> Erfahren Sie, wie Sie mit der Artisan-Konsole von Laravel eigene CLI-Befehle erstellen.

## Was ist Artisan

Artisan ist die Kommandozeilenschnittstelle (CLI), die Laravel mitbringt.
Sie liegt als Skript `artisan` im Projekt-Root und stellt zahlreiche Befehle bereit, die Ihre Entwicklung und Ihren Betrieb effizienter machen.

Um die verfügbaren Befehle anzuzeigen, verwenden Sie den Befehl `list`.

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

Um sich die Nutzung eines Befehls anzeigen zu lassen, stellen Sie `help` voran.

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

<Info>
  Wenn Sie Laravel Sail verwenden, nutzen Sie statt `php artisan` den Befehl `sail artisan`.
  Der Befehl wird im Docker-Container ausgeführt.

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

## Tinker

[Laravel Tinker](https://github.com/laravel/tinker) ist eine REPL-Umgebung, mit der Sie Eloquent-Modelle, Jobs, Events und die gesamte Anwendung interaktiv auf der Kommandozeile steuern.

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

Nach dem Start können Sie beliebigen PHP-Code direkt ausführen.

```php theme={null}
// Benutzer laden und prüfen
>>> App\Models\User::find(1)
// Einen Datensatz per Factory anlegen
>>> App\Models\User::factory()->create()
// Eine Serviceklasse direkt aufrufen
>>> app(App\Services\OrderService::class)->process(1)
```

<Tip>
  Tinker verändert Daten tatsächlich. In der Produktionsumgebung sollten Sie daher besonders vorsichtig sein.
  Für lokale oder Staging-Umgebungen ist es ideal, um Verhalten zu prüfen und zu debuggen.
</Tip>

## Eigene Befehle erstellen

### Befehl erzeugen

Mit `make:command` erzeugen Sie das Grundgerüst einer Befehlsklasse.

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

Das erzeugt die Datei `app/Console/Commands/ImportProducts.php`.

### Grundaufbau eines Befehls

Die erzeugte Klasse besteht im Wesentlichen aus drei Elementen: `$signature`, `$description` und `handle()`.

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

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ImportProducts extends Command
{
    /**
     * Definition von Befehlsname, Argumenten und Optionen
     */
    protected $signature = 'import:products';

    /**
     * Beschreibung des Befehls (wird in `php artisan list` angezeigt)
     */
    protected $description = 'Produktdaten aus CSV importieren';

    /**
     * Ausführung des Befehls
     */
    public function handle(): void
    {
        $this->info('Import wird gestartet...');
        // Verarbeitung hier
        $this->info('Fertig.');
    }
}
```

<Info>
  In Laravel 13 können Sie auch die Schreibweise über PHP-Attribute verwenden.

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

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

### Argumente und Optionen definieren

Über `$signature` legen Sie Namen, Argumente und Optionen des Befehls gebündelt fest.

```php theme={null}
protected $signature = 'import:products
                        {file : Pfad zur zu importierenden CSV-Datei}
                        {--limit= : Maximale Anzahl zu importierender Einträge}
                        {--dry-run : Nicht in die DB speichern (nur zur Prüfung)}';
```

| Notation              | Typ                  | Beschreibung                             |
| --------------------- | -------------------- | ---------------------------------------- |
| `{file}`              | Pflicht-Argument     | Wird bei Auslassung als Fehler behandelt |
| `{file?}`             | Optionales Argument  | Ohne Angabe `null`                       |
| `{file=products.csv}` | Argument mit Default | Ohne Angabe wird der Default verwendet   |
| `{--queue}`           | Flag-Option          | Vorhanden: `true`, sonst `false`         |
| `{--limit=}`          | Option mit Wert      | Wert über `--limit=100` übergeben        |
| `{--limit=50}`        | Option mit Default   | Ohne Angabe `50`                         |
| `{--Q\|queue}`        | Mit Shortcut         | Auch über `-Q` angebbar                  |

### Argumente und Optionen abrufen

In `handle()` greifen Sie mit `argument()` und `option()` auf die Werte zu.

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

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

    if ($dryRun) {
        $this->warn('Dry-Run-Modus: Speicherung in der DB wird übersprungen');
    }
}
```

### `input()` — typisierte Accessoren

Mit `input()` erhalten Sie typisierten Zugriff auf Argumente und Optionen, ähnlich wie bei HTTP-Requests.

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

public function handle(): void
{
    // Argumente/Optionen als CommandInput-Instanz abrufen
    $from = $this->input()->date('from');
    $type = $this->input()->enum('type', ReportType::class);
    $limit = $this->input()->integer('limit');
}
```

Wenn Sie nur einen bestimmten Namen abrufen möchten, übergeben Sie diesen direkt (es wird sowohl in Argumenten als auch Optionen gesucht).

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

<Tip>
  Während `argument()` und `option()` Strings zurückgeben, konvertieren die typisierten Accessoren von `input()` in den passenden Typ. Praktisch, wenn Sie Datumsangaben, Zahlen oder Enums typsicher verarbeiten möchten.
</Tip>

## Interaktion mit dem Benutzer

### Ausgabe von Nachrichten

Für farbige Konsolenausgaben stehen fertige Methoden bereit.

```php theme={null}
$this->info('Verarbeitung erfolgreich abgeschlossen');    // grün
$this->warn('Diese Aktion kann nicht rückgängig gemacht werden'); // gelb
$this->error('Ein Fehler ist aufgetreten');       // rot
$this->line('Normaler Text');             // ohne Farbe
$this->comment('Zusatzinfo');               // hellgrau
```

### Benutzer befragen

Sie können interaktiv Eingaben vom Benutzer einholen.

```php theme={null}
// Texteingabe
$name = $this->ask('Bitte den Namen des Verantwortlichen eingeben');

// Mit Default
$env = $this->ask('Ausführungsumgebung wählen', 'production');

// Verdeckte Eingabe (z. B. Passwörter, Eingabe nicht sichtbar)
$password = $this->secret('Bitte API-Schlüssel eingeben');

// Ja/Nein-Bestätigung
if (! $this->confirm('In die produktive DB importieren?')) {
    $this->info('Abgebrochen.');
    return;
}

// Auswahl aus Optionen
$format = $this->choice('Ausgabeformat wählen', ['csv', 'json', 'xml'], 0);
```

### Fortschrittsanzeige

Bei der Verarbeitung großer Datenmengen können Sie den Fortschritt übersichtlich anzeigen.

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

// Übergeben Sie eine Collection – der Fortschrittsbalken erscheint automatisch
$this->withProgressBar(Product::cursor(), function (Product $product) {
    $this->processProduct($product);
});
```

Für feinere manuelle Steuerung nutzen Sie folgendes Muster.

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

## Praxisnahe Anwendungsfälle

### Datenimport-Befehl

Ein praktisches Beispiel eines Befehls, der Produktdaten aus einer CSV-Datei importiert.

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

  <Step title="Befehl implementieren">
    ```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 : Pfad zur CSV-Datei}
                                {--limit= : Maximale Anzahl zu importierender Einträge}
                                {--dry-run : Nur zur Prüfung (nicht in die DB speichern)}';

        protected $description = 'Produktdaten aus einer CSV-Datei importieren';

        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("Datei nicht gefunden: {$filePath}");
                return;
            }

            // CSV mit den eingebauten PHP-Funktionen einlesen
            $handle = fopen($filePath, 'r');
            $headers = fgetcsv($handle); // Erste Zeile als Header verwenden
            $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} Einträge betroffen (Dry-Run: Speicherung übersprungen)");
            } else {
                $this->info("{$count} Einträge erfolgreich importiert.");
            }
        }
    }
    ```
  </Step>

  <Step title="Befehl ausführen">
    ```shell theme={null}
    # Normaler Import
    php artisan import:products storage/products.csv

    # Anzahl begrenzen
    php artisan import:products storage/products.csv --limit=100

    # Als Dry-Run prüfen
    php artisan import:products storage/products.csv --dry-run
    ```
  </Step>
</Steps>

### Regelmäßiger Wartungsbefehl

Ein Beispiel für einen regelmäßig ausgeführten Wartungsbefehl, etwa zum Entfernen alter Daten.

```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 : Alter (in Tagen), ab dem gelöscht wird}';

    protected $description = 'Abgeschlossene Bestellungen löschen, die älter als die angegebene Anzahl an Tagen sind';

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

        if (! $this->confirm("Abgeschlossene Bestellungen älter als {$days} Tage löschen?")) {
            $this->info('Abgebrochen.');
            return;
        }

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

        $this->info("{$deleted} Bestellungen gelöscht.");
    }
}
```

### Kombination mit dem Scheduler

Ihre Befehle können in `routes/console.php` geplant ausgeführt werden.
Details finden Sie unter [Task-Scheduling](/de/scheduling).

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

// Täglich um 2 Uhr nachts
Schedule::command('orders:prune --days=90')->dailyAt('2:00');

// Jeden Montag um 9 Uhr
Schedule::command('import:products storage/weekly.csv')->weeklyOn(1, '9:00');
```

## Befehle testen

Mit den Testfunktionen von Laravel können Sie das Verhalten Ihrer Artisan-Befehle überprüfen.

```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
    {
        // Abgeschlossene Bestellung erzeugen, die älter als 90 Tage ist
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(100),
        ]);

        // Kürzlich abgeschlossene Bestellung (sollte erhalten bleiben)
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(10),
        ]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Abgeschlossene Bestellungen älter als 90 Tage löschen?', 'yes')
            ->expectsOutput('1 Bestellungen gelöscht.')
            ->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('Abgeschlossene Bestellungen älter als 90 Tage löschen?', 'no')
            ->expectsOutput('Abgebrochen.')
            ->assertExitCode(0);

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

<Info>
  Beispiele für Assertion-Methoden, die mit `artisan()` genutzt werden können:

  | Methode                           | Beschreibung                                    |
  | --------------------------------- | ----------------------------------------------- |
  | `expectsOutput('...')`            | Prüft, dass der angegebene Text ausgegeben wird |
  | `expectsQuestion('?', 'Antwort')` | Simuliert die Eingabe von `ask()`               |
  | `expectsConfirmation('?', 'yes')` | Simuliert die Antwort auf `confirm()`           |
  | `expectsChoice('?', 'Auswahl')`   | Simuliert die Auswahl von `choice()`            |
  | `assertExitCode(0)`               | Prüft den Exit-Code (0 = Erfolg)                |
  | `assertFailed()`                  | Prüft, dass der Exit-Code nicht 0 ist           |
</Info>

## Häufig verwendete Befehle im Überblick

```shell theme={null}
# Neuen Befehl erzeugen
php artisan make:command CommandName

# Verfügbare Befehle auflisten
php artisan list

# Nutzung eines Befehls anzeigen
php artisan help command:name

# Tinker starten
php artisan tinker
```


## Related topics

- [Laravel Prompts](/de/prompts.md)
- [Prozesse](/de/processes.md)
- [Konsolentests](/de/console-tests.md)
- [Browser-Tests (Dusk)](/de/dusk.md)
- [Tutorial - Laravel Console Starter](/de/packages/laravel-console-starter/tutorial.md)
