最后,经过几天的研究和思考,我终于使用 Laravel 解决了这个问题的最佳方法。
我不得不说,这尤其困难Laravel 5.2
因为,在这个版本中,Session中间件仅在路由中使用的控制器中执行,这意味着如果由于某种原因我使用控制器(未死记硬背链接)并尝试访问会话,这是不可能的。
因此,因为我无法使用会话,所以我决定使用 URL 参数。这里有解决方法;我希望你们中的一些人觉得它有用。
所以,你有一个界面:
interface Service
{
public function execute();
}
然后是该接口的几个实现:
服务一:
class ServiceOne implements Service
{
public function execute()
{
.......
}
}
服务二.
class ServiceTwo implements Service
{
public function execute()
{
.......
}
}
有趣的是,我有一个控制器,其功能依赖于服务接口。尽管如此,我仍然需要根据用户输入将其动态解析为 ServiceOne 或 ServiceTwo。所以:
控制器
class MyController extends Controller
{
public function index(Service $service, ServiceRequest $request)
{
$service->execute();
.......
}
}
请注意,ServiceRequest 验证了该请求已经具有我们需要解析依赖项的参数(将其称为'service_name'
)
现在,在 AppServiceProvider 中我们可以通过以下方式解决依赖关系:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
//This specific dependency is going to be resolved only if
//the request has the service_name field stablished
if(Request::has('service_name'))
{
//Obtaining the name of the service to be used (class name)
$className = $this->resolveClassName(Request::get('service_name')));
$this->app->bind('Including\The\Namespace\For\Service', $className);
}
}
protected function resolveClassName($className)
{
$resolver = new Resolver($className);
$className = $resolver->resolveDependencyName();
return $className;
}
}
所以现在所有的责任都由 Resolver 类承担。该类基本上使用传递给构造函数的参数来返回将用作 Service 接口的实现的类的全名(带有命名空间):
class Resolver
{
protected $name;
public function __construct($className)
{
$this->name = $className;
}
public function resolveDependencyName()
{
//This is just an example, you can use whatever as 'service_one'
if($this->name === 'service_one')
{
return Full\Namespace\For\Class\Implementation\ServiceOne::class;
}
if($this->name === 'service_two')
{
return Full\Namespace\For\Class\Implementation\ServiceTwo::class;
}
//If none, so throw an exception because the dependency can not be resolved
throw new ResolverException;
}
}
嗯,我真的希望它能对你们中的一些人有所帮助。
最好的祝愿!
- - - - - 编辑 - - - - - -
我只是意识到直接在 Laravel 容器内使用请求数据并不是一个好主意。从长远来看,这确实会造成一些麻烦。
最好的方法是直接注册所有可能支持的实例(serviceone 和 servicetwo),然后直接从控制器或中间件解析其中一个,因此控制器“决定”使用什么服务(从所有可用的服务中)基于请求的输入。
最后,它的工作原理是一样的,但它会让你工作得更自然。
我要感谢 Laravel 的 Slack Chat 问题频道的用户 rizqi。
他亲自打造了金article http://rizqi.id/laravel-registry-pattern对这个。请阅读它,因为它以非常正确的方式彻底解决了这个问题。
Laravel 注册表模式 http://rizqi.id/laravel-registry-pattern