3

I'm struggling to implement Zend_Navigation breadcrumbs with dynamic URL parameters.

Simple scenario: Imagine a CompanyController with a viewAction() that takes an id parameter from the URL to fetch information about a particular company.

URL: example.com/company/view/id/4

Desired breadcrumb: Home > Companies > [ Company name ]

I've did some research online and came up with the following solution: in a predispatch() hook I take the id parameter from $_SERVER['REQUEST_URI'] (we can't use the request object for this since dispatching hasn't started yet) and fetch that record from the database. Then i simply add this page to its root page in the Zend_Navigation container. You can see the implementation of it at the bottom of this post.

The problem i found with this approach is that it's not scalable:

  1. What if there's more than one action for each company?

    Apart from viewing the company record, we might also want to edit (example.com/company/edit/id/4) or perform other actions on it. Sure, we could create a switch statement to check the action from the URL, but I feel this is not scalable.

  2. What if there's more than one dynamic parameter in the URL?

    For each company we also need to manage its contacts (the company owner, the financial officer, etc) so we create a ContactsController which also holds a viewAction and editAction. Since a contact is directly tied to a company, we want to have this reflected in the URL and breadcrumbs as well:

    URL: example.com/company/company-id/4/contacts/contact-id/2
    
    Desired breadcrumb: Home > Companies > [Company name] > Contacts of [Company name] > [Contact name]
    

    We can easily set up a route for this. But now we have to fetch more than one id from the URL. This process can get more prone to mistakes as the URL's become more complex.

So far I've come up with two plausible solutions:

  1. Extend the Plugin_Navigation::preDispatch() hook

    A possible solution would be to use verbose URL parameters (like company-id and contact-id). This way we can extract the name (ie company) and fetch the record through the corresponding model class.

  2. Add the pages to the navigation container in our action methods

    This would be the most flexible option I think, but it also means that we can't manage our breadcrumbs from a central point anymore, which is a serious downside.

I've done a fair amount of research but there doesn't seem to be an out-of-the-box solution for this problem. Any help would be very much appreciated.

The Plugin_Navigation::preDispatch() hook mentioned earlier:

class Plugin_Navigation extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch()
    {
        $view       = Zend_Controller_Action_HelperBroker::getExistingHelper('ViewRenderer')->view;
        $config     = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml','nav');
        $container  = new Zend_Navigation($config);

        $urlParts   = explode('/', ltrim($_SERVER['REQUEST_URI'], '/'));
        $pos        = array_search('id', $urlParts);

        if ($pos !== FALSE)
        {
            $id         = $urlParts[$pos + 1];

            $model      = new Model_Company();
            $company    = $model->fetchOne($id);

            $rootPage   = $container->findOneBy('controller', 'company');

            $rootPage->addPage(new Zend_Navigation_Page_Mvc(array(
                    'controller' => 'company',
                    'action'     => 'view',
                    'label'      => $company->name
                )));

            $view->navigation($container);
        }
    }
}

Some references on this topic:

Community
  • 1
  • 1
Freek Vanraes
  • 227
  • 5
  • 11

1 Answers1

0

You can Solve This Simply By bootstraping the front controller in the bootstrap.php file

in bootstrap.php file add:

public function _initRequest()
{
    $this->bootstrap('frontController');
    $front = $this->getResource('frontController');
    $front->setRequest(new Zend_Controller_Request_Http());

    $request = $front->getRequest();
}

then in the plugin pass the request object in the preDispatch function :

class Your_Controller_Plugin_PluginName extends Zend_Controller_Plugin_Abstract
{
     public function preDispatch(Zend_Controller_Request_Abstract $request)
     {
           $MyController =$request->getControllerName();
           $MyAction     =$request->getActionName();
           $MyParams     =$request->getParams(); 

    //now you can go ahead with implementing your breadcrumb

     }
}