2008-09-24 46 views
15

因此,我正在编写一个框架,我要基于我正在开发的几个应用程序(该框架在那里,因此我有一个可以使用的环境,以及一个可让我,例如,使用单一登录)PHP应用程序URL路由

我想制作这个框架,它的应用程序使用面向资源的架构。

现在,我想创建一个可由APP编写器扩展的URL路由类(也可能由CMS应用程序用户进行扩展,但未来将提前推出),并试图找出最佳做法它通过查看其他应用程序如何执行它。

回答

12

我更喜欢使用reg ex制作自己的格式,因为它是常识。我写了一个我使用的小班,它允许我嵌套这些注册前路由表。我使用类似的东西,通过继承来实现,但它不需要继承,所以我重写了它。

我做了一个注册关键和映射到我自己的控制字符串。以下面的例子。我访问/api/related/joe和我的路由器类创建一个新的对象ApiController并调用它的方法relatedDocuments(array('tags' => 'joe'));

// the 12 strips the subdirectory my app is running in 
$index = urldecode(substr($_SERVER["REQUEST_URI"], 12)); 

Route::process($index, array(
    "#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags", 

    "#^thread/(.*)/post$#Di" => "ThreadController/post/title", 
    "#^thread/(.*)/reply$#Di" => "ThreadController/reply/title", 
    "#^thread/(.*)$#Di"   => "ThreadController/thread/title", 

    "#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags", 
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id", 
    "#^ajax/reply/(.*)$#Di"  => "ArticleController/newReply/id", 
    "#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle", 

    "#^$#Di"     => "HomeController", 
)); 

为了保持向下的错误和简单了,你可以细分表。这样,您可以将路由表放入其控制的类中。以上面的例子,你可以将三个线程调用合并为一个。

Route::process($index, array(
    "#^api/related/(.*)$#Di" => "ApiController/relatedDocuments/tags", 

    "#^thread/(.*)$#Di"   => "ThreadController/route/uri", 

    "#^ajax/tag/(.*)/(.*)$#Di" => "TagController/add/id/tags", 
    "#^ajax/reply/(.*)/post$#Di"=> "ThreadController/ajaxPost/id", 
    "#^ajax/reply/(.*)$#Di"  => "ArticleController/newReply/id", 
    "#^ajax/toggle/(.*)$#Di" => "ApiController/toggle/toggle", 

    "#^$#Di"     => "HomeController", 
)); 

然后你定义ThreadController :: route就像这样。

function route($args) { 
    Route::process($args['uri'], array(
     "#^(.*)/post$#Di" => "ThreadController/post/title", 
     "#^(.*)/reply$#Di" => "ThreadController/reply/title", 
     "#^(.*)$#Di"   => "ThreadController/thread/title", 
    )); 
} 

此外,您可以在右侧为您的路由字符串定义任何默认值。只是不要忘记记录它们,否则你会迷惑人。如果您没有在右侧包含函数名,我现在正在调用索引。 Here是我当前的代码。您可能需要将其更改为处理错误的方式和/或默认操作。

+0

第二个例子非常符合Ive实际上正在考虑的事情......(RoR风格) - 我将通过你的代码来看看。 – Mez 2008-09-25 07:08:06

1

使用Regexs列表匹配,我应该使用

例如

^/users/[\w-]+/bookmarks/(.+)/$ 
^/users/[\w-]+/bookmarks/$ 
^/users/[\w-]+/$ 

哪个对象优点:尼斯和简单,让我直接定义路线 缺点:必须是有序的,不使其易于在(非常容易出错)添加新的东西

这一点,据我所知,Django是如何做它

-4

尝试采取看看MVC模式。
Zend Framework使用它,但也CakePHP,CodeIgniter,...

我个人不喜欢MVC模型,但它的大部分时间实现为“视图为web”组件。

的决定非常依赖于偏好...

+0

我使用的是MVC的东西。我正在谈论技术方面或URL路由。这不是我的问题的答案 – Mez 2008-09-24 15:34:29

0

我想了很多框架使用Apache的mod_rewrite和前端控制器的组合。使用mod_rewrite,您可以将如下所示的URL变为:/ people/get/3: index.php?controller = people & method = get & id = 3。 Index.php将实现您的前端控制器,它根据给定的参数来路由页面请求。

+0

因此,显然忽略你的非ROA网址,它与我上面的答案一样,但使用mod_rewrite与特定的规则,并通过特定的paraameters,而不是通过代码“路由”? – Mez 2008-09-24 15:40:18

-1

默认Zend的MVC框架使用像

/router/controller/action/key1/value1/key2/value2 

其中router是路由器文件(通过mod_rewrite映射的结构,controller是从由从Zend_Controller_Actionaction派生的类中定义的控制器的动作的处理程序引用了控制器中的一个方法,名为actionAction。键/值对可以按任何顺序排列,并可作为关联数组的行为方法使用。 t在我自己的代码中,到目前为止它的工作还算不错。

+0

操作不适用于ROA系统,仅适用于RPC式系统 – Mez 2008-09-24 15:36:28

+0

您是完全正确的......我误解了这个问题。那和ROA把我扔了。我一直称它为REST。 – 2008-09-24 20:16:08

+0

虽然这不是RESTFul。只是为了记录。 – DanMan 2012-06-25 09:57:59

2

又一个框架? - 无论如何...

路由的诀窍是把它传递给你的路由控制器。

你可能想使用类似的东西,我在这里记载:

http://www.hm2k.com/posts/friendly-urls

第二个解决方案,您可以使用类似Zend框架的URL。

0

正如您所料,有很多方法可以做到这一点。

例如,在Slim Framework,路由引擎的一个实例可以是folllowing(基于图案${OBJECT}->${REQUEST METHOD}(${PATTERM}, ${CALLBACK})):

$app->get("/Home", function() { 
    print('Welcome to the home page'); 
} 

$app->get('/Profile/:memberName', function($memberName) { 
    print('I\'m viewing ' . $memberName . '\'s profile.'); 
} 

$app->post('/ContactUs', function() { 
    print('This action will be fired only if a POST request will occure'); 
} 

所以,初始化实例($app)获取每个请求方法的方法(例如获取,发布,放置,删除等),并获取一个路由作为第一个参数,并将回调作为第二个参数。

路由可以获取令牌 - 这是“变量”,它将在运行时基于某些数据(例如成员名称,文章ID,组织位置名称或其他 - 就像在每个路由控制器中一样)发生更改。

就我个人而言,我喜欢这种方式,但我认为它对于高级框架来说不够灵活。

由于我与ZF和目前的Yii工作,我有我作为一个企业的框架的一部分,创造了一个路由器,我工作的一个例子:

路由引擎是基于在正则表达式(类似于@ gradbot的一个),但得到了双向对话,所以如果你的客户端不能运行mod_rewrite(在Apache中)或在他或她的服务器上添加重写规则,他或她仍然可以使用传统包含查询字符串的网址。

该文件包含一个阵列,每个的话,每个项目类似于该实施例中:

$_FURLTEMPLATES['login'] = array(
    'i' => array(// Input - how the router parse an incomming path into query string params 
     'pattern' => '@Members/Login/[email protected]', 
     'matches' => array('Application' => 'Members', 'Module' => 'Login'), 
    ), 
    'o' => array(// Output - how the router parse a query string into a route 
     '@Application=Members(&|&)Module=Login/[email protected]' => 'Members/Login/' 
    ) 
); 

也可以使用更复杂的组合,诸如:

$_FURLTEMPLATES['article'] = array(
    'i' => array(
     'pattern' => '@CMS/Articles/([\d]+)/[email protected]', 
     'matches' => array('Application' => "CMS", 
      'Module' => 'Articles', 
      'Sector' => 'showArticle', 
      'ArticleID' => '$1'), 
    ), 
    'o' => array(
    '@Application=CMS(&|&)Module=Articles(&|&)Sector=showArticle(&|&)ArticleID=([\d]+)@' => 'CMS/Articles/$4' 
    ) 
); 

底线,正如我认为的那样,可能性是无止境的,它只取决于你希望你的框架有多复杂,以及你希望如何使用它。

如果是,例如,只是打算成为一个Web服务或简单的网站包装 - 只需与Slim框架的写作风格 - 非常简单和漂亮的代码。

但是,如果你想开发复杂的网站使用它,我认为正则表达式是解决方案。

祝你好运! :)

0

你应该看看PUX https://github.com/c9s/Pux

这里是简介

<?php 
require 'vendor/autoload.php'; // use PCRE patterns you need Pux\PatternCompiler class. 
use Pux\Executor; 

class ProductController { 
    public function listAction() { 
     return 'product list'; 
    } 
    public function itemAction($id) { 
     return "product $id"; 
    } 
} 
$mux = new Pux\Mux; 
$mux->any('/product', ['ProductController','listAction']); 
$mux->get('/product/:id', ['ProductController','itemAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$mux->post('/product/:id', ['ProductController','updateAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$mux->delete('/product/:id', ['ProductController','deleteAction'] , [ 
    'require' => [ 'id' => '\d+', ], 
    'default' => [ 'id' => '1', ] 
]); 
$route = $mux->dispatch('/product/1'); 
Executor::execute($route);