Bootstrap

Phalcon学习笔记(5)router路由器

Web开发中的router和平时家里拨号上网使用的路由器不是一回事,Web开发中的router会把用户传递过来的URL解析成相应的参数并传递给http程序来解释执行。


在MVC模式中,用户能够访问的是Controller/Action,然后http程序会根据用户访问的Controller/Action把页面呈现给用户,而router就是负责把这个把用户访问的URL解释为Controller/Action的解释器


router是Phalcon中必须被DI依赖注入器注册的部件,因为这是MVC框架运行所必需的,否则无法解析URL


router的基本用法就是:1.定义一个router;2.定义其解析规则;3.将其注册到DI


当然也可以使用更简单的办法,就是在DI注册时使用匿名函数,一次完成所有操作

$di->set('router', function () {
    $router = new \Phalcon\Mvc\Router();
    $router->add('/:module/:controller/:action/:params', array(
        'module' => 1,
        'controller' => 2,
        'action' => 3,
        'params' => 4,
    ));
    return $router;
});

其中最复杂的就是定义解析规则,router会根据用户定义的规则将URL解析到合适的控制器和行为上


最简单的解析规则就是直接给出URL的定义,使用router的add()函数

//Define a route
$router->add(
    "/admin/users/my-profile",
    array(
        "controller" => "users",
        "action"     => "profile",
    )
);

//Another route
$router->add(
    "/admin/users/change-password",
    array(
        "controller" => "users",
        "action"     => "changePassword",
    )
);

这样一个一个定义的话对于一个网站来讲是不可能的,除非极特殊的URL,比如管理员后台入口才可以,一般都会使用通配符定义

//Define a route
$router->add(
    "/admin/:controller/a/:action/:params",
    array(
        "controller" => 1,
        "action"     => 2,
        "params"     => 3,
    )
);

其中第一个参数中以冒号开头的部分对应第二个参数中的键值

对于一些特殊的URL还可以使用正则表达式

$router->add(
    "/news/([0-9]{4})/([0-9]{2})/([0-9]{2})/:params",
    array(
            "controller" => "posts",
            "action"     => "show",
            "year"       => 1, // ([0-9]{4})
            "month"      => 2, // ([0-9]{2})
            "day"        => 3, // ([0-9]{2})
            "params"     => 4, // :params
    )
);

这里并没有指定哪个字段对应控制器,那个字段对应行为,但是遇到符合这个正则表达式的URL,则会自动调用postsController/showAction,然后把year/month/day识别为变量,并传递给Controller/showAction


注意识别字段时以1开始,而不是C或者php风格的从0开始


在Controller/showAction中,可以这样接收传递过来的变量

<?php

class PostsController extends \Phalcon\Mvc\Controller
{

    public function indexAction()
    {

    }

    public function showAction()
    {

        // Return "year" parameter
        $year = $this->dispatcher->getParam("year");

        // Return "month" parameter
        $month = $this->dispatcher->getParam("month");

        // Return "day" parameter
        $day = $this->dispatcher->getParam("day");

    }

}
通过dispatcher获取了year/month/day,这样这3个变量就得到了解析,还可以通过setVar("var_name", $var_value)的方式输出到视图里

不使用正则表达式,还可以用花括号代指变量,而不必在router里面指定这个变量

$router->add(
    "/documentation/{chapter}/{name}.{type:[a-z]+}",
    array(
        "controller" => "documentation",
        "action"     => "show"
    )
);
然后在Controller里直接读取chapter和name这2个变量

<?php

class DocumentationController extends \Phalcon\Mvc\Controller
{

    public function showAction()
    {

        // Returns "name" parameter
        $year = $this->dispatcher->getParam("name");

        // Returns "type" parameter
        $year = $this->dispatcher->getParam("type");

    }

}
还有一种短写语法

// Short form
$router->add("/posts/{year:[0-9]+}/{title:[a-z\-]+}", "Posts::show");

// Array form:
$router->add(
    "/posts/([0-9]+)/([a-z\-]+)",
    array(
       "controller" => "posts",
       "action"     => "show",
       "year"       => 1,
       "title"      => 2,
    )
);

还可以把花括号定义变量名的方法与正则表达式字段对应法结合起来

$router->add('/news/{country:[a-z]{2}}/([a-z+])/([a-z\-+])',
    array(
        'section' => 2, //Positions start with 2
        'article' => 3
    )
);
识别的时候,要注意因为1号位(注意Phalcon识别是从1开始,而不是从0开始)已经被花括号变量名识别过了,所以在后面的对应关系中必须要从2开始


对于多模块的程序,router中必须包含module的信息

$router->add("/login", array(
    'module' => 'backend',
    'controller' => <span class="s1">'Backend\Controllers\Login'</span><span class="p">,</span>
    'action' => 'index',
));

$router->add("/products/:action", array(
    'module' => 'frontend',
    'controller' => 'products',
    'action' => 1,
));
必要时可以使用带命名空间的全名


如果开发者希望在http访问方式不同时,能够产生不同的行为,还可以这样

// This route only will be matched if the HTTP method is GET
$router->addGet("/products/edit/{id}", "Posts::edit");

// This route only will be matched if the HTTP method is POST
$router->addPost("/products/save", "Posts::save");

// This route will be matched if the HTTP method is POST or PUT
$router->add("/products/update")->via(array("POST", "PUT"));

还有一些其他的用法

// Taking URI from $_GET["_url"]
$router->handle();

// or Setting the URI value directly
$router->handle("/employees/edit/17");

// Getting the processed controller
echo $router->getControllerName();

// Getting the processed action
echo $router->getActionName();

//Get the matched route
$route = $router->getMatchedRoute();
通过handle()函数,可以解析URL,然而这并不会直接发生什么作用,而是要通过getControllerName()和getActionName()这两个函数才能发挥作用,一般这两个函数用于用户权限判定,以确定用户是否有对应的Controller/Action的访问权限。


另外在用户输入错误的URL时,还可以这样处理

    $router->notFound(array(
        'module' => 'entrance',
        'controller' => 'index',
        'action' => 'index',
    ));
    $router->setDefaultModule('entrance');
    $router->setDefaultController('index');
    $router->setDefaultAction('index');

官方文档中还提供了这样一段代码,可以让开发者测试路由功能

<?php

//These routes simulate real URIs
$testRoutes = array(
    '/',
    '/index',
    '/index/index',
    '/index/test',
    '/products',
    '/products/index/',
    '/products/show/101',
);

$router = new Phalcon\Mvc\Router();

//Add here your custom routes

//Testing each route
foreach ($testRoutes as $testRoute) {

    //Handle the route
    $router->handle($testRoute);

    echo 'Testing ', $testRoute, '<br>';

    //Check if some route was matched
    if ($router->wasMatched()) {
        echo 'Controller: ', $router->getControllerName(), '<br>';
        echo 'Action: ', $router->getActionName(), '<br>';
    } else {
        echo 'The route wasn\'t matched by any route<br>';
    }
    echo '<br>';

}
主要就是看看解析的对不对,解析的不对就要调整解析规则


以笔者的经验来看,多module的router解析时比较容易出错,另外param不能为0,在URL中也应该尽量避免0,因为Phalcon会把0的param识别为null,虽然一般来讲数据库相关的网站涉及到id时都是从1开始,一般不会有问题,不过开发者在使用时还是要注意


在官方文档中还有更详尽的内容

https://docs.phalconphp.com/en/latest/reference/routing.html

;