Archive for April, 2011

Super sexy URLs with ZF and the joy of controller plugins!

Friday, April 1st, 2011

I’m in a bit of a hurry so this one will be short and sweet. I wanted to be able to use sexy uris (/your-homepage-rocks) instead of the lame-o /module/controller/action/id/# URIs. I also wanted error handling to work properly if they tried to access a page that didn’t exist so, what to do!? Build a controller plugin of course – ain’t (English police get me) they sexy?

// My\Controller\Plugin\Page.php

// I'm a huge-mongous fan of namespaces
namespace My\Controller\Plugin;
use Zend_Controller_Plugin_Abstract, Zend_Controller_Front;

class Page extends Zend_Controller_Plugin_Abstract
{
        // predispatch so that we can take over before routing kicks in
        public function preDispatch($request)
        {
                $front = \Zend_Controller_Front::getInstance();
                $dispatch = $front->getDispatcher();
                
                // normally when something isn't dispatchable an exception is thrown and we're whisked away to the errorController
                if (!$dispatch->isDispatchable($request)) {
                        $bootstrap = $front->getParam('bootstrap');
                        $website = $bootstrap->getResource('website');
                        
                        // did we find a page?
                        $page = \My\Service::getRepository('Website\Page')->findOneBy(
                                array('website' => $website->getId(), 'slug' => $request->getControllerName()));

                        if ($page) {
                                // page exists, reroute by setting controller, action, and an additional "page" param
                                $request->setModuleName('default')
                                        ->setControllerName('page')
                                        ->setActionName('index')
                                        ->setParam('page', $page);
                        }
                }
        }
}
// application\controllers\PageController

class PageController extends Zend_Controller_Action
{
        // before dispatching, verify a few page properties
        public function preDispatch()
        {
                // grab the page from earlier - if it doesn't exist it will be handled with an exception
                $page = $this->_request->page;
                if (!$page || !$page->getPublished()) {
                        // page must exist and be published
                        throw new \My\Exception\NotFound();
                } elseif (!$page->isAllowed('read')) {
                        // user must have read permission
                        throw new \My\Exception\AccessForbidden();
                }
                
                // send it to the view to take care of
                $this->view->page = $page;
        }

        public function indexAction()
        {}
}

And, it works! You can handle the case where a page doesn’t exist as usual with the errorController. If a page does exist then everything is routed over to the pageController to display as normal. The only downside to this sexy magic is the additional query when an invalid request is dispatched. Fortunately, the overhead on that is minimal.