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', ], ], ], ], ], ], ], ];