Passer au contenu principal

Qu’est-ce qu’Artisan

Artisan est l’interface en ligne de commande (CLI) livrée avec Laravel. Il s’agit du script artisan à la racine du projet, qui propose de nombreuses commandes pour accélérer le développement et l’exploitation. Pour lister les commandes disponibles :
php artisan list
Pour l’aide d’une commande :
php artisan help migrate
Avec Laravel Sail, utilisez sail artisan à la place. La commande s’exécute dans le conteneur Docker.
./vendor/bin/sail artisan list

Tinker

Laravel Tinker est un REPL qui permet d’interagir en ligne de commande avec toute l’application : modèles Eloquent, jobs, événements, etc.
php artisan tinker
Écrivez directement du PHP.
// Récupérer un utilisateur
>>> App\Models\User::find(1)
// Créer un enregistrement via une factory
>>> App\Models\User::factory()->create()
// Appeler une classe de service
>>> app(App\Services\OrderService::class)->process(1)
Tinker modifie réellement les données. Faites attention en production. Convient à la vérification et au débogage en local ou en staging.

Créer une commande personnalisée

Générer la commande

Utilisez make:command pour générer le squelette.
php artisan make:command ImportProducts
app/Console/Commands/ImportProducts.php est généré.

Structure de base

La classe générée possède trois éléments clés : $signature, $description et handle().
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ImportProducts extends Command
{
    /**
     * Nom de la commande et définition des arguments/options.
     */
    protected $signature = 'import:products';

    /**
     * Description de la commande (affichée par php artisan list).
     */
    protected $description = 'Importer les données produits depuis un CSV';

    /**
     * Logique d'exécution.
     */
    public function handle(): void
    {
        $this->info('Démarrage de l\'import...');
        // Placez le traitement ici
        $this->info('Terminé.');
    }
}
Sous Laravel 13, vous pouvez aussi utiliser des attributs PHP.
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;

#[Signature('import:products')]
#[Description('Importer les données produits depuis un CSV')]
class ImportProducts extends Command
{
    public function handle(): void
    {
        // ...
    }
}

Définir arguments et options

$signature regroupe le nom, les arguments et les options.
protected $signature = 'import:products
                        {file : chemin du CSV à importer}
                        {--limit= : nombre maximum d\'enregistrements}
                        {--dry-run : n\'enregistre pas en base (vérification)}';
NotationTypeDescription
{file}Argument obligatoireErreur si omis
{file?}Argument optionnelnull si omis
{file=products.csv}Argument avec défautValeur par défaut
{--queue}Option booléennetrue si présente, sinon false
{--limit=}Option à valeur--limit=100
{--limit=50}Option avec défaut50 si omise
{--Q|queue}Raccourci-Q accepté

Lecture des arguments et options

Dans handle(), utilisez argument() et option().
public function handle(): void
{
    $file    = $this->argument('file');
    $limit   = $this->option('limit');
    $dryRun  = $this->option('dry-run');

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

    if ($dryRun) {
        $this->warn('Mode dry-run : rien ne sera enregistré');
    }
}

input() — accesseurs typés

input() permet d’accéder aux arguments/options avec les mêmes accesseurs typés que pour une requête HTTP.
use App\Enums\ReportType;

public function handle(): void
{
    // Récupérer sous forme de CommandInput
    $from = $this->input()->date('from');
    $type = $this->input()->enum('type', ReportType::class);
    $limit = $this->input()->integer('limit');
}
Pour ne récupérer qu’un nom précis, passez-le directement (cherche à la fois dans arguments et options).
$queue = $this->input('queue', 'default');
Alors que argument() et option() retournent des chaînes, input() convertit dans le type approprié. Idéal pour manipuler dates, nombres ou enums en toute sécurité.

Interaction utilisateur

Écrire dans la console

Vous disposez de méthodes pour afficher des messages colorés.
$this->info('Traitement réussi');    // Vert
$this->warn('Action irréversible');  // Jaune
$this->error('Une erreur est survenue'); // Rouge
$this->line('Texte simple');         // Sans couleur
$this->comment('Note');              // Gris clair

Poser une question

Recevez des entrées de manière interactive.
// Saisie texte
$name = $this->ask('Nom du responsable');

// Avec valeur par défaut
$env = $this->ask('Environnement d\'exécution', 'production');

// Champ masqué (mot de passe)
$password = $this->secret('Clé d\'API');

// Oui / Non
if (! $this->confirm('Importer en base de production ?')) {
    $this->info('Annulé.');
    return;
}

// Choix
$format = $this->choice('Format de sortie', ['csv', 'json', 'xml'], 0);

Barre de progression

Affichez la progression pour de gros traitements.
use App\Models\Product;

// Passer une collection : la barre s'affiche automatiquement
$this->withProgressBar(Product::cursor(), function (Product $product) {
    $this->processProduct($product);
});
Pour un contrôle plus fin :
$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();

Cas d’usage

Commande d’import de données

Exemple d’import de produits depuis un CSV.
1

Générer la commande

php artisan make:command ImportProducts
2

Implémentation

<?php

namespace App\Console\Commands;

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

class ImportProducts extends Command
{
    protected $signature = 'import:products
                            {file : chemin du CSV}
                            {--limit= : nombre maximum d\'enregistrements}
                            {--dry-run : vérification (pas d\'enregistrement)}';

    protected $description = 'Importer les données produits depuis un CSV';

    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("Fichier introuvable : {$filePath}");
            return;
        }

        // Lecture CSV avec les fonctions natives PHP
        $handle = fopen($filePath, 'r');
        $headers = fgetcsv($handle); // Première ligne = en-têtes
        $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} enregistrements ciblés (dry-run : rien n'a été sauvegardé)");
        } else {
            $this->info("{$count} enregistrements importés.");
        }
    }
}
3

Exécuter la commande

# Import standard
php artisan import:products storage/products.csv

# Avec limite
php artisan import:products storage/products.csv --limit=100

# Vérification (dry-run)
php artisan import:products storage/products.csv --dry-run

Commande de maintenance périodique

Exemple : suppression de vieilles données.
<?php

namespace App\Console\Commands;

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

class PruneOldOrders extends Command
{
    protected $signature = 'orders:prune {--days=90 : supprimer plus vieilles que N jours}';

    protected $description = 'Supprimer les commandes terminées plus anciennes que N jours';

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

        if (! $this->confirm("Supprimer les commandes terminées de plus de {$days} jours ?")) {
            $this->info('Annulé.');
            return;
        }

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

        $this->info("{$deleted} commandes supprimées.");
    }
}

Combinaison avec la planification

Les commandes créées peuvent être planifiées dans routes/console.php. Voir Planification de tâches.
use Illuminate\Support\Facades\Schedule;

// Tous les jours à 2h du matin
Schedule::command('orders:prune --days=90')->dailyAt('2:00');

// Chaque lundi à 9h
Schedule::command('import:products storage/weekly.csv')->weeklyOn(1, '9:00');

Tester les commandes

La fonctionnalité de test de Laravel permet de vérifier vos commandes Artisan.
<?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
    {
        // Commande terminée de plus de 90 jours
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(100),
        ]);

        // Commande récente (ne doit pas être supprimée)
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(10),
        ]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Supprimer les commandes terminées de plus de 90 jours ?', 'yes')
            ->expectsOutput('1 commandes supprimées.')
            ->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('Supprimer les commandes terminées de plus de 90 jours ?', 'no')
            ->expectsOutput('Annulé.')
            ->assertExitCode(0);

        $this->assertDatabaseCount('orders', 1);
    }
}
Assertions disponibles avec artisan() :
MéthodeDescription
expectsOutput('...')Vérifie qu’un texte est affiché
expectsQuestion('?', 'réponse')Simule une saisie pour ask()
expectsConfirmation('?', 'yes')Simule une réponse à confirm()
expectsChoice('?', 'choix')Simule un choix pour choice()
assertExitCode(0)Vérifie le code de sortie (0 = succès)
assertFailed()Vérifie que le code n’est pas 0

Commandes fréquentes

# Créer une commande
php artisan make:command CommandName

# Lister les commandes
php artisan list

# Aide d'une commande
php artisan help command:name

# Démarrer Tinker
php artisan tinker
Dernière modification le 13 juillet 2026