我正在创建一个遵循MVC模式的MySQL数据库驱动的PHP(W)CMS应用程序。首先来看看框架:
MVC框架处理请求并根据URL决定加载/调用的内容,例如:http://domain.com/user/details/121
将加载并实例化User
控制器对象,并使用用户标识调用其details(121)
方法作为参数传递,然后实例化User_Model
并使用121用户ID“询问”用户的详细数据,最后用View
显示结果。这是MVC架构的基本概念。没什么特别的,此时一切都很清楚。
虽然这将是CMS,但我想处理Page
模型。具有neseary权限的用户(主要是admin和/或root)可以在页面上执行基本的CRUD操作和其他内容,例如:
我可以使用:
http://domain.com/about_us
)或者我可以修改这些属性,即使我可以删除页面......等等。
MVC框架在处理前端和后端调用之间没有区别。 例如,我们有一些要求:
http://domain.com
http://domain.com/user/details/121
http://domain.com/about_us
第一个将加载后端控制器,如前所述, 但其他人会加载一个前端内容。 当Bootstrap加载适当的控制器/操作时,我们会在上面的示例中查找实际的控制器文件:
http://domain.com/our_products/1255
/controllers/Users.php
/controllers/About_us.php
第一个可以加载因为这是之前编写的“静态”控制器,但是About_us和Our_products不是现有的控制器,所以如果无法加载控制器,则引导程序搜索数据库是否有一个页面包含相同的URL否定(如:about_us,our_products)。如果有,我们加载一个公共的FrontEndController并显示所请求的页面数据,如果没有,则显示404错误。
我这样做是因为我希望引导程序以相同的方式处理所有请求,但我不希望每个前端URL强制包含/controllers/Our_products.php
(例如:FrontEndController
}。所以这就是我将其隐藏起来的方式,因此URL可以保持更加用户友好。我的问题是:这是一个好习惯吗?或者还有其他正确的方法吗?
答案 0 :(得分:0)
MVC框架处理请求并根据URL
决定加载/调用的内容
您通常拥有的是某种Router
和Dispatcher
类。路由器将接受user/details/121
,解析它并返回Route
。
$route = $router->route( $request->getUri() );
路由器可以保存配置值,例如URI中的允许空格字符,默认允许字符等。
您还可以向路由器添加自定义路由
$router->addRoutes($routes);
自定义路由可以是简单的关联数组
$routes['requested-uri'] = 'custom-route'
在上面的示例中,您说当他们访问网站的根目录时,您希望他们真正看到“关于我们”页面,以便可以这样做:
$router->addRoutes([
'' => 'about-us
]);
当URI为''(空白)时的含义,然后转到'about-us'路线。它不应该进行重定向,只是透明地加载不同的路由,同时保持客户端Web浏览器中的URI不变。
路由显然可能更复杂,使用添加到路由集合的路由对象来获得更多高级控制的自定义路由。一些框架使用注释和各种不同的方式来实现灵活的路由。
然后,调度员可以接受从路由器返回的路由并发送它。这意味着验证请求的路由是否确实存在,即控制器文件是否存在以及控制器中所请求的方法是否存在。
$view = $dispatcher->dispatch($route);
在Dispatcher::Dispatch()
方法内:
// Check if the controller file exists.
// Instantiate the controller file, preferably using a controller factory.
// Check if the controller method exists.
// Call the controller method
call_user_func_array([$controller, $route->getMethod()], $route->getParams());
$view = $controller->getView();
$action = $route->getAction();
// Call the view method.
if( method_exists($view, $action) ) {
$view->$action();
}
return $view;
我发现以下是一种非常容易理解的处理控制器方法/操作的方法。假设您有一个登录控制器,用户首先向其发送GET请求,并在表单中发送登录详细信息时向其发送POST请求。
public function getIndex() { }
public function postIndex() {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
方法名称前面的获取和帖子是请求类型,这可以防止您必须执行此类操作
public function index() {
if( $this->request->getType() === 'POST' ) {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
}
它还可以让您更好地控制授权(如果您在路由层执行此操作),因为您可以轻松地允许用户向控制器发送GET请求,但拒绝他们访问发送POST请求。
每个控制器与视图具有一对一的关系。在构造中将视图注入控制器,最好使用控制器工厂。
发送GET请求http://domain.com/user/details/121
时会发生什么情况,路由器会分解URI并将其转换为定位User
控制器的路由,getDetails()
方法带参数121
,调度程序检查控制器和方法是否存在,然后调用提供用户ID作为参数的方法,控制器在视图中设置用户ID。以下是User
控制器。
public function getDetails($userId) {
$this->getView()->setUserId( (int)$userId );
}
然后视图有一个名为details()
的方法。与控制器中调用的方法名称相同,前面没有请求类型。
然后,调度程序调用视图的details()
方法,然后获取所需的数据。
设置页面标题在视图中完成,因为它仅用于演示目的。
与用户控制器相关的部分视图
public function details() {
// Fetch the user by using the previously set user ID from the controller.
// If he doesn't exist set an error template, set the response code to 404,
// or redirect. Do whatever you want really.
$this->setTitle('User Details');
// Build template objects, bind the fetched user data to main template.
}
如何实施setTitle
方法以及所有相关内容取决于您。
视图将响应发送回客户端,无论是HTML内容,JSON,XML还是任何其他内容类型。
例如,您的应用程序允许您搜索用户并将其导出到Microsoft Excel工作簿文件(.xlsx)并提示用户下载。
视图将:
Content-Type