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 :
Pour l’aide d’une commande :
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.
É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)}';
| Notation | Type | Description |
|---|
{file} | Argument obligatoire | Erreur si omis |
{file?} | Argument optionnel | null si omis |
{file=products.csv} | Argument avec défaut | Valeur par défaut |
{--queue} | Option booléenne | true si présente, sinon false |
{--limit=} | Option à valeur | --limit=100 |
{--limit=50} | Option avec défaut | 50 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() 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.
Générer la commande
php artisan make:command ImportProducts
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.");
}
}
}
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éthode | Description |
|---|
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