最初,我的超薄框架应用采用经典结构
(index.php)
get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
但随着我添加更多的路由和路由组,我转向了基于控制器的方法:
index.php
get('/hello/:name', 'HelloController::hello');
$app->run();
HelloController.php
这是可行的,它对组织我的应用程序结构很有帮助,同时还允许我为每个控件方法构建单元测试。
然而,我不确定这是正确的方式。我觉得我是在自成一格地嘲笑Silex的mount
方法,这不可能是好事。在每个控制器方法中使用$app上下文需要我使用SlimSlim::getInstance(),这似乎不如像闭包那样使用$app。
所以...有没有同时兼顾效率和秩序的解决方案,或者效率是以路线/封闭噩梦为代价的?
我想我可以和你们分享我的所作所为。我注意到SlimSlim中的每个路由方法在某种程度上都称为方法maproute
(为了清楚起见,我更改了官方源代码的缩进)
Slim.php
protected function mapRoute($args)
{
$pattern = array_shift($args);
$callable = array_pop($args);
$route = new SlimRoute(
$pattern,
$callable,
$this->settings['routes.case_sensitive']
);
$this->router->map($route);
if (count($args) > 0) {
$route->setMiddleware($args);
}
return $route;
}
反过来,SlimRoute构造函数调用setCallable
Route.php
public function setCallable($callable)
{
$matches = [];
$app = $this->app;
if (
is_string($callable) &&
preg_match(
'!^([^:]+):([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)$!',
$callable,
$matches
)
) {
$class = $matches[1];
$method = $matches[2];
$callable = function () use ($class, $method) {
static $obj = null;
if ($obj === null) {
$obj = new $class;
}
return call_user_func_array([$obj, $method], func_get_args());
};
}
if (!is_callable($callable)) {
throw new InvalidArgumentException('Route callable must be callable');
}
$this->callable = $callable;
}
基本上就是
$callable
是字符串,并且(注意单个冒号)的格式为ClassName
:method
,则它是非静态的,因此Slim将实例化该类,然后对其调用方法。ClassName
应该是FQCN,所以更像MyProjectControllersClassName
。
UtilMySlim
protected function mapRoute($args)
{
$pattern = array_shift($args);
$callable = array_pop($args);
$route = new UtilMyRoute(
$this, // <-- now my routes have a reference to the App
$pattern,
$callable,
$this->settings['routes.case_sensitive']
);
$this->router->map($route);
if (count($args) > 0) {
$route->setMiddleware($args);
}
return $route;
}
所以基本上UtilMyRoute
是SlimRoute
,在其构造函数中有一个额外的参数,我将其存储为$this->app
此时,getCallable可以将应用程序注入每个需要实例化的控制器
UtilMyRoute.php
public function setCallable($callable)
{
$matches = [];
$app = $this->app;
if (
is_string($callable) &&
preg_match(
'!^([^:]+):([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)$!',
$callable,
$matches
)
) {
$class = $matches[1];
$method = $matches[2];
$callable = function () use ($app, $class, $method) {
static $obj = null;
if ($obj === null) {
$obj = new $class($app); // <--- now they have the App too!!
}
return call_user_func_array([$obj, $method], func_get_args());
};
}
if (!is_callable($callable)) {
throw new InvalidArgumentException('Route callable must be callable');
}
$this->callable = $callable;
}
就是这样。使用这两个类,我可以将$app
注入到我在路由上声明的任何控制器中,只要我使用单个冒号将控制器与方法分开。使用paamayim nekudotayim将以静态方式调用该方法,因此,如果我尝试访问该方法中的$this->app
,将引发错误。
优点:
缺点:
结束语:(4年后)
在Slim v3中,他们删除了静态访问器。反过来,如果您使用相同的约定FQCNClassName:method
,控制器将使用应用程序的容器实例化。此外,该方法还接收来自路由的请求、响应和$args
。这样的直接投资,很大的国际奥委会。我非常喜欢它。
回顾我对Slim 2的做法,它打破了最基本的替换(利斯科夫替换)原则。
class Route extends SlimRoute
{
protected $app;
public function __construct($app, $pattern, $callable, $caseSensitive = true) {
...
}
}
应该是
class Route extends SlimRoute
{
protected $app;
public function __construct($pattern, $callable, $caseSensitive = true, $app = null) {
...
}
}
这样它就不会违反合同,并且可以透明地使用。
这篇关于超薄框架:路由和控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持吉威生活!
[英文标题]Slim Framework: routes and controllers
声明:本媒体部分图片、文章来源于网络,版权归原作者所有,如有侵权,请联系QQ:330946442删除。