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:
What if there's more than one action for each company?
Apart from
view
ing the company record, we might also want toedit
(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.What if there's more than one dynamic parameter in the URL?
For each
company
we also need to manage itscontacts
(the company owner, the financial officer, etc) so we create aContactsController
which also holds aviewAction
andeditAction
. 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:
Extend the
Plugin_Navigation::preDispatch()
hookA possible solution would be to use verbose URL parameters (like
company-id
andcontact-id
). This way we can extract the name (iecompany
) and fetch the record through the corresponding model class.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: