我正在使用Zend Framework 1.12.3开发API。我正在使用Zend_Rest_Route,但我希望有分层URL:
我正在考虑使用这种方法,因为我必须将某些主题分配给某些教授,我相信这种方案可以很好地解决它。
但是,我很难实现分层网址。我已经尝试过了:
Zend_Controller_Router_Route with Chains,在config .ini文件中,但由于必须指定控制器和动作,因此在访问http://api.example.com/professors/:professorId/subjects时它始终指向相同的动作(即,无论呼叫什么方法是 - POST,PUT,GET,DELETE - 它总是指向config .ini文件中指定的操作)。例如,如果我在配置文件中指定了getAction,使用链总是会调用getAction,无论我使用的方法是什么。目前,当进行POST调用时,它实际上调用了postAction()(对于PUT,GET,DELETE,PATCH,HEAD和OPTIONS也是如此)。我的Controller文件如下所示:
class V1_ProfessorsController extends REST_Controller
{
public function optionsAction()
{
// code goes here
}
public function headAction()
{
// code goes here
}
public function indexAction()
{
// code goes here - list of resources
}
public function getAction()
{
// code goes here
}
public function postAction()
{
// code goes here
}
public function putAction()
{
// code goes here
}
public function patchAction()
{
// code goes here
}
public function deleteAction()
{
// code goes here
}
}
对Zend_Rest_Route进行子类化并将match()函数重写为指出here。问题是,虽然这在调用http://api.example.com/professors/:professorId/subjects
时确实有效,但它仍然使用调用http://api.example.com/professors
时使用的相同ProfessorsController。我不确定这一点,但我相信最好有自己的控制器(例如ProfessorsSubjectsController)。
另外,我有一个问题。分层路线应如何运作?为不同的资源/子资源设置不同的控制器会更好吗?例如,拥有http://api.example.com/professors/:professorId
的ProfessorsController和http://api.example.com/professors/:professorId/subjects/:subjectId
的ProfessorsSubjectsController?
答案 0 :(得分:3)
我找到了一个我稍微修改过的解决方案。这是一个自定义路由类,可以执行我认为我们都希望它执行的操作。
<?php
require_once "modules.inc";
class Rest_Controller_Route extends Zend_Controller_Router_Route
{
/**
* @var Zend_Controller_Front
*/
protected $_front;
protected $_actionKey = 'action';
/**
* Prepares the route for mapping by splitting (exploding) it
* to a corresponding atomic parts. These parts are assigned
* a position which is later used for matching and preparing values.
*
* @param Zend_Controller_Front $front Front Controller object
* @param string $route Map used to match with later submitted URL path
* @param array $defaults Defaults for map variables with keys as variable names
* @param array $reqs Regular expression requirements for variables (keys as variable names)
* @param Zend_Translate $translator Translator to use for this instance
*/
public function __construct(Zend_Controller_Front $front, $route, $defaults = array(), $reqs = array(), Zend_Translate $translator = null, $locale = null)
{
$this->_front = $front;
$this->_dispatcher = $front->getDispatcher();
parent::__construct($route, $defaults, $reqs, $translator, $locale);
}
/**
* Matches a user submitted path with parts defined by a map. Assigns and
* returns an array of variables on a successful match.
*
* @param string $path Path used to match against this routing map
* @return array|false An array of assigned values or a false on a mismatch
*/
public function match($path, $partial = false)
{
$return = parent::match($path, $partial);
// add the RESTful action mapping
if ($return) {
$request = $this->_front->getRequest();
$path = $request->getPathInfo();
$params = $request->getParams();
$path = trim($path, '/');
if ($path != '') {
$path = explode('/', $path);
}
$lastParam = array_pop($path);
// Determine Action
$requestMethod = strtolower($request->getMethod());
if ($requestMethod == 'head') {
if (is_numeric($lastParam)) {
$return[$this->_actionKey] = 'head';
$return["id"] = $lastParam;
}
} else if ($requestMethod != 'get') {
if ($request->getParam('_method')) {
$return[$this->_actionKey] = strtolower($request->getParam('_method'));
} elseif ( $request->getHeader('X-HTTP-Method-Override') ) {
$return[$this->_actionKey] = strtolower($request->getHeader('X-HTTP-Method-Override'));
} else {
$return[$this->_actionKey] = $requestMethod;
}
// Map PUT, DELETE and POST to actual create/update/delete actions
// based on parameter count (posting to resource or collection)
switch( $return[$this->_actionKey] ){
case 'post':
$return[$this->_actionKey] = 'post';
break;
case 'put':
$return[$this->_actionKey] = 'put';
$return["id"] = $lastParam;
break;
case 'delete':
$return[$this->_actionKey] = 'delete';
$return["id"] = $lastParam;
break;
}
} else {
// if the last argument in the path is a numeric value, consider this request a GET of an item
if (is_numeric($lastParam)) {
$return[$this->_actionKey] = 'get';
$return["id"] = $lastParam;
} else {
if (isset($data[0]) && is_numeric($data[0])) {
$return[$this->_actionKey] = 'get';
$return["id"] = $lastParam;
} else {
$return[$this->_actionKey] = 'index';
}
}
}
}
return $return;
}
}
要使用它,请在bootstrap或index.php中创建所有这样的路由,两个例子:
$route = new Rest_Controller_Route($front, 'customers/*', array('controller' => 'customers'));
$router->addRoute('customers', $route);
$route = new Rest_Controller_Route($front, 'customers/:customer_id/documents/*', array('controller' => 'customers-documents'));
$router->addRoute('customersdocuments', $route);
这对我来说是一种魅力。你,考虑到这不是我的最终解决方案,所以可能有龙我没有发现所以要注意。 :)