本文译自 Matt Stauffer 的 系列文章 .

本文中涉及的新功能都是关于 Commands 的,这些特性在 Laravel 旧版本中已经有了,但是在 Laravel 5.0 中变得更加好用了。

I’ll be using examples in this blog post from a new application I’m working on called SaveMyProposals, which allows conference speakers to save talk proposals.

本文中将会用到例子来自于我正在开发的一个叫做 SaveMyProposals 的新应用。<--more-->

什么是 Command, Command handler 和 Command bus?

在 Shawn McCool 的这篇文章 中,你可以深入了解 command, command handler, command bus 的概念。但总的来说:

Command 是一个代表信息的简单对象。它只包含你打算做某件事时需要用到的信息。在我们接下来的例子中,它就是 "复制谈话命令(Duplicate Talk Command)", 任何时候当用户要复制一条谈话建议时,我们的系统就会调用它。 这个 "重复谈话命令" 会包含复制一个谈话所需要的全部属性集——比如一个序列化的 Talk 对象或者是 TaldId.

Command Handler 则是用于对 Command 做出响应的一个类。Command 可以在一个或多个 Handlers 之间传递, 每个 Handler 从 Command 中取出重要的信息并做某些操作来响应。

Command bus 是一套用于调度 Commands 的系统。它把 commands 与对应的 Handlers 进行匹配,并使它们能够一起工作。一般情况下,人们需要编写自己的 command bus, 但 Laravel 内置了一个开箱即用的 Command bus, 所以至少在本文涉及的范围内我们不用担心这个问题。

在 Laravel 中使用 Command

在开始介绍 Laravel 5.0 中使用 Command 的整个结构之前,我们先看看最终的用例是怎样的。假设一个用户访问了系统的某个路由,比如 savemyproposals.com/talks/12345/duplicate , 该路由被解析到 TalkController@duplicate(12345) .

下面是处理这个请求的路由和方法:

// Http\Controllers\TalkController
...
public function duplicate($talkId)
{
$talk = Talk::findOrFail($talkId);
$this->dispatch(new DuplicateTalkCommand($talk));
// 取决于具体的实现,这两行代码也可能简化为一行代码:
// $this->dispatch(new DuplicateTalkCommand($talkId));
}

接下来是 Command 的代码:

// Commands\DuplicateTalkCommand
... class DuplicateTalkCommand extends Command
{
public $talk; public function __construct(Talk $talk)
{
$this->talk = $talk;
}
}

然后是 Command handler:

// Handlers\Commands\DuplicateTalkCommandHandler
... class DuplicateTalkCommandHandler
{
public function handle(DuplicateTalkCommand $command)
{
// 对 $command 变量进行某些操作
dd($command);
}
}

就如上面的代码所展示的,控制器通过一些必要的信息创建了一个 DuplicateTalkCommand 对象,通过内置的 command bus dispatcher 对齐进行调度,于是该命令的处理程序自动对其进行处理。

体系结构

接下来,我们先来看看这些命令和处理程序存放在什么位置,然后再说说如何生成它们。

文件夹

在 Laravel 5.0 的应用框架中, app/ 目录下有两个新的文件夹: Commands 和 Handlers , Handlers 目录下还有两个子目录: Commands 和 Events (这个目录说明我们还可以期待事件处理的特性).

app/
Commands/
Handlers/
Commands/
Events/

相信看到目录结构你就能猜到,Commands 的代码存放在 app/Commands 目录下,而 Command handlers 则存放在 app/Handlers/Commands 目录下—— Handler 的文件名与其对应的 Command 保持一致,但是要加上 Handler 后缀。

Artisan

非常值得庆幸的是,你不用自己动手来创建 Command 和 Command Handler。新版本提供了一个全新的 Artisan 生成工具,通过它可以快速生成这些文件:

$ php artisan make:command DuplicateTalkCommand

默认情况下,这条命令会生成一个自处理的命令(不生成单独的 Command handler),并且该命令不添加到队列。加上 --handler 参数可以同时生成 handler, 加上 --queued 参数可以将其加入到队列。

执行这个 artisan 命令会生成两个文件: 命令文件( app/Commands/DuplicateTalkCommand.php ) 和 处理程序文件( app/Handlers/Commands/DuplicateTalkCommandHandler.php ) (假设使用了 --handler 参数),并且生成的处理程序中的 handle 方法会自动加上与其匹配的命令的类型约束。

基本工作流程

综上所述,要创建一个新的 DuplicateTalkCommand , 你需要执行以下工作:

  1. php artisan make:command DuplicateTalkCommand
  2. 编辑 DuplicateTalkCommand , 增加一个 public 属性 $talk 并在构造函数中初始化这个属性。
  3. 编辑 DuplicateTalkCommandHandler , 在 handle() 方法中编写具体代码,完成你需要执行的操作。
  4. 在控制器或者 Artisan 命令中调度(调用)这个命令。

队列

把命令加入队列

如果希望某个命令在每次被调用时加入到队列中以便异步执行,你需要做的是让该命令实现 ShouldBeQueued 接口。 Laravel 会发现这个接口并把其加入队列等候执行,而不是立即执行。

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued
{
//...
}

InteractsWithQueue trait

在你的 Command 类中加上这个 trait, 会让你的 Command 具有在以前版本中用惯了的队列命令(queue commands)所具有的全部特性: $command->release() , $command->delete() , $command->attempts() 等等。

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued, InteractsWithQueue
{
//...
}

SerializesModels trait

如果你传入一个 Eloquent 模型作为属性,就像前面的例子中那样,并且希望命令放入队列中执行而不是同步执行,那么必须要考虑到 Eloquent 模型的序列化,这可能会给你带来一些麻烦。不过在 Laravel 5.0 版本中,你可以给你的 Command 加一个 名为SerializesModels 的 trait 来解决这个问题。方法很简单,在类的代码块顶部加上即可:

...
class DuplicateTalkCommand extends Command implements ShouldBeQueued
{
use SerializesModels;
// ...
}

Dispatcher

DispatchesCommands Trait

你可能注意到,在前面的例子中,我们可以直接在控制器中使用 $this->dispatch() 方法。这是控制器的一个语法糖。这个语法糖实际上是通过名为 DispathesCommands 的 trait 来实现的。你可以在控制器之外的任何地方使用这个 trait.

比如,你希望某个服务类可以在方法中使用 $this->dispatch() , 你只要在你的服务类的代码块顶部使用 DispatchesCommands 这个 trait 即可:

...
class MyServiceClass { use DispatchesCommands; //...
}

注入 bus

如果你希望更直接、更清楚地调用 Command bus 而不是借助于 Laravel 系统提供的 trait, 你可以直接向你的类的构造函数或者是方法注入 Illuminate\Contracts\Bus\Dispatcher 实例(参见 Laravel 5.0 之方法注入 )。

...
public function __construct(\Illuminate\Contracts\Bus\Dispatcher $bus)
{
$this->bus = $bus;
}
public function doSomething()
{
$this->bus->dispatch(new Command);
}

dispatchFrom(command::class, $request 或任何 arrayAccessible 对象)

在之前的例子中,我们已经看到了调用命令的最简单方式,就是 $bus->dispatch(new Command(params...)) . 但有时候由于新建命令的参数列表变得越来越大——比如,当你的命令用于来处理表单输入的时候:

...
class CreateTalkCommand extends Command
{
public function __construct($title, $description, $outline, $organizer_notes, $length, $type, $level) {
// ...
}
}

这时,如果还用之前的方式来实例化命令,代码会变得很难看:

$this->dispatch(new CreateTalkCommand($input['title'], $input['description'],
$input['outline'], $input['organizer_notes'], $input['length'],
$input['type'], $input['level']));

通常我们的表单请求会传递与属性相同 key 的数组,从数组或者请求对象中获得具体的值。幸亏 Laravel 5.0 有针对这种情况的解决方案:

$this->dispatch('NameOfCommand', $objectThatImplementsPHPArrayAccessible);

因此我们可以用下面的代码替换上面那长长的一串:

$this->dispatchFrom(CreateTalkCommand::class, $input);

或者这样:

public function doSomethingInController(Request $request)
{
$this->dispatchFrom(CreateTalkCommand::class, $request);
// ...
}

Laravel 会自动在传入的数组或者 arrayAccessible 对象中去寻找与属性名相同的 key, 取出对应的值来调用命令的构造函数。

自处理命令

如果你嫌每个命令都要创建一个 Command 类和一个 Command handler 类太麻烦的话,你可以创建一个“自处理”的 Command. 这种情况下 Command 只有单一的处理程序,且该处理程序就是 Command 自己。要让一个 Command 变成自处理,只需要给该 command 类加一个 handle() 方法,并让它实现 SelfHandling 接口:

...
class DuplicateTalkCommand extends Command implements SelfHandling
{
//...
public function handle()
{
//...
}

其它注意事项

  • 命令处理程序会由 IOC 容器进行解析,因此你可以注入 repositories, service classes 或者任何其它类型到你的命令处理程序的构造函数中,然后在 handle() 方法中调用它们。
  • 几乎所有相关的 trait 和 接口都在 Illuminate\Contracts\Bus 或者 Illuminate\Contracts\Queue 命名空间下。比如, Illuminate\Contracts\Bus\SelfHandling .
  • 如果你的命令是放入队列执行,不需要在处理程序的最后执行 $command->delete() 方法。只要你的处理程序没有抛出任何异常,Laravel 会假定它已经正确完成,并自动将其从队列中移除。

写在最后

就这么多了,如果我遗漏了什么,或者某个问题讲得不够清楚,请让我知道。本文涉及到的点还有一些需要补充和替换的地方。暂时来说,我希望本文可以帮助你了解新版 Laravel 中的 Command 的运行机制。此外, Taylor 在 Laracasts 上的视频 涵盖了本文的全部内容并且讲得更多。

Laravel 5.0 之命令及处理程序的更多相关文章

  1. laravel 5.0 artisan 命令列表(中文简体)

    #php artisan list Laravel Framework version Usage: [options] command [arguments] Options(选项): --help ...

  2. IIS7.0 Appcmd 命令详解和定时重启应用池及站点的设置

    IIS7.0 Appcmd 命令详解 废话不说!虽然有配置界面管理器!但是做安装包的时候命令创建是必不可少的!最近使用NSIS制作安装包仔细研究了一下Appcmd的命令,可谓是功能齐全. 上网查了些资 ...

  3. phpstom 实用laravel 需要附加的 命令

    首先利用composer 下载相关的插件 在根目录执行此代码 composer require barryvdh/laravel-ide-helper 再者在config/app.php 添加一条命令 ...

  4. Laravel 5.1使用命令行模式(artisan)运行php脚本

    Laravel有内置命令调度器,可以方便的实现Cron. 任务调度定义在app/Console/Kernel.php文件的schedule方法中,该方法已经包含了一个示例.Laravel里有两种方法执 ...

  5. IIS7.0 Appcmd 命令详解

    原文 IIS7.0 Appcmd 命令详解 一:准备工作 APPcmd.exe 位于 C:\Windows\System32\inetsrv 目录 使用 Cd c:\Windows\System32\ ...

  6. PHP laravel 5.0 Blade 模板引擎 Api使用备注

    PHP laravel 5.0 Blade 模板引擎 Api使用备注 /** * PHP laravel 5.0 Blade 模板引擎 Api使用备注 **/ //子模版中开头,调用@extends( ...

  7. Laravel 6.0 Schedule Preventing Task Overlaps 测试

    1 目的 1.1 测试 Laravel 6.0 任务执行机制 2 意义 2.1 在日常开发中,有的任务比较复杂,在两次任务的调度周期间隔中无法完成. 2.2 为了防止重复任务的持续生成和反复调用,对服 ...

  8. IIS6.0远程命令执行

    0X00 漏洞信息: 漏洞编号:CVE-2017-7269 漏洞简述:开启WebDAV服务的IIS 6.0被爆存在缓存区溢出漏洞导致远程代码执行,目前针对 Windows Server2003 R2 ...

  9. 浪潮 ClusterEngineV4.0 任意命令执行

    1.浪潮ClusterEngineV4.0 任意命令执行 影响版本 ClusterEngineV4.0 2.漏洞影响 远程代码执行 3.复现 fofa语句 title='TSCEV4.0' 抓包构造e ...

随机推荐

  1. Hadoop学习之--Fair Scheduler作业调度分析

    Fair Scheduler调度器同步心跳分配任务的过程简单来讲会经历以下环节: 1. 对map/reduce是否已经达到资源上限的循环判断 2. 对pool队列根据Fair算法排序 3.然后循环po ...

  2. cocos2d-html5将js编译为jsc

    在d:\DevTool\cocos2d-x-2.2.2\cocos2d-x-2.2.2\tools\cocos2d-console\console 有 cocos2d_jscompile.py coc ...

  3. MoveTo和MoveBy

    cc.MoveTo是“移动到这里",而cc.MoveBy则是“相对于之前点再移动”,通俗一点就是说这里需要两个坐标pos1(x1,y1),pos2(x2,y2). 如果是cc.MoveTo的 ...

  4. 开源的c语言人工神经网络计算库 FANN

    这年头机器学习非常的火,神经网络算是机器学习算法中的比较重要的一种.这段时间我也花了些功夫,学了点皮毛,顺便做点学习笔记. 介绍人工神经网络的基本理论的教科书很多.我正在看的是蒋宗礼教授写的<人 ...

  5. FATFS文件系统

    STM32移植文件系统,操作SD卡,对SD卡进行读写 FATFS文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已,函数为空,要用户添加代码.然后 F ...

  6. labview事件结构

    等待事件发生,并执行相应条件分支,处理该事件.事件结构 包括一个或多个子程序框图或事件分支,结构处理时间时,仅有一个子程序框图或分支在执行.等待事件通知时,该结构可超时. 连线边框左上角的“超时”接线 ...

  7. centos 安装redis php

    $ wget http://download.redis.io/releases/redis-3.0.7.tar.gz $ tar xzf redis-3.0.7.tar.gz $ cd redis- ...

  8. CGI 是什么

    CGI是公共网关接口,是Java Servlet 的前身,Java Servlet  是运行在服务器端的小程序.

  9. 使用Map List 封装json数据

    <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</art ...

  10. Java 理论与实践: 流行的原子——新原子类是 java.util.concurrent 的隐藏精华(转载)

    简介: 在 JDK 5.0 之前,如果不使用本机代码,就不能用 Java 语言编写无等待.无锁定的算法.在 java.util.concurrent 中添加原子变量类之后,这种情况发生了变化.请跟随并 ...