如何在MVC框架中有效地实现模块,并在单个模块中处理多个控制器的路由? [英] How do I effectively implement modules in an MVC framework, and handle routing to multiple Controllers in a single module?

查看:101
本文介绍了如何在MVC框架中有效地实现模块,并在单个模块中处理多个控制器的路由?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开发了一个基本的MVC框架作为php中的一个学习项目 - 这实际上是它的第二个版本,我正在努力改进第一个版本的两个方面:




  • 请求路由:映射请求,例如/ controller / action / [params]

  • 模块:设计用于扩展应用程序的插件应用程序,例如一个CMS。



这是我现在所在的地方:




  1. 我可以提出请求并将其解析成各种各样的部分,
    eg控制器,动作,args等。这些映射到相应的
    控制器类/文件,例如/ foo / bar - > FooController :: bar() -
    所有这些都在我的RequestRouter类中完成,并封装在
    Request对象中。




    • 我维护一个包含分类引用(控制器,lib等)到应用程序文件的Manifest对象。清单由我的自动加载程序方法使用。

    • 由于清单已被缓存,每当我添加新文件/类时都会重建它,这在新模块被添加/删除时是正确的。 li>

  2. Controller :: methods()呈现正确的视图。


  3. 然后来到这些模块,其核心是结构化的
    (/ root / raspberry / vendors / core / module)




问题



目前我认为的问题是路由/请求处理的组合,其中模块关心:




  • 如果我要求project.dev/admin映射到
    AdminController :: index() - 这是正确的

  • 但是,当我引用project.dev/admin/editor时,我仍然得到 AdminController :: editor()哪里我真的想要的是 EditorController :: index()



经过一番研究,您将创建一个装饰器,它实现了一个前端控制器模式,并包装给定的控制器。装饰器可以重新解析对控制器进行/编辑的请求,并重新映射剩余的段(/ editor / action / args)。



所有这一切似乎都可以正常工作,但我觉得我在流程(RequestRouter)中缺少一些基本原理。我在这里研究了其他类似的问题,并阅读了HMVC,原则上它似乎可以回答我的问题,但它似乎比框架驱动(如果这是有道理的)接口驱动还查看了像Kohana这样的其他框架,但是我不太了解他们的模块系统和路由到同一模块中的多个控制器的工作方式。



非常感谢如何有效地实现模块系统而不引入前端控制器或重新解析请求。或者,如果我应该以不同的方式重组我的模块,我想了解如何做到这一点。



附加信息:



我的RequestRouter维护了我预定义的路由列表(包括其默认方法)。使用这些预定义的路由,我可以访问/管理/编辑器,并获得EditorController :: index(),但是我必须为每个控制器定义一个路由,并请求发送到模块。我不认为这是好的设计。以下是我的路线示例:

  Array 

[/ foo] =& b $ b(
[controller] => FooController
[method] => bar
[path] => / core


[/ admin] => Array

[controller] => AdminController
[method] => index
[path] => / vendors /


[/ admin / editor] => Array

[controller] => EditorController
[method] => index
[路径] => / vendors / admin



这是我的Request对象的样子:

 请求对象

[property: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

这是应用程序文件系统:



http://s11.postimage.org/pujb2g9v7/Screen_shot_2012_10_09_at_8_45_27_PM.png

解决方案

这个问题似乎是由于过度简化的路由机制引起的。我得到的印象是,您使用简单的 explode()从URL收集参数。虽然这在基本示例中起作用,但当您尝试使用更高级的路由方案时,安装程​​序将失败。



而不是将 / ,您应该将其与regexp模式进行匹配。基本上,您定义一个匹配模式的列表,第一个匹配用于填充请求实例。



在您的情况下,将有两种模式定义:




  • '#admin /(:?((? ?P<控制器> [^ / \,;?\\\
    ]?+))/(P<作用> [^ / \,;?\\\
    ] +))#'

  • '#(:?(:?/(?P< controller> [^ / \。,;?\\\
    ] +))?/(?P< action> [^ / \。,??\\\
    ] +))?#'



如果第一个失败,第二个将失败。



PS 你应该知道控制器不应该渲染输出。响应的产生是视图实例的责任。视图应该是包含演示逻辑的全功能对象,并且可以从多个模板中构成响应。


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:

  • Request Routing: mapping requests, e.g. /controller/action/[params]
  • Modules: drop-in applications designed to extend the application, e.g. a CMS.

This is where I'm at right now:

  1. 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.

    • I maintain a Manifest object that contains categorized references (controllers, lib, etc) to application files. The manifest is used by my autoloader method.
    • Since the manifest is cached, it is rebuilt whenever I add new files/classes, this true for when new modules get added/removed.
  2. The Controller::methods() render the correct views fine.

  3. 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:

  • 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()

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).

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.

Additional Info:

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
)

This is a sample of my Manifest:

[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_shot_2012_10_09_at_8_45_27_PM.png

解决方案

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.

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<controller>[^/\.,;?\n]+))?/(?P<action>[^/\.,;?\n]+))?#'
  • '#(:?(:?/(?P<controller>[^/\.,;?\n]+))?/(?P<action>[^/\.,;?\n]+))?#'

If the first one fails, the second one would match.

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.

这篇关于如何在MVC框架中有效地实现模块,并在单个模块中处理多个控制器的路由?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆