草庐IT

php - Laravel 5 命令总线的最佳实践

coder 2024-04-30 原文

我正在尝试重构 Controller 并查看了 Laravel 的命令总线。

在阅读了一堆文章并观看了一些视频后,我觉得这可能是重构我的 Controller 的好方法。

但是,似乎我不应该从命令返回任何内容。

When using commands you follow the Command-query separation (CQS) principle: a function is either a query (i.e. it returns something) or a command (i.e. it affects state). Both are mutually exclusive. So a command is not supposed to return anything and a query is not supposed to modify anything. source

我已经创建了命令CreateCustomerCommand:

namespace App\Commands;

use QuickBooks_IPP_Service_Customer;

use App\Commands\Command;
use Illuminate\Contracts\Bus\SelfHandling;

class CreateCustomer extends Command implements SelfHandling
{
    private $qb;
    private $customer_service;
    private $customer;
    private $is_same_address;
    private $name;
    private $primary_phone;
    ...

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->qb = Quickbooks::Instance();
        $this->qb->ipp->version(QuickBooks_IPP_IDS::VERSION_3);

        $this->customer_service = new QuickBooks_IPP_Service_Customer();

        $this->is_same_address = $request->input('customer.isSameAddress');
        $this->name = ucwords(strtolower($request->input('customer.name')));
        $this->primary_phone = $request->input('customer.primaryPhone');
    }
    ...

    /**
     * Execute the command.
     *
     * @return void
     */
    public function handle()
    {
        $this->customer->setDisplayName($this->name);

        ...

        $this->customer_service->add(...);



    }
}

关于最佳实践的三个问题:

  1. 调用 $this->customer_service->add() 后,返回一个客户 id。如何将此 id 发送回 Controller ?

  2. 将事件日志合并到哪里最好?

事件:

$activity = new Activity();
$activity->event = 'Created Customer: ' . $this->name;
$activity->user = Auth::user()->name;
$activity->save();

将它包含在 CreateCustomerCommand 的末尾是不是最好?

  1. 事件怎么样?

事件:

event(new CustomerWasCreatedOrUpdated);

我是应用程序架构的新手,正在寻找一种方法来使我的 Controller 简单易维护。如果有人能指出我正确的方向,我会很高兴。

最佳答案

首先,感谢您努力使您的 Controller “简单且可维护”。您可能并不总能实现这一目标,但实现这一目标往往会有所收获。

如何将 ID 发送回 Controller ?

命令是一般服务的特例。您的命令可以自由声明额外的公共(public)方法来查询更改状态的结果。如果命令由 CLI 应用程序使用,则该应用程序可能会执行类似 echo $this->command->getAddedCustomerId() 的操作。基于网络的 Controller 可以类似地使用它。

但是,您引用的建议——要么在没有输出的情况下更改状态,要么在有输出的情况下进行查询——是明智的。如果您正在更改状态并且您需要知道更改该状态的结果,那么您可能正在滥用命令。

作为类比,考虑 Linux 命令“useradd”,您可以像 useradd -m 'Clara Barton' cbarton 那样调用它。该命令运行并仅向您提供成功或失败指示。但请注意,您给了它主键,“cbarton”。您可以独立查询该 key ,例如 grep cbarton/etc/passwd,但重要的是 useradd 没有为您创建 ID。

总而言之,更改状态的命令最多应该告诉您成功或失败。如果您希望检查该状态更改的结果,您应该为命令提供定位状态更改所需的键。

所以您可能需要的是一般服务。命令可能使用该服务, Controller 可能使用该服务,模型可能使用该服务。但服务只是执行一项工作并为您提供必要 API 的通用类。

将事件日志合并到哪里?

假设您不使用 PHP-AOP,则应预先建立谨慎而严格的事件日志记录实践,并在整个开发生命周期中遵循。

在很大程度上,事件日志的位置取决于系统的主要架构模型。如果您严重依赖事件,那么一个好地方可能是 Event 门面的扩展或日志事件。如果您广泛依赖 DI,那么您可以在您决定需要记录的代码中传递 Logger。

在命令的特定情况下,您可以采用任何一种方式,同样取决于您的主要架构模型。如果您避开事件,那么您将通过 Laravel 的正常类型提示 DI 注入(inject)记录器。如果您利用事件,那么您可能会执行类似 Event::fire('log', new LogState('Blah Blah', compact ($foo, $bar)));

也就是说,最重要的是您依赖于可插拔和可配置的日志记录服务,您可以根据测试、QA 和生产需求更换和调整该服务。

事件呢?

嗯,事件很棒。直到他们不是。根据我的经验,事件真的会变得复杂,因为它们在 IMO 中被滥用来传递数据和影响状态。

事件就像传送器:你沿着一条路径前进,然后事件触发,突然间你被传送到整个代码库并突然出现在一个完全不同的地方,然后你做了一些事情然后被丢弃马上回到你原来的地方。当事件发生时,您必须以某种方式思考并高效地遵循代码。

如果 Laravel 事件是您第一次接触事件,我不鼓励您大量使用它们。相反,我建议您将它们限制在一个特定的包或应用程序的一部分,直到您感受到它们提供的功能以及它们所需的架构和开发严谨性。

关于php - Laravel 5 命令总线的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31306779/

有关php - Laravel 5 命令总线的最佳实践的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  4. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  5. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  6. ruby-on-rails - Rails 中同一个类的多个关联的最佳实践? - 2

    我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来

  7. ruby - 从 Ruby : capturing the output while displaying the output? 运行 shell 命令 - 2

    我有一个问题。我想从另一个ruby​​脚本运行一个ruby​​脚本并捕获它的输出信息,同时让它也输出到屏幕。亚军#!/usr/bin/envrubyprint"Enteryourpassword:"password=gets.chompputs"Hereisyourpassword:#{password}"我运行的脚本文件:开始.rboutput=`runner`putsoutput.match(/Hereisyour(password:.*)/).captures[0].to_s正如您在此处看到的那样,存在问题。在start.rb的第一行,屏幕是空的。我在运行程序中看不到“输入您的密

  8. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  9. ruby - 是否有将图像文件转换为 ASCII 艺术的命令行程序或库? - 2

    有这样的事吗?我想在Ruby程序中使用它。 最佳答案 试试这个http://csl.sublevel3.org/jp2a/此外,Imagemagick可能还有一些东西 关于ruby-是否有将图像文件转换为ASCII艺术的命令行程序或库?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6510445/

  10. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

随机推荐