{{TOC}} {{DOCVER 5.0=f6e862ba51a97f2bf9865f35fd5c522c14af93a1 31.07.2015 15:36:24}} .(alert) Данная статья документации актуальна только для версии 5.0 и была удалена в версии 5.1. == Введение == Командная шина Laravel предоставляет удобный способ инкапсуляции задач вашего приложения в простые и понятные "команды". Чтобы понять назначение команд, давайте представим, что мы создаём приложение, которое позволяет пользователям покупать подкасты. Когда пользователь покупает подкаст, должно произойти несколько событий. Например, мы должны снять деньги с карты пользователя, добавить запись о покупке в нашу базу данных и послать электронное письмо с подтверждением покупки. Возможно, мы также должны выполнить некоторые проверки, например, разрешено ли пользователю вообще покупать подкасты. Мы могли бы поместить всю эту логику в метод контроллера. Однако у этого подхода есть несколько недостатков. Во-первых, наш контроллер, вероятно, обрабатывает и несколько других входящих HTTP-действий, поэтому включение сложной логики в каждый метод контроллера приведёт к его быстрому разрастанию, и его будет сложно читать. Во-вторых, будет трудно снова использовать логику покупки подкаста за пределами контекста контроллера. В-третьих, будет сложнее проводить юнит-тесты команд, поскольку нам придётся также генерировать заглушки для HTTP-запросов и выполнять полноценный запрос к приложению, чтобы протестировать логику покупки подкаста. Вместо размещения этой логики в контроллере, мы можем принять решение инкапсулировать её в объект "команды", такой как команда %%PurchasePodcast%%. == Создание команд == Командная строка Artisan может генерировать новые классы команды, используя команду %%(sh)make:command%%: %%(sh) php artisan make:command PurchasePodcast %% Только что сгенерированный класс будет помещён в каталог %%(t)app/Commands%%. По умолчанию команда содержит два метода: конструктор и метод %%handle%%. Конструктор позволяет вам передавать любые соответствующие объекты команде, в то время как метод %%handle%% выполняет команду. Например: %% class PurchasePodcast extends Command implements SelfHandling { protected $user, $podcast; /** * Создание нового экземпляра команды. * * @return void */ public function __construct(User $user, Podcast $podcast) { $this->user = $user; $this->podcast = $podcast; } /** * Выполнение команды. * * @return void */ public function handle() { // Логика покупки подкаста... event(new PodcastWasPurchased($this->user, $this->podcast)); } } %% Метод %%handle%% может использовать указание типов зависимостей, и они будут автоматически внедрены ((/docs/v5/container сервис-контейнером)). %% /** * Выполнение команды. * * @return void */ public function handle(BillingGateway $billing) { // Логика покупки подкаста... } %% == Выполнение команд == Как выполнить только что созданную команду? Можно непосредственно вызвать метод %%handle%%. Однако, выполнение команд через "командную шину" Laravel имеет ряд преимуществ, которые мы обсудим далее. Если вы посмотрите на основной контроллер своего приложения, то вы заметите типаж %%DispatchesCommands%%. Он позволяет вызывать метод %%dispatch%% из любого контроллера. %% public function purchasePodcast($podcastId) { $this->dispatch( new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId)) ); } %% Командная шина займётся выполнением команды и вызовом IoC-контейнера для внедрения любых необходимых зависимостей в метод %%handle%%. Вы можете добавить типаж %%(t)Illuminate\Foundation\Bus\DispatchesCommands%% в любой класс. Если вы хотите получить экземпляр командной шины через конструктор какого-либо из ваших классов, вы можете указать тип интерфейса %%Illuminate\Contracts\Bus\Dispatcher%%. Наконец, вы можете просто использовать фасад %%Bus%%, чтобы быстро выполнить команду: %% Bus::dispatch( new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId)) ); %% === Передача параметров в запросах === Передача переменных HTTP-запроса в команды очень распространена. Поэтому, вместо того чтобы делать это вручную для каждого запроса, Laravel предоставляет некоторые вспомогательные методы. Давайте взглянем на метод %%dispatchFrom%%, доступный в типаже %%DispatchesCommands%%: %% $this->dispatchFrom('Command\Class\Name', $request); %% Этот метод проверяет конструктор класса команды, который он получил первым аргументом, и затем извлекает переменные из HTTP-запроса (или любого другого объекта типа %%ArrayAccess%%), чтобы заполнить необходимые параметры конструктора команды. Так, если наш класс команды примет аргумент %%firstName%% в своём конструкторе, то командная шина попытается получить параметр %%firstName%% из HTTP-запроса. Вы можете также передать массив как третий аргумент метода %%dispatchFrom%%. Этот массив будет использоваться, чтобы заполнить любые параметры конструктора, которые не доступны из запроса: %% $this->dispatchFrom('Command\Class\Name', $request, [ 'firstName' => 'Taylor', ]); %% == Очередь команд == Командная шина предназначена не только для синхронных задач, которые работают во время прохождения текущего запроса. Она также служит основным методом создания очередей задач в Laravel. Так как же заставить командную шину поставить нашу задачу в очередь для фоновой обработки вместо того, чтобы выполнить её синхронно? Очень просто. Во-первых, при генерации новой команды, просто добавьте к команде флаг %%(sh)--queued%%: %%(sh) php artisan make:command PurchasePodcast --queued %% И вы увидите, это добавляет ещё несколько функций к команде, а именно, интерфейс %%(t)Illuminate\Contracts\Queue\ShouldBeQueued%% и типаж %%SerializesModels%%. С помощью них командная шина ставит команды в очередь, а также корректно сериализует и десериализует любые Eloquent-модели, которые содержатся в вашей команде как свойства. Если вы хотите преобразовать существующую команду в команду для очереди, просто реализуйте интерфейс %%(t)Illuminate\Contracts\Queue\ShouldBeQueued%% в классе вручную. Он не содержит методов и просто служит индикатором для командной шины. Теперь просто пишите свою команду как обычно. Шина автоматически поставит команду в очередь для фоновой обработки. Для получения дополнительной информации о взаимодействии с очередью команд просмотрите полную ((/docs/v5/queues документацию по очередям)). == Конвейер команд == Прежде чем команда передастся в обработчик, вы можете передать её через другие классы на "конвейер". Каналы команд работают как HTTP-middleware, за исключением ваших команд! Например, канал команды может обернуть всю работу команды в транзакцию базы данных, или просто зарегистрировать в логе её выполнение. Чтобы добавить канал в вашу шину, вызовите метод диспетчера %%pipeThrough%% из вашего метода %%App\Providers\BusServiceProvider::boot%%: %% $dispatcher->pipeThrough(['UseDatabaseTransactions', 'LogCommand']); %% Канал команды определяется в методе %%handle%%, подобно middleware: %% class UseDatabaseTransactions { public function handle($command, $next) { return DB::transaction(function() use ($command, $next) { return $next($command); }); } } %% Классы каналов команд выполняются через ((/docs/v5/container IoC-контейнер)), поэтому свободно указывайте типы любых необходимых зависимостей в их конструкторах. Вы можете даже определить %%Closure%% в качестве канала команды: %% $dispatcher->pipeThrough([function($command, $next) { return DB::transaction(function() use ($command, $next) { return $next($command); }); }]); %%