
Artisan: как создать пользовательские команды make
05.02.2019 19:45 | Laravel
Довольно удобно создавать классы из консоли. Но что если в 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
На этом на сегодня всё. Успехов!