如何在 MVC 框架中有效地实现模块,并在单个模块中处理到多个控制器的路由?
我已经开发了一个基本的 MVC 框架作为 php 的学习项目——这实际上是它的第二个版本,我正在努力改进第一个版本所欠缺的两个方面:
I've developed a basic MVC framework as a learning project in php--this is actually the second version of it, and I'm trying to improve two aspects that the first version fell short on:
- 请求路由:映射请求,例如/controller/action/[参数]
- 模块:旨在扩展应用程序的嵌入式应用程序,例如一个内容管理系统.
- Request Routing: mapping requests, e.g. /controller/action/[params]
- Modules: drop-in applications designed to extend the application, e.g. a CMS.
我能够接受一个请求并将其解析为不同的部分,例如控制器、动作、参数等.这些映射到相应的控制器类/文件,例如/foo/bar"->FooController::bar() -所有这些都在我的 RequestRouter 类中完成并封装在一个请求对象.
I'm able to take a request and parse it into it's various pieces, e.g. controller, action, args, etc.. These map to corresponding controller classes/files, e.g. "/foo/bar" -> FooController::bar() - all of which is done in my RequestRouter class and encapsulated in a Request object.
- 我维护一个 Manifest 对象,其中包含对应用程序文件的分类引用(控制器、库等).清单由我的自动加载程序方法使用.
- 由于清单被缓存,所以每当我添加新文件/类时都会重建它,这对于添加/删除新模块时也是如此.
Controller::methods() 渲染正确的视图.
The Controller::methods() render the correct views fine.
然后是模块,它们的组织方式就像核心结构一样(/root/raspberry/vendors/core/module)
Then comes the modules, which are organized like the core is structured (/root/raspberry/vendors/core/module)
问题
我认为我目前遇到的问题是与模块相关的路由/请求处理的组合:
The Problem
The issue that I think I'm having at the moment is a combination of Routing/Request Handling where modules are concerned:
- 如果我请求 project.dev/admin 它映射到AdminController::index() -- 这是正确的
- 然而,当我引用 project.dev/admin/editor 时,我仍然得到 AdminController::editor() 我真正想要的是 EditorController::index()
- If I request project.dev/admin it maps to the AdminController::index() -- this is correct
- However, when I reference project.dev/admin/editor I still get AdminController::editor() where what I really want is EditorController::index()
经过一些研究,我想我可以创建一个装饰器,它实现一个前端控制器模式并包装给定的控制器.装饰器可以重新解析请求,使/editor 成为控制器并重新映射剩余的段 (/editor/action/args).
After some research, I figure I could create a Decorator, which implements a Front Controller pattern and wraps a given Controller. The decorator could re-parse the request to make /editor the controller and re-map the remaining segments (/editor/action/args).
所有这些似乎都可以正常工作,但我觉得我在流程的早期(RequestRouter)中遗漏了一些基本的东西.我在 SO 中研究了其他类似的问题,并阅读了 HMVC,原则上它似乎可以回答我的问题,但它似乎更多的是接口驱动而不是框架驱动(如果这有意义?)我已经还研究了 Kohana 等其他框架,但我不太了解它们的模块系统和路由到同一模块中的多个控制器的工作原理.
All of this seems like it could work fine, but I feel like I'm missing something fundamental earlier in the flow (RequestRouter). I've research other similar questions here in SO, and read up on HMVC, and in principle it seems like it might answer my questions, but it seems more interface-driven than framework-driven (if that makes sense?) I've also looked at other frameworks like Kohana, but I'm not quite grasping how their module system and routing to multiple controllers in the same module work.
关于如何在不引入前端控制器或重新解析请求的情况下有效实现模块系统的任何见解或建议,将不胜感激.或者,如果我应该以不同的方式重新构建我的模块,我想了解如何做到这一点.
Any insights or suggestions on how to effectively implement a module system without introducing a Front Controller or re-parsing a request would be much appreciated. Alternately, if I should re-structure my modules in a different manner, I'd like to understand how to do that.
我的 RequestRouter 维护一个我预先定义的路由列表(包括它们的默认方法).使用这些预定义的路由,我可以访问/admin/editor 并获得 EditorController::index(),但我必须为每个控制器定义一个路由,并请求进入控制器中的控制器模块.我不认为这是一个好的设计.这是我的路线示例:
My RequestRouter maintains a list of routes that I pre-defined (including their default methods). Using these pre-defined routes, I can access /admin/editor and get EditorController::index(), but I'd have to define a route for every controller and request that goes to controllers in the module. I don't think this is good design. Here's a sample of my routes:
Array
(
[/foo] => Array
(
[controller] => FooController
[method] => bar
[path] => /core
)
[/admin] => Array
(
[controller] => AdminController
[method] => index
[path] => /vendors/admin
)
[/admin/editor] => Array
(
[controller] => EditorController
[method] => index
[path] => /vendors/admin
)
)
这是我的请求对象的样子:
This is what my Request object looks like:
Request Object
(
[properties:Request:private] => Array
(
[url] => /admin/editor
[query] =>
[uri] => /admin/editor
[controller] => admin
[action] => editor
[args] =>
[referrer] => Array
(
[HTTP_REFERER] =>
[REMOTE_ADDR] => 127.0.0.1
[HTTP_VIA] =>
[HTTP_X_FORWARDED_FOR] =>
)
[get] =>
)
[request_status:Request:private] => 200
)
这是我的清单示例:
[controller] => Array
(
[icontroller] => /htdocs/raspberry/raspberry/core/controller/icontroller.class.php
[index] => /htdocs/raspberry/raspberry/core/controller/index.php
[serviceerror] => /htdocs/raspberry/raspberry/core/controller/serviceerror.controller.php
[admin] => /htdocs/raspberry/raspberry/vendors/core/admin/controller/admin.controller.ph
[composer] => /htdocs/raspberry/raspberry/vendors/core/admin/controller/composer.controller.php
)
这是应用程序文件系统:
This is the application file system:
http://s11.postimage.org/pujb2g9v7/Screen_shot1_2007.png2012_10_09_at_8_45_27_PM.png"
http://s11.postimage.org/pujb2g9v7/Screen_shot_2012_10_09_at_8_45_27_PM.png
推荐答案
该问题似乎是由过于简化的路由机制引起的.我得到的印象是您正在使用简单的 explode()
从 URL 收集参数.虽然这仅适用于基本示例,但当您尝试使用更高级的路由方案时,设置将失败.
The issue seems to be caused by over-simplified routing mechanism. The impression i get is that you are using simple explode()
to collect the parameters from URL. While this works just fin in basic examples, the setup will fail when you try to use a bit more advanced routing schemes.
与其在 /
上拆分字符串,不如将其与正则表达式模式进行匹配.基本上,您定义要匹配的模式列表,第一个匹配项用于填充 Request
实例.
Instead of splitting the string on /
, you should match it against regexp patterns. Basically, you define a list of patterns to match against and first match is used to populate the Request
instance.
在您的情况下会定义两种模式:
In your situation would would have two patterns defined:
'#admin/(:?(:?/(?P
[^/.,;? ]+))?/(?P[^/.,;? ]+))?#' '#(:?(:?/(?P
[^/.,;? ]+))?/(?P[^/.,;? ]+))?#'
'#admin/(:?(:?/(?P<controller>[^/.,;? ]+))?/(?P<action>[^/.,;? ]+))?#'
'#(:?(:?/(?P<controller>[^/.,;? ]+))?/(?P<action>[^/.,;? ]+))?#'
如果第一个失败,第二个会匹配.
If the first one fails, the second one would match.
P.S. 你应该知道控制器不应该渲染输出.响应的生成是视图实例的责任.视图应该是包含表示逻辑的功能齐全的对象,可以组合来自多个模板的响应.
P.S. You should know that controllers are not supposed to render output. Generation of response is responsibility of view instances. Views should be fully functional objects which contain presentation logic and can compose a response from multiple templates.
相关文章