我需要一种根据请求参数通过IoC提供程序加载对象的方法。现在我直接用我要重构的App::make(xy, [$urlParamter]
加载我的对象,这样我就可以按照预期的方式使用依赖注入。为了描述我目前的架构,我需要向你展示一些安静的信息,最后你会发现我对它的具体问题。
我正在构建一个通用的CMS框架,它提供了一个可以使用自定义导入实现进行扩展的导入体系结构。 现在我正在努力通过IoC容器正确加载具体类,因为它们总是依赖于所选的导入。
要深入研究我的问题,这是routes.php
Route::get('/import', ['as' => 'overview', 'uses' => '\CMSFramework\Http\Controllers\Import\ImportController@index']);
这会生成一个视图,用户可以选择要触发的具体导入。选择具体导入后,用户应获取单独的视图以准备相应的导入(即上载CSV文件,选择要导入的区域等)。
在我的概念中,导入实现包括:
CMS框架部分包括:
ImportBase
。它提供了一个接口,用于接收任何导入的进度并实现共享操作,如清理工作数据等。)ImportStatus
类是ImportBase
- 类的一部分,通过$ImportBase->status()
处理所有运行时状态信息(例如&#34;作业仍在运行,进展如何)。该类还为所谓的&#34;有效载荷提供了一个容器。允许任何conrete导入实现推送和获取自定义状态信息(即任何子流程已完成)回到我的IoC架构。在用户选择具体导入后,以下路由将操作委派给自定义导入实现的控制器。如果它是框架支持的标准操作,例如通过URL /import/<importkey>/clean
,则cms框架的继承BaseController接管并处理请求
Route::get('/import/{key}/{method}', ['uses' => function($key, $method) {
return App::make('\\MadeleinePim\\Http\\Controllers\\Import\\'.ucfirst(camel_case($key)).'Controller')->$method($key);
}]);
我知道通过命名约定的这种直接绑定可以改进(可能通过自定义配置文件),但是现在这对我有用。
现在我需要展示一个示例,说明我是如何尝试通过/import/<importkey>/seedCsvDataToDatabase
在我的控制器中实现具体导入目标的:
public function seedCsvDataToDatabase($key)
{
// The IoC binding is shown in next code snippet. I did not found a good way to use method injection because
// of the route-specific parameters that control the responsible import implementation
$import = \App::make(Import::class, [$key]);
// Now trigger the import service operation of that concrete import implementation (probably bad design here)
$import->seed();
// Now, that this preparation task is done, I use the ImportStatus object which is part of the Import to store
// status informations. With this I can then decided in which step the user is (Think of it like a wizard to
// prepare any import)
$import->status()
->set(ConcreteImport::STATUS_SEEDED, true)
->set(ConcreteImport::STATUS_SEEDED_DURATION_SECONDS, (microtime(true) - $time_start) / 60);
// Back to controller method that determines in which status the import is to delegate/redirect to different
// views.
return redirect('/import/<importkey>');
}
我对导入类的IoC绑定:
$this->app->singleton(Import::class, function ($app, array $parameters) {
$importKey = head($parameters);
// There is a config file that provides the class names of the concrete import implementations
$importClassName = config()->get('import.' . $importKey);
if (!$importClassName) {
throw new ImportNotFoundException($importKey, "Import with key '{$importKey}' is not setup properly'");
}
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
最后,导入状态的延迟加载(封装在ImportStatus
对象中看起来像这样
public function status()
{
if (!$this->status) {
$this->status = \App::make(ImportStatus::class, [$this->key()]);
}
return $this->status;
}
我希望这能说明我尝试从IoC容器中解析导入对象的方式。
到目前为止,我的学习是,这不是注入我的物体的正确方法。
假设是正确的,我不应该在运行时将$importKey
传递给App::make()
,而应该尝试将其设为独立吗?
我对此失败的尝试是让IoC绑定更智能,让它访问请求以正确注入我的具体导入对象所需的$importKey
,如(伪代码!):
$this->app->bind(ImportStatus::class, function(Container $app) {
// Did not find a good way to access the {key}-part of my route /import/{key}/{method}
$key = $app->make(Request::class)->get('key'); // Does not work like this
return new \Scoop\Import\ImportStatus($key);
});
$importKey
到ServiceProvider(或者更好地从那里拉出来吗?)更新1
对于我在IoC Binding中访问Route的最直接的想法,我这样做了:
$this->app->singleton(Import::class, function (Container $app) {
$importKey = \Route::current()->getParameter('key');
$importClassName = config()->get('import.' . $importKey);
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
然而,@ Sandyandi N. dela Cruz使用路由器绑定的想法阻止了Binding和Request之间的直接耦合,这仍然感觉不对。使用路由器绑定将请求参数耦合到实现,听起来更合适。
答案 0 :(得分:1)
我认为您已经在IoC容器上花了很多时间。为什么不实现Factory
模式并执行路由绑定而不是创建多个控制器来处理不同的Import
?粗略的例子如下:
app/Provider/RouteServiceProvider.php
boot()
方法public function boot(Router $router)
{
parent::boot($router);
// Add the statement below.
$router->bind('import', 'App\RouteBindings@import');
}
App\RouteBindings
课程设为app/RouteBindings.php
import()
方法:public function import($importKey, $route)
{
switch ($importKey) {
case 'import_abc':
return new ImportAbc;
break; // break; for good measure. ;)
case 'import_xyz':
return new ImportXyz;
break;
// and so on... you can add a `default` handler to throw an ImportNotFoundExeption.
}
}
Import
类的路径。 Route::get('import/{import}/{method}', 'ImportController@handleImport');
此处,{import}
将根据您的网址返回正确的Import
具体类。
ImportController
handleImport()
您可以执行以下操作:public function handleImport(Import $import, $method)
{
// $import is already a concrete class resolved in the route binding.
$import->$method();
}
所以当你点击:http://example.com/import/import_abc/seed
时,路径绑定将返回一个具体的ImportAbc
类,并将其存储在$import
方法的handleImport()
上,然后存储在handleImport()
中1}}方法将执行:$import->seed();
。提示:您应该将其他控制器逻辑(例如$import->status()->set()
)移动到Import
类中。保持控制器薄。
只需确保您的Import
类具有相同的签名。
它有点像Laravel的Route Model Binding,除了你为绑定创建逻辑。
同样,这只是一个粗略的例子,但我希望它有所帮助。