Использовать сегменты URL как параметры метода действия в Zend Framework
В Kohana/CodeIgniter у меня может быть URL-адрес в этой форме:
http://www.name.tld/controller_name/method_name/parameter_1/parameter_2/parameter_3 ...
И затем прочтите параметры в моем контроллере следующим образом:
class MyController
{
public function method_name($param_A, $param_B, $param_C ...)
{
// ... code
}
}
Как вы достигаете этого в Zend Framework?
Ответы
Ответ 1
Обновление (04/13/2016):
Ссылка в моем ответе ниже перемещена и исправлена. Однако на всякий случай он снова исчезнет - вот несколько альтернатив, которые дают подробную информацию об этой технике, а также используют исходную статью в качестве справочного материала:
@Эндрю Тейлор - это правильный способ обработки URL-адресов Zend Framework. Однако, если вы хотите иметь параметры URL-адреса в действии вашего контроллера (как в вашем примере), ознакомьтесь с этим руководством по Zend DevZone.
Ответ 2
Взгляните на классы Zend_Controller_Router:
http://framework.zend.com/manual/en/zend.controller.router.html
Это позволит вам определить Zend_Controller_Router_Route, который сопоставляет ваш URL таким образом, который вам нужен.
Пример наличия 4 статических параметров для действия индекса Index-регулятора:
$router = new Zend_Controller_Router_Rewrite();
$router->addRoute(
'index',
new Zend_Controller_Router_Route('index/index/:param1/:param2/:param3/:param4', array('controller' => 'index', 'action' => 'index'))
);
$frontController->setRouter($router);
Это добавляется в ваш bootstrap после того, как вы определили свой передний контроллер.
После вашего действия вы можете использовать:
$this->_request->getParam('param1');
Внутри вашего метода действий для доступа к значениям.
Эндрю
Ответ 3
Я расширил Zend_Controller_Action
своим классом контроллера и произвел следующие изменения:
В dispatch($action)
заменен метод
$this->$action();
с
call_user_func_array(array($this,$action), $this->getUrlParametersByPosition());
И добавил следующий метод
/**
* Returns array of url parts after controller and action
*/
protected function getUrlParametersByPosition()
{
$request = $this->getRequest();
$path = $request->getPathInfo();
$path = explode('/', trim($path, '/'));
if(@$path[0]== $request->getControllerName())
{
unset($path[0]);
}
if(@$path[1] == $request->getActionName())
{
unset($path[1]);
}
return $path;
}
Теперь для URL-адреса, например /mycontroller/myaction/123/321
в моем действии я получу все параметры после контроллера и действия
public function editAction($param1 = null, $param2 = null)
{
// $param1 = 123
// $param2 = 321
}
Дополнительные параметры в URL-адресе не вызовут никакой ошибки, поскольку вы можете отправить больше параметров для определенного метода. Вы можете получить все из них func_get_args()
И вы все равно можете использовать getParam()
обычным способом.
Ваш URL-адрес может не содержать имени действия с использованием по умолчанию.
На самом деле мой URL-адрес не содержит имен параметров. Только их ценности. (Точно так же, как в вопросе)
И вам нужно определить маршруты, чтобы указать позиции параметров в URL-адресе, чтобы следовать концепциям структуры и иметь возможность создавать URL-адреса с использованием методов Zend.
Но если вы всегда знаете позицию своего параметра в URL-адресе, вы можете легко получить его так.
Это не так сложно, как использование методов отражения, но, я думаю, это обеспечивает меньшие накладные расходы.
Теперь метод отправки выглядит следующим образом:
/**
* Dispatch the requested action
*
* @param string $action Method name of action
* @return void
*/
public function dispatch($action)
{
// Notify helpers of action preDispatch state
$this->_helper->notifyPreDispatch();
$this->preDispatch();
if ($this->getRequest()->isDispatched()) {
if (null === $this->_classMethods) {
$this->_classMethods = get_class_methods($this);
}
// preDispatch() didn't change the action, so we can continue
if ($this->getInvokeArg('useCaseSensitiveActions') || in_array($action, $this->_classMethods)) {
if ($this->getInvokeArg('useCaseSensitiveActions')) {
trigger_error('Using case sensitive actions without word separators is deprecated; please do not rely on this "feature"');
}
//$this->$action();
call_user_func_array(array($this,$action), $this->getUrlParametersByPosition());
} else {
$this->__call($action, array());
}
$this->postDispatch();
}
// whats actually important here is that this action controller is
// shutting down, regardless of dispatching; notify the helpers of this
// state
$this->_helper->notifyPostDispatch();
}
Ответ 4
Для более простого метода, который позволяет более сложные конфигурации, попробуйте этот пост. Вкратце:
Создать application/configs/routes.ini
routes.popular.route = popular/:type/:page/:sortOrder
routes.popular.defaults.controller = popular
routes.popular.defaults.action = index
routes.popular.defaults.type = images
routes.popular.defaults.sortOrder = alltime
routes.popular.defaults.page = 1
routes.popular.reqs.type = \w+
routes.popular.reqs.page = \d+
routes.popular.reqs.sortOrder = \w+
Добавить в bootstrap.php
// create $frontController if not already initialised
$frontController = Zend_Controller_Front::getInstance();
$config = new Zend_Config_Ini(APPLICATION_PATH . ‘/config/routes.ini’);
$router = $frontController->getRouter();
$router->addConfig($config,‘routes’);
Ответ 5
Первоначально опубликовано здесь http://cslai.coolsilon.com/2009/03/28/extending-zend-framework/
Мое текущее решение выглядит следующим образом:
abstract class Coolsilon_Controller_Base
extends Zend_Controller_Action {
public function dispatch($actionName) {
$parameters = array();
foreach($this->_parametersMeta($actionName) as $paramMeta) {
$parameters = array_merge(
$parameters,
$this->_parameter($paramMeta, $this->_getAllParams())
);
}
call_user_func_array(array(&$this, $actionName), $parameters);
}
private function _actionReference($className, $actionName) {
return new ReflectionMethod(
$className, $actionName
);
}
private function _classReference() {
return new ReflectionObject($this);
}
private function _constructParameter($paramMeta, $parameters) {
return array_key_exists($paramMeta->getName(), $parameters) ?
array($paramMeta->getName() => $parameters[$paramMeta->getName()]) :
array($paramMeta->getName() => $paramMeta->getDefaultValue());
}
private function _parameter($paramMeta, $parameters) {
return $this->_parameterIsValid($paramMeta, $parameters) ?
$this->_constructParameter($paramMeta, $parameters) :
$this->_throwParameterNotFoundException($paramMeta, $parameters);
}
private function _parameterIsValid($paramMeta, $parameters) {
return $paramMeta->isOptional() === FALSE
&& empty($parameters[$paramMeta->getName()]) === FALSE;
}
private function _parametersMeta($actionName) {
return $this->_actionReference(
$this->_classReference()->getName(),
$actionName
)
->getParameters();
}
private function _throwParameterNotFoundException($paramMeta, $parameters) {
throw new Exception("Parameter: {$paramMeta->getName()} Cannot be empty");
}
}