Artisan: як створити користувацькі команди make

Artisan: як створити користувацькі команди make

Досить зручно створювати класи з консолі. Та що робити у випадку, коли в artisan немає необхідних команд? Що ж, ніхто не заважає нам самим зробити своє життя простішим і комфортнішим.

Оскільки особисто я часто пишу трейти і інтерфейси, саме їх буду використовувати в якості прикладів.

Перш за все заглянемо під капот і поцікавимося, яким чином laravel генерує класи/файли каналів, подій, контролерів і т.д. Для цього зайдемо в директорію vendor/laravel/framework/src/Illuminate/Foundation/Console, в якій виявимо цілу пачку класів команд. Переглянувши файли, назва яких включає make ми зрозуміємо, що всі вони є нащадками класу GeneratorCommand, в якому є практично все, що потрібно - створення директорій і файлів, побудова класів, заміна неймспейсів і багато іншого. Відмінно - особливо бруднити руки не доведеться. Також звернімо увагу на абстрактний метод getStub:

/**
 * Get the stub file for the generator.
 *
 * @return string
 */
abstract protected function getStub();

 

Цей метод ми зобов'язані реалізувати і вказати в ньому шлях до файлу, який буде використовуватися генератором. У тій же самій директорії також знайдемо теку з файлами-шаблонами. Наприклад, так виглядає шаблон model.stub для моделі:

<?php

namespace DummyNamespace;

use Illuminate\Database\Eloquent\Model;

class DummyClass extends Model
{
    //
}

 

Тобто нам залишається за наявними зразками реалізувати свої власні класи плюс під них написати шаблони. Виконуємо в консолі:

php artisan make:command TraitMakeCommand

 

В результаті буде створений файл app/Console/Commands/TraitMakeCommand.php. Відкриваємо його і вставляємо наступний код:

<?php

namespace App\Console\Commands;

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

class TraitMakeCommand extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $name = 'make:trait';

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

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

    /**
     * Replace the class name for the given stub.
     *
     * @param  string  $stub
     * @param  string  $name
     * @return string
     */
    protected function replaceClass($stub, $name)
    {
        $stub = parent::replaceClass($stub, $name);
        return str_replace('DummyTrait', $this->argument('name'), $stub);
    }

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

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

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return [
            ['name', InputArgument::REQUIRED, 'The name of the trait.'],
        ];
    }
}

 

Хоча код говорить сам за себе, все ж поясню: у властивостях ми вказали назву і опис команди, а також тип "класу", який буде згенеровано. Сказали, що буде один обов'язковий аргумент - назва трейту (метод getArguments); пояснили, де взяти шаблон (getStub); дали вказівки щодо неймспейса (getDefaultNamespace, це, до речі, означає, що якщо у нас немає директорії app/Traits - вона буде створена); і попросили замінити DummyTrait на ту назву, яку ми передамо як аргумент команди (replaceClass). Тепер в app/Console створимо каталог stubs, в якому помістимо шаблон trait.stub. Ось його код:

<?php

namespace DummyNamespace;

trait DummyTrait
{
    //
}

 

Чи потрібно щось ще? Ні, оскільки відповідно до офіційної документації всі команди, що знаходяться в app/Console/Commands будуть автоматично зареєстровані в Artisan. Перевірити, так це чи ні, можна виконавши:

php artisan list

 

і дійсно так! Серед усього іншого побачимо:

make:trait           Create a new trait

 

Пробуємо:

php artisan make:trait Billable

 

...і знаходимо в app/Traits файл Billable.php з таким вмістом:

<?php

namespace App\Traits;

trait Billable
{
    //
}

 

Що й треба було. Для контрактів (інтерфейсів) процедура аналогічна. Ось код файлу app/Console/Commands/ContractMakeComand.php:

<?php

namespace App\Console\Commands;

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

class ContractMakeCommand extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $name = 'make:contract';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a new contract interface';

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

    /**
     * Replace the class name for the given stub.
     *
     * @param  string  $stub
     * @param  string  $name
     * @return string
     */
    protected function replaceClass($stub, $name)
    {
        $stub = parent::replaceClass($stub, $name);
        return str_replace('DummyContract', $this->argument('name'), $stub);
    }

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

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

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return [
            ['name', InputArgument::REQUIRED, 'The name of the contract.'],
        ];
    }
}

 

і його шаблону:

<?php

namespace DummyNamespace;

interface DummyContract
{
    //
}

 

Приклад команди:

php artisan make:contract Flyable

 

На цьому на сьогодні все. Успіхів!