Aron Rotteveel
Aron Rotteveel

Reputation: 83173

Using Zend_Controller_Route to automatically determine the controller based on the user role: how?

I am currently figuring out a way to the the above.

My current application structure is as follows:

/modules
 / modulename
  /controllers
    /ProjectController.php

The application has 3 different user roles, each with functionality contained within these modules. I'd like to prevent having multiple actions for each user role in one controller as I think it makes my code less readable and ugly. So, I was thinking about a structure like this:

/modules
 / modulename
  /controllers
    /ProjectController.php
    /EmployeeProjectController.php
    /ExecutiveProjectController.php

This should work as follows:

Of course, I could relatively easy a different URL scheme to provide for this for each user role, but I do not want this. I want a uniform URL scheme.

Next step would then be to create routes for each of the controllers to rewrite them to another controller. I'd like to prevent this as well.

I want a way to globally tell the router to prefix controllers with 'Executive' or 'Employee' or whatever, based on the user role.

What would be the best way to this?

Upvotes: 0

Views: 164

Answers (2)

Aron Rotteveel
Aron Rotteveel

Reputation: 83173

I have spent quite some time figuring out how to use Zend_Controller_Router_Route to do this and have come up with as solution that works:

class App_Controller_Router_Route_Rolebasedcontroller extends Zend_Controller_Router_Route_Module
{
    /**
     * Roles that should be rewritten automatically
     * 
     * @var array
     */
    protected $_rewriteRoles = array('employee', 'executive');

    /**
     * Matches a user submitted path. Assigns and returns an array of variables
     * on a successful match.
     *
     * If a request object is registered, it uses its setModuleName(),
     * setControllerName(), and setActionName() accessors to set those values.
     * Always returns the values as an array.
     *
     * @param string $path Path used to match against this routing map
     * @return array An array of assigned values or a false on a mismatch
     */ 
    public function match($path, $partial = false)
    {
        $result = parent::match($path, $partial);

        $role = Plano_Acl::getInstance()->getCurrentRole();

        if (null !== $role && in_array($role, $this->_rewriteRoles))
        {
            if (isset($result[$this->_controllerKey]))
            {
                $result[$this->_controllerKey] = $role . ucfirst($result[$this->_controllerKey]);
            }
        }

        return $result;
    }

    /**
     * Assembles user submitted parameters forming a URL path defined by this route
     * Removes fole prefixes when required
     *
     * @param array $data An array of variable and value pairs used as parameters
     * @param bool $reset Weither to reset the current params
     * @return string Route path with user submitted parameters
     */
    public function assemble($data = array(), $reset = false, $encode = true, $partial = false)
    {
        if (!$this->_keysSet) {
            $this->_setRequestKeys();
        }

        $params = (!$reset) ? $this->_values : array();

        foreach ($data as $key => $value) {
            if ($value !== null) {
                $params[$key] = $value;
            } elseif (isset($params[$key])) {
                unset($params[$key]);
            }
        }

        $params += $this->_defaults;

        $url = '';

        if ($this->_moduleValid || array_key_exists($this->_moduleKey, $data)) {
            if ($params[$this->_moduleKey] != $this->_defaults[$this->_moduleKey]) {
                $module = $params[$this->_moduleKey];
            }
        }
        unset($params[$this->_moduleKey]);

        $controller = $params[$this->_controllerKey];

        // remove role prefix from url when required
        $role = Plano_Acl::getInstance()->getCurrentRole();
        if (null !== $role && in_array($role, $this->_rewriteRoles))        
        {
            if (substr($params[$this->_controllerKey], 0, strlen($role)) == $role)
            {
                $controller = lcfirst(substr($params[$this->_controllerKey], strlen($role)));
            }
        }

        unset($params[$this->_controllerKey]);

        $action = $params[$this->_actionKey];
        unset($params[$this->_actionKey]);

        foreach ($params as $key => $value) {
            $key = ($encode) ? urlencode($key) : $key;
            if (is_array($value)) {
                foreach ($value as $arrayValue) {
                    $arrayValue = ($encode) ? urlencode($arrayValue) : $arrayValue;
                    $url .= '/' . $key;
                    $url .= '/' . $arrayValue;
                }
            } else {
                if ($encode) $value = urlencode($value);
                $url .= '/' . $key;
                $url .= '/' . $value;
            }
        }

        if (!empty($url) || $action !== $this->_defaults[$this->_actionKey]) {
            if ($encode) $action = urlencode($action);
            $url = '/' . $action . $url;
        }

        if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) {
            if ($encode) $controller = urlencode($controller);
            $url = '/' . $controller . $url;
        }

        if (isset($module)) {
            if ($encode) $module = urlencode($module);
            $url = '/' . $module . $url;
        }

        return ltrim($url, self::URI_DELIMITER);
    }   
}

Upvotes: 1

David Weinraub
David Weinraub

Reputation: 14184

Sounds like you could use a front controller plugin with a routeStartup() method that examines the Zend_Auth instance and adds the role-specific routes (from INI or XML file, for example).

Something like this:

class My_Controller_Plugin_RouteByAuth extends Zend_Controller_Plugin_Abstract
{
    public function routeStartup()
    {
        $auth = Zend_Auth::getInstance();
        if (!$auth->hasIdentity()){
            return;
        }
        $identity = $auth->getIdentity();
        if ($identity->isAdmin()){
            $this->_addRoutes('routes_admin.ini');
        } else if ($identity->isEmployee()){
            $this->_addRoutes('routes_employee.ini');
        } else if ($identity->isExecutive()){
            $this->_addRoutes('routes_executive.ini');
        } else {
            return;
        }
    }

    protected function _addRoutes($file)
    {
        $front = Zend_Controller_Front::getInstance();
        $router = $front->getRouter();
        $routes = new Zend_Config_Ini(APPLICATION_PATH . '/configs/ ' . $file);
        $router->addConfig($routes);
    }
}

Not tested, but I hope this conveys a core of a workable idea.

Upvotes: 1

Related Questions