• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 11/05/2020

Les commandes et les assistants

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Dans les chapitres précédents on a plusieurs fois dû créer des migrations, des populations, des contrôleurs, des modèles... Artisan possède des commandes pour effectuer certaines de ces opérations mais il ne va pas bien loin et il nous a fallu créer pas mal de code qui, de toute évidence, pourrait plus ou moins facilement être automatisé.

Il est possible d'améliorer les commandes d'artisan ou même de s'en créer des nouvelles. Il existe aussi des assistants pour nous aider dans ces tâches un peu pénibles ou répétitives. 

Améliorer une commande

Il existe dans Artisan une commande pour générer un modèle :

php artisan make:model MonModele

Voici ce qu'on obtient :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class MonModele extends Model
{
    //
}

Ce n'est pas grand chose mais c'est déjà ça, une coquille un peu vide.

Si on a prévu de placer les modèles dans un dossier spécifique il suffit de renseigner l'espace de nom lors de la commande :

php artisan make:model App\Models\MonModele

On a vu qu'il était pratique d'avoir une propriété$fillable pour répertorier les colonnes qu'on peut mettre à jour sans risque avec un assignement de masse. Ce qui serait bien serait d'avoir une option pour ajouter cette propriété.

Si vous regardez dans le dossier des commandes "console" on voit qu'il en existe déjà une :

Les commandes "console"

Il s'agit d'un exemple qui peut vous guider pour réaliser une commande.

Puisque la commande existe déjà dans Artisan on ne va pas réinventer la roue et nous contenter d'étendre les possibilités de celle-ci. Il faut un peu fouiller dans le framework pour trouver cette commande :

La commande ModelMakeCommand de Laravel

Avec ce code :

<?php

namespace Illuminate\Foundation\Console;

use Illuminate\Console\GeneratorCommand;
use Symfony\Component\Console\Input\InputOption;

class ModelMakeCommand extends GeneratorCommand
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'make:model';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a new Eloquent model class';

    /**
     * The type of class being generated.
     *
     * @var string
     */
    protected $type = 'Model';

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        if (parent::fire() !== false) {
            if ($this->option('migration')) {
                $table = str_plural(snake_case(class_basename($this->argument('name'))));

                $this->call('make:migration', ['name' => "create_{$table}_table", '--create' => $table]);
            }
        }
    }

    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub()
    {
        return __DIR__.'/stubs/model.stub';
    }

    /**
     * Get the default namespace for the class.
     *
     * @param  string  $rootNamespace
     * @return string
     */
    protected function getDefaultNamespace($rootNamespace)
    {
        return $rootNamespace;
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['migration', 'm', InputOption::VALUE_NONE, 'Create a new migration file for the model.'],
        ];
    }
}

La commande utilise un gabarit (stub) qu'on trouve ici :

Le stub de la commande

Avec ce code :

<?php

namespace DummyNamespace;

use Illuminate\Database\Eloquent\Model;

class DummyClass extends Model
{
    //
}

Les "Dummy" permettent d'avoir des emplacements aux données variables, ici le nom de l'espace de nom et celui de la classe.

On va commencer par créer notre classe en étendant celle de Laravel. Alors créons la nouvelle commande avec artisan :

php artisan make:console ModelMakeCommand

On la retrouve ici :

Notre commande

Avec ce code généré :

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ModelMakeCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
    }
}

On l'enregistre dansapp\Console\Kernel.php  :

<?php
protected $commands = [
    Commands\Inspire::class,
    Commands\ModelMakeCommand::class,
];

On va aussi créer notre stub :

Notre stub
Notre stub

Avec ce code pour ajouter la propriété$fillable :

<?php

namespace DummyNamespace;

use Illuminate\Database\Eloquent\Model;

class DummyClass extends Model  {

	/**
	 * Attributes that should be mass-assignable.
	 *
	 * @var array
	 */
	protected $fillable = [DummyFillable];

}

Il ne nous reste plus qu'à modifier le code généré automatiquement pour d'une part étendre la classe de Laravel, et d'autre part assurer le fonctionnement de l'optionfillable :

<?php

namespace App\Console\Commands;

use Illuminate\Foundation\Console\ModelMakeCommand as BaseModelCommand;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Support\Facades\Schema as Schema;

class ModelMakeCommand extends BaseModelCommand
{

    protected $exclude = ['id', 'password', 'created_at', 'updated_at'];

    protected function getStub()
    {
        return __DIR__.'/stubs/model.stub';
    }

    protected function buildClass($name)
    {
        $stub = $this->files->get($this->getStub());

        return $this->replaceNamespace($stub, $name)
        ->replaceFillable($stub)
        ->replaceClass($stub, $name);
    }

    protected function replaceFillable(&$stub)
    {
        if($this->input->getOption('fillable'))
        {
            // On construit le nom de la table à partir du nom du modèle
            $table = str_plural(strtolower($this->getNameInput()));
            // On récupère le nom des colonnes
            $columns = Schema::getColumnListing($table);
            // On exclut les colonnes non désirées 
            $columns = array_filter($columns, function($value)
            {
              return !in_array($value, $this->exclude);
            });
            // On ajoute des apostrophes
            array_walk($columns, function(&$value) {
                $value = "'" . $value . "'";
            });
            // CSV format
            $columns = implode(',', $columns);
        }

        $stub = str_replace('DummyFillable', isset($columns)? $columns : '', $stub);

        return $this;
    }

    protected function getOptions()
    {
        return [
            ['migration', 'm', InputOption::VALUE_NONE, 'Create a new migration file for the model.'],
            ['fillable', null, InputOption::VALUE_NONE, 'Set the fillable columns.', null]
        ];
    }
}

Je ne vais pas détailler le code de cette commande, je vous laisse l'analyser si vous désirez en comprendre le fonctionnement. Le but est juste de montrer qu'il est possible de le réaliser avec un exemple concret.

On va tester cette commande avec la tableposts qui nous a servi pour le petit blog :

php artisan make:model Post --fillable

On obtient ce modèle :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model  {

	/**
	 * Attributes that should be mass-assignable.
	 *
	 * @var array
	 */
	protected $fillable = ['titre','contenu','user_id'];

}

On a bien eu la création de la propriété et le remplissage du tableau avec exclusion des colonnes prévues.

Laravel Schema Designer

Le designer de schéma que vous pouvez trouver ici est une démarche visuelle et efficace. Il suffit de dessiner les tables et de renseigner les champs et relations et ensuite sont générés pour nous :

  • migrations,

  • populations,

  • modèles.

Prenons un exemple avec des livres et des auteurs et une relation 1:n entre elles.

La première chose consiste à créer un compte, ce qui est gratuit. Ensuite vous donnez un nom à votre base :

Le nom de la base
Le nom de la base

Création des tables

Ensuite vous cliquez sur le bouton pour ajouter une table :

Créer une table
Créer une table

Ensuite vous renseignez les champs :

Le formulaire de création d'une table
Le formulaire de création d'une table

Vous avez alors votre table visuellement à l'écran :

La table des livres
La table des livres

Créez de la même manière la table des auteurs (vous pouvez jouer avec les couleurs pour bien distinguer les tables, ce qui s'avère utile dès qu'il y en a beaucoup) :

La table des auteurs
La table des auteurs

Création des champs

On va ensuite renseigner les champs pour les tables. Commençons par les auteurs. Il suffit de cliquer sur le bouton comportant le signe "+" :

Ajout d'un champ
Ajout d'un champ

Le formulaire est très complet, il suffit de le renseigner :

Le formulaire de création d'un champ
Le formulaire de création d'un champ

Après validation on voit notre nouveau champ dans la table :

Le champ ajouté
Le champ ajouté

Deux boutons sont présents pour le modifier ou le supprimer. On va de la même manière renseigner la table des livres avec le titre et la clé étrangère :

La clé étrangère

On se retrouve donc avec nos deux tables et leurs champs renseignés :

Les deux tables créées avec leurs champs renseignés

Création des relations

Il ne nous reste plus qu'à définir les relations. Commençons par les auteurs. Il faut cliquer sur ce bouton :

Le bouton de création d'une relation
Le bouton de création d'une relation

On a alors ce formulaire à compléter :

Le formulaire de création de la relation
Le formulaire de création de la relation

Avec ensuite la liste des relations existantes :

Les relations existantes
Les relations existantes

Evidemment nous n'en avons qu'une. La relation apparaît visuellement sous la forme d'une liaison avec apparition du nom de la méthode au survol de la souris :

La relation représentée visuellement

Pratique non ? :)

De la même manière on crée la relation vue du côté des livres :

La relation vue du côté des livres
La relation vue du côté des livres

On a également une représentation visuelle avec le nom de la méthode qui apparaît au survol :

La représentation visuelle de la méthode belongsTo

Pour ajouter la population il faut cliquer sur ce bouton :

Le bouton pour la population
Le bouton pour la population

On a alors un formulaire à compléter :

Le formulaire pour la population

Exportation des fichiers

Maintenant notre schéma est terminé, il ne reste plus qu'à exporter les fichiers :

Le menu de l'exportation

On va tout demander en cliquant directement sur "EXPORT ALL". On reçoit un fichier compressé avec tout ça :

Les fichiers exportés

On a donc les migrations, par exemple celle pour les auteurs :

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateAuteursTable extends Migration {

    public function up()
	{
		Schema::create('auteurs', function(Blueprint $table) {
			$table->increments('id');
			$table->timestamps();
			$table->string('nom', 100)->unique();
		});
	}

	public function down()
	{
		Schema::drop('auteurs');
	}
}

Une migration spécifique pour les clés étrangères :

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateForeignKeys extends Migration {

    public function up()
	{
		Schema::table('livres', function(Blueprint $table) {
			$table->foreign('auteur_id')->references('id')->on('livres')
						->onDelete('restrict')
						->onUpdate('restrict');
		});
	}

	public function down()
	{
		Schema::table('livres', function(Blueprint $table) {
			$table->dropForeign('livres_auteur_id_foreign');
		});
	}
}

La population qu'on a prévue pour un auteur :

<?php

class AuteurTableSeeder extends Seeder {

    public function run()
	{
		//DB::table('auteurs')->delete();

		// AuteurTableSeeder
		Auteur::create(array(
				'nom' => 'Dupont'
			));
	}
}

Avec le DatabaseSeeder à jour :

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;

class DatabaseSeeder extends Seeder {

    public function run()
	{
		Model::unguard();

		$this->call('AuteurTableSeeder');
		$this->command->info('Auteur table seeded!');
	}
}

Les modèles, par exemple pour les auteurs :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Auteur extends Model {

    protected $table = 'auteurs';
	public $timestamps = true;
	protected $fillable = array('nom');

	public function livres()
	{
		return $this->hasMany('Livre');
	}

}

On a même des vues avec un squelette de formulaire, par exemple pour les auteurs :

{!! Form::open(array('route' => 'route.name', 'method' => 'POST')) !!}
    <ul>
		<li>
			{!! Form::label('nom', 'Nom:') !!}
			{!! Form::text('nom') !!}
		</li>
		<li>
			{!! Form::submit() !!}
		</li>
	</ul>
{!! Form::close() !!}

Ce générateur crée aussi des contrôleurs et est vraiment très convivial avec son interface graphique.

En résumé

  • Artisan propose un grand arsenal de commandes pour générer du code. Il est possible de créer de nouvelles commandes ou d'étendre les possibilités des commandes existantes.

  • Le designer de schéma permet, à partir d'une interface visuelle conviviale, de générer des migrations, des modèles, des populations, des contrôleurs, des vues.

Exemple de certificat de réussite
Exemple de certificat de réussite