Question

In Kohana/CodeIgniter, I can have a URL in this form:

http://www.name.tld/controller_name/method_name/parameter_1/parameter_2/parameter_3 ...

And then read the parameters in my controller as follows:

class MyController 
{
    public function method_name($param_A, $param_B, $param_C ...)
    {
        // ... code
    }
}

How do you achieve this in the Zend Framework?

Was it helpful?

Solution

Update (04/13/2016): The link in my answer below moved and has been fixed. However, just in case it disappears again -- here are a few alternatives that provide some in depth information on this technique, as well as use the original article as reference material:


@Andrew Taylor's response is the proper Zend Framework way of handling URL parameters. However, if you would like to have the URL parameters in your controller's action (as in your example) - check out this tutorial on Zend DevZone.

OTHER TIPS

Take a look at the Zend_Controller_Router classes:

http://framework.zend.com/manual/en/zend.controller.router.html

These will allow you to define a Zend_Controller_Router_Route which maps to your URL in the way that you need.

An example of having 4 static params for the Index action of the Index controller is:

$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);

This is added to your bootstrap after you've defined your front controller.

Once in your action, you can then use:

$this->_request->getParam('param1');

Inside your action method to access the values.

Andrew

I have extended Zend_Controller_Action with my controller class and made the following changes:

In dispatch($action) method replaced

$this->$action();

with

call_user_func_array(array($this,$action), $this->getUrlParametersByPosition());

And added the following method

/**
 * 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;
}

Now for a URL like /mycontroller/myaction/123/321

in my action I will get all the params following controller and action

public function editAction($param1 = null, $param2 = null)
{
    // $param1 = 123
    // $param2 = 321
}

Extra parameters in URL won't cause any error as you can send more params to method then defined. You can get all of them by func_get_args() And you can still use getParam() in a usual way. Your URL may not contain action name using default one.

Actually my URL does not contain parameter names. Only their values. (Exactly as it was in the question) And you have to define routes to specify parameters positions in URL to follow the concepts of framework and to be able to build URLs using Zend methods. But if you always know the position of your parameter in URL you can easily get it like this.

That is not as sophisticated as using reflection methods but I guess provides less overhead.

Dispatch method now looks like this:

/**
 * 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();
}    

For a simpler method that allows for more complex configurations, try this post. In summary:

Create 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+

Add to 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’);

Originally posted here http://cslai.coolsilon.com/2009/03/28/extending-zend-framework/

My current solution is as follows:

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”); 
    } 
} 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top