Routing - Định tuyển

Routing trong ZF là việc xác định sự ăn khớp của một request(yêu cầu) để gửi đến một controller. Thông thường bộ định tuyển sẽ xem xét URI yêu cầu, so sánh các phân đoạn của URL với các dàng buộc được cung cấp. Nếu dàng buộc và URL hợp mẫu nhau thì một tập hợp các mẫu này được trả về, một trong số đó có thể là tên controller sẽ thi hành.

Sử dụng riêng zend-route bằng lệnh composer sau:

composer require zendframework/zend-router

RouteInterface

Đơn vị cơ bản của Routing là route được sinh ra bằng việc triển khai giao diện mẫu sau:

namespace Zend\Router;

use Zend\Stdlib\RequestInterface as Request;

interface RouteInterface
{
    public static function factory(array $options = []);
    public function match(Request $request);
    public function assemble(array $params = [], array $options = []);
}

ZF triển khai sẵn cho ta nhiều lớp từ giao diện này chỉ việc sử dụng chúng nằm ở namespace Zend\Router\Http như các Route: Chain, Hostname, Literal, Method, Part, Regex, Scheme, Segment, Wildcard. Do cùng giao diện nên tất cả chúng đều có 3 hàm factory, match, assemble như trên. Và hàm factory là để khởi tạo ra đối tượng Route bằng cách truyền một mảng tham số. Sau này bạn sử dụng Route nào thì đều khởi tạo bằn cách

$route = Route::factory($option)

RouteMatch

Một Route với hàm match, nó nhận tham số là Resquest rồi so sánh với mẫu và trả về một đối tượng gọi là RouteMatch, định nghĩa của lớp này tại Zend\Router\RouteMatch. Đối tượng này có chứa các tham số URI mà Route chuyển đến, để lấy thàm số nào đó:

$id = $routeMatch->getParam('id', false);

RouteStackInterface

Thường thì trong ứng dụng sẽ có nhiều Route, các Route này tập hợp lại vào trong một đối tượng để dễ quản lý, đối tượng này có dạng triển khai từ giao diện RouteStackInterface dạng sau, là tập hợp dạng LIFO.

namespace Zend\Router;

interface RouteStackInterface extends RouteInterface
{
    public function addRoute($name, $route, $priority = null);
    public function addRoutes(array $routes);
    public function removeRoute($name);
    public function setRoutes(array $routes);
}

Trong ZF đã triển khai sẵn hai lớp từ giao diện này là: SimpleRouteStack và TreeRouteStack.

Như vậy để thêm một Route vào RouteStack dùng lệnh addRoute và thêm nhiều Route dùng lệnh addRoutes.

SimpleRouteStack : đây là bộ quản lý các route đơn giản, nó sẽ kiểm tra sự phù hợp của từng route theo thứ tự LIFO cho đến khi tìm thấy cái phù hợp.

TreeRouteStack : bộ quản lý có dạng B - Tree linh hoạt hơn. (Bộ khung ZF dùng cái này) nó phân nhánh một route thành nhiều nhánh con. Để cấu hình loại này cần một số thiết lập sau:

  • Cần một Route cơ bản (gốc) của cây.
  • Tùy chọn route_plugins để Zend\Router\RoutePluginManager nạp sau các Route.
  • Tùy chọn may_terminate cho biết router không có phân đoạn nào phía sau.
  • Tùy chọn mảng child_routes

Khi một route phù hợp với TreeRouteStack, các tham số phù hợp cho mỗi phân đoạn của cây sẽ được trả về.

(Đọc thêm) Lấy RouteMatch, TreeRouteStack trong Zend Skeleton

Việc lấy RouteMatch giúp bạn biết được Controller và Action hay bất kỳ tham số nào của Route để thực hiện các mục đích nào đó của bạn (ví dụ để ACL) có thể làm như sau:

1) Đã có Service Manager

        $servicemanager = ??? //Đã có
        $router =  $servicemanager->get('router');
        $request = $servicemanager->get('request');
        $routeMatch = $router->match($request);

        echo $routeMatch->getParam('controller');

2) Overrid hàm dispatch của Controller

Làm như cách 1 nhưng không phải lấy $request từ ServiceManager

3) Overrid hàm onDispatch của Controller

    public function onDispatch(MvcEvent $e)
    {
        $routeMatch = $e->getRouteMatch();        
        return parent::onDispatch($e); 
    }

4) Tạo ra một Listener cho Route Event trong Bootstrap của Module

 

Các Route hay dùng

Zend\Mvc\Router\Http\Literal

Route Literal dùng để so sánh chính xác theo đường dẫn URI. Cấu hình bằng cách chỉ ra đường dẫn mong muốn và thiết lập tham số "defaults" phù hợp. Ví dụ tạo ra một Literal sau:

'/foo',
    'defaults' => [
        'controller' => 'Application\Controller\IndexController',
        'action'     => 'foo',
        'myparam'    => 'value-my-params'
    ]
]);


$request = new \Zend\Http\Request();
$request->setUri('/foo'); //$request->setUri($_SERVER['REQUEST_URI']);

$rs = $route->match($request);
if ($rs) {
    echo $rs->getParam('action');
}

Thì đường dẫn "/foo" là trùng với route và nó trả về RouteMatch với key là "action" giá trị là "foo" các tham số khác tương tự.

Zend\Mvc\Router\Http\Segment

Route này cho phép xác định sự phù hợp theo đường dẫn URI, mỗi phân đoạn đại diện trong URI được biểu thị bằng dấu : và theo sau là các ký tự khác, nếu phân đoạn là tùy chọn (nghĩa là có thể có, có thể không) thì phân đoạn đó được bao bởi dấu [].

Ví dụ ký hiệu: /:foo[/:bar] thì có nghĩa là một URI nào đó bắt đầu bằng / theo sau là text (tương ứng với :foo), nếu tìm thấy một / tiếp theo thì cần có một text sau cái này nữa (tương ứng với :bar) thì là phù hợp với route này. Như vậy các URL này sẽ phù hợp: /abc, /abc/xyz, /xyz ...

Ví dụ:

$route = Segment::factory([
    'route' => '/:controller[/:action]',
    'constraints' => [
        'controller' => '[a-zA-Z][a-zA-Z0-9_-]+',  //Dàng buộc với đoạn controller
        'action'     => '[a-zA-Z][a-zA-Z0-9_-]+',  //Dàng buộc với đoạn action
    ],
    'defaults' => [
        'controller' => 'Application\Controller\IndexController',
        'action'     => 'index',
    ],
]);

Thường thì sẽ định nghĩa một mảng chứa các tham số cấu hình, rồi đưa vào hàm factory để tạo. Ví dụ khai báo một mảng Route phức tạp sau trong Zend Skeleton:

return [
    'router' => [
        'routes' => [
            // Route Literal cho truy cập trang chủ
            'home' => [
                'type' => 'literal',
                'options' => [
                    'route' => '/',
                    'defaults' => [
                        'controller' => 'Application\Controller\IndexController',
                        'action' => 'index',
                    ],
                ],
            ],

            // Literal truy cập đường dẫn /blog
            'blog' => [
                'type' => 'literal',
                'options' => [
                    'route' => '/blog',
                    'defaults' => [
                        'controller' => 'Application\Controller\BlogController',
                        'action' => 'index',
                    ],
                ],
                'may_terminate' => true,
                'child_routes' => [
                    //  Route con của blog, truy cập theo /blog/text123
                    'post' => [
                        'type' => 'segment',
                        'options' => [
                            'route' => '/[:slug]',
                            'constraints' => [
                                'slug' => '[a-zA-Z0-9_-]+',
                            ],
                            'defaults' => [
                                'action' => 'view',
                            ],
                        ],
                    ],
                    // Route cho trang /rss
                    'rss' => [
                        'type' => 'literal',
                        'options' => [
                            'route' => '/rss',
                            'defaults' => [
                                'action' => 'rss',
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
];

Đăng ký nhận bài viết mới