女士们,先生们,度过美好的时光。
不久前,在Laravel中查看单个项目时,我遇到了重复和重复代码的现象。
最重要的是:系统具有一些用于AJAX请求的内部API结构,从本质上返回了数据库中的某些内容(订单,用户,配额等)的集合。 该结构的全部目的是返回带有结果的JSON,仅此而已。 在代码审查中,我使用相同的代码计算了5个或6个类,唯一的区别是对ResourceCollection,JsonResource和模型本身的依赖项注入。 我认为这种方法从根本上来说是错误的,并且我决定使用Laravel框架提供给我们的强大DI对我的代码进行正确的更改。
所以,我是怎么想到的。
我已经有大约一年半的Magento 2开发经验,当我第一次遇到这个CMS时,我对其DI感到震惊。 对于那些不知道的人:在Magento 2中,系统的一小部分是建立在所谓的“虚拟类型”上的。 也就是说,对于特定的类,我们并不总是转向“真实的”类。 我们指的是基于特定的“真实”类(例如,管理网格的Collection,通过DI进行组装)而被“组装”的虚拟类型。 也就是说,我们实际上可以通过简单地在DI中编写类似的内容来构建用于我们的依赖项的任何类:
<virtualType name="Vendor\Module\Model\ResourceModel\MyData\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">vendor_table</argument> <argument name="resourceModel" xsi:type="string">Vendor\Module\Model\ResourceModel\MyData </argument> </arguments> </virtualType>
现在,通过请求Vendor \ Module \ Model \ ResourceModel \ MyData \ Grid \ Collection类,我们获得了Magento \ Framework \ View \ Element \ UiComponent \ DataProvider \ SearchResult的实例,但mainTable依赖项设置为“ vendor_table”,resourceModel-“供应商\模块\模型\ ResourceModel \ MyData。
最初,这种方法对我来说似乎并不完全清楚,也不完全是“适当的”方法,也不是很正常的方法,但是经过一年的发展,我反而成为这种方法的追随者,而且,我在我的项目中发现了这种方法的应用。 。
回到Laravel。
DI Laravel建立在“服务容器”上-一个管理系统中的活页夹和依赖项的实体。 因此,例如,我们可以向DummyDataProviderInterface接口指示此DummyDataProvider接口本身的实现。
app()->bind(DummyDataProviderInterface::class, DummyDataProvider::class);
然后,当我们在服务容器中请求DummyDataProviderInterface时(例如,通过类构造函数),我们将获得DummyDataProvider类的实例。
许多
(由于某种原因)在Laravel服务容器中结束了这些知识,然后去做他们自己的,更有趣的事情,
但是徒劳 。
Laravel不仅可以“绑定”真实的实体,例如给定的接口,还可以创建所谓的“虚拟类型”(又名别名)。 而且,即使在这种情况下,Laravel也不必传递实现您的类型的类。 bind()方法可以将匿名函数作为第二个参数,并在其中传递$ app参数-应用程序类的实例。 总的来说,现在我们更多地进入上下文绑定,其中传递给实现“虚拟类型”的类的内容取决于当前情况。
我警告您,并非所有人都同意这种构建应用程序体系结构的方法,因此,如果您是数百个相同类的拥护者,请跳过此材料。
因此,首先,我们将决定什么将充当“真实”课程。 以代码审查中出现的一个项目示例为例,我们来处理一下资源请求的情况(实际上是CRUD,但有所减少)。
让我们看一下常见的Crud控制器的实现:
<?php namespace Wolf\Http\Controllers\Backend\Crud; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Wolf\Http\Controllers\Controller; class BaseController extends Controller { protected $model; protected $resourceCollection; protected $jsonResource; public function __construct( $model, $resourceCollection = null, $jsonResource = null ) { $this->model = $model; $this->resourceCollection = $resourceCollection; $this->jsonResource = $jsonResource; } public function index(Request $request) { return $this->resourceCollection::make($this->model->get()); } public function show($id) { return $this->jsonResource::make($this->model->find($id)); } }
实际上,我对项目的执行并没有太在意,因为该项目处于计划阶段。
我们有两种方法应该返回给我们:索引(index)和索引(show),该集合从数据库返回实体的集合;显示(show)返回特定实体的json资源。
如果我们使用实类,则每次创建包含1-2个setter的类,这些setter定义模型,资源和集合的类。 想象一下数十个文件,其中真正复杂的实现只有1-2个。 我们可以使用DI Laravel避免这种“克隆”。
因此,该系统的架构将很简单,但是像瑞士手表一样可靠。
有一个json文件,其中包含“虚拟类型”数组,这些数组直接引用将用作集合,模型,资源等的类...
例如,这:
{ "Wolf\\Http\\Controllers\\Backend\\Crud\\OrdersResourceController": { "model": "Wolf\\Model\\Backend\\Order", "resourceCollection": "Wolf\\Http\\Resources\\OrdersCollection", "jsonResource": "Wolf\\Http\\Resources\\OrderResource" } }
接下来,使用Laravel绑定,我们将Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController设置为我们的虚拟类型的类Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController作为实现类(请注意该类不应是抽象的,因为当请求Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController时,我们应该获得Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController的实例,而不是抽象类)。
在CrudServiceProvider中,在boot()方法中,放入以下代码:
$path = app_path('etc/crud.json'); if ($this->filesystem->isFile($path)) { $virtualTypes = json_decode($this->filesystem->get($path), true); foreach ($virtualTypes as $virtualType => $data) { $this->app->bind($virtualType, function ($app) use ($data) { $bindingData = [ 'model' => $app->make($data['model']), 'resourceCollection' => $data['resourceCollection'], 'jsonResource' => $data['jsonResource'] ]; return $app->makeWith(self::BASE_CRUD_CONTROLLER, $bindingData); }); } }
常量BASE_CRUD_CONTROLLER包含实现CRUD控制器逻辑的类的名称。
远非理想,但它有效:)
在这里,我们遍历具有虚拟类型的数组并设置绑定器。 注意,我们仅从服务容器获取模型实例,而ResourceCollection和JsonResource只是类名。 为什么这样 该模型不必填充属性,没有它们就可以很好地做到。 但是,集合必须吸收某种资源,它们将从中获取数据和实体。 因此,在BaseController中,我们分别使用静态方法collection()和make()(原则上,我们可以添加动态getter,将其放入资源中并返回一个实例给我们,但我会留给您),这些方法将返回这些实例相同的集合,但数据已传输到它们。
实际上,原则上您可以将整个Laravel绑定带到这种状态。
总共,请求Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController,我们获得了控制器Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController的实例,但是具有模型,资源和集合的内置依赖性。 它仅用于创建ResourceCollection和JsonResource,您可以控制返回的数据。