Nested breadcrumb using zend-navigation

Hello
I have a problem using my navigation via zend-navigation, my concept of site hierarchy is such below:
blog ->category->posts
when im in the category page the breadcrumb shown is correct
blog > category
but when im in the posts the breadcrumb shows this:
blog -> posts
as you can see the ‘category’ is missing.
the ‘navigation’ key is here:

     'navigation' => [
    'default' => [
        [
            'label' => 'Home',
            'route' => 'home',
        ],
        [
            'label' => 'blog',
            'route' => 'blog',
            'pages' => [
                [
                    'label'  => 'all',
                    'route'  => 'blog',
                    'action' => 'index',
                ],
                [
                    'route'  => 'blog/detail',
                    'action' => 'detail',
                ],
                [
                    'route'=>'blog/category',
                    'action'=>'category',
                ],
                /*[
                    'label'  => 'Delete',
                    'route'  => 'album',
                    'action' => 'delete',
                ],*/
            ],
        ],
    ],];

it says i have two main pages called ‘home’ and ‘blog’ and under the ‘blog’ i have pages like ‘all’ and ‘category’ and ‘detail’ that represents posts.
how should i use nested like approach to work it out?
thank you

In your configuration for the navigation container is no “posts”?!

Example:

$navigation = new Zend\Navigation\Navigation(
    [
        [
            'label' => 'Blog',
            'uri'   => 'blog',
            'pages' => [
                [
                    'label' => 'Detail',
                    'uri'   => 'blog/detail',
                ],
                [
                    'label' => 'Category',
                    'uri'   => 'blog/category',
                    'pages' => [
                        [
                            'label'  => 'Posts',
                            'uri'    => 'blog/category/posts',
                        ],
                    ],
                ],
            ],
        ],
    ]
);
1 Like

thank you for your response but i am planning to use dynamic breadcrumb as I want to have a page builder structure in the future that accepts some parameter in one page to dynamically change the view.anyway, how should I use this $navigation in my view script?

You mean dynamic navigation? Add one detail page and then update the label and parameter of the detail page in your view script. Example:

/** @var \Zend\Navigation\Page\Mvc $page */
$page = $this->navigation('default')->findOneBy('route', 'blog/detail');
$page->setParams(['id' => $post->getId()]);
$page->setLabel($post->getTitle());

After this you can output the breadcrumbs.

You do not need a new navigation container. Extend your configuration and you will get the same results.

1 Like

thank you for your patience but here is the deal:
i have module directory set to Application as default module and Blog.
As the documentation said I put my ‘navigation’ container code in the Application->config->module.config.php and the default code is:

'navigation' => [
    'default' => [
        [
            'label' => 'Home',
            'route' => 'home',
        ],
        [
            'label' => 'Blog',
            'route' => 'blog',
            'pages' => [
                [
                    'label'  => 'All',
                    'route'  => 'blog',
                    'action' => 'index',
                ],
                [
                    'route'  => 'blog/detail',
                    'action' => 'detail',
                ],
                [
                    'route'=>'blog/category',
                    'action'=>'category',
                ],

            ],
        ],
    ],

in my Blog module the config file under Blog->config->module.config.php the route is like this:

    'router' => [
    // Open configuration for all possible routes
    'routes' => [
        // Define a new route called "blog"
        'blog' => [
            // Define a "literal" route type:
            'type' => Literal::class,
            // Configure the route itself
            'options' => [
                // Listen to "/blog" as uri:
                'route' => '/blog',
                // Define default controller and action to be called when
                // this route is matched
                'defaults' => [
                    'controller' => Controller\ListController::class,
                    'action'     => 'index',
                ],
            ],
            'may_terminate' => true,
            'child_routes'=> [
                'detail'=>[
                    'type'=>segment::class,
                    'options'=>[
                        'route'=>'/single/:id',
                        'defaults'=>[
                            'action'=>'detail',
                        ],
                        'constraints'=>[
                            'id'=>'[1-9]\d*',
                        ],
                    ],

                ],
                'category'=>[
                    'type'=>segment::class,
                    'options'=>[
                        'route'=>'/subpage-category/id-:cid/:title',
                        'defaults'=>[
                            'action'=>'category',
                        ],
                        'constraints'=>[
                            //'catTitle'=>'[a-zA-Z][a-zA-Z0-9_-]*',
                            'cid'=>'[1-9]\d*',
                        ],
                    ],

                ],

            ],
        ],
    ],
],

I have put the code you recommended in Application config like this:

      use Zend\Navigation\Navigation;
$navigation = new Navigation(
    [
        'default'=>[
            [
                'label'=>'Home',
                'route'=>'home',
            ],
            [
                'label'=>'Blog',
                'route'=>'blog',
                'pages'=> [
                    'label'=>'category',
                    'route'=>'blog/category',
                    'pages'=>[
                        'label'=>'Detail',
                        'route'=>'blog/detail',
                    ],
                ]
            ]
        ]

    ]);
return [
    'router' => [
        'routes' => [
            'home' => [
                'type' => Literal::class,
                'options' => [
                    'route'    => '/',
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action'     => 'index',
                    ],
                ],
            ],
            'application' => [
                'type'    => Segment::class,
                'options' => [
                    'route'    => '/application[/:action]',
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action'     => 'index',
                    ],
                ],
            ],
        ],
    ],
    'controllers' => [
        'factories' => [
            Controller\IndexController::class => InvokableFactory::class,
        ],
    ],
    'view_manager' => [
        'display_not_found_reason' => true,
        'display_exceptions'       => true,
        'doctype'                  => 'HTML5',
        'not_found_template'       => 'error/404',
        'exception_template'       => 'error/index',
        'template_map' => [
            'layout/layout'           => __DIR__ . '/../view/layout/layout.phtml',
            'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
            'error/404'               => __DIR__ . '/../view/error/404.phtml',
            'error/index'             => __DIR__ . '/../view/error/index.phtml',
        ],
        'template_path_stack' => [
            __DIR__ . '/../view',
        ],
    ],

    /*'navigation' => [
        'default' => [
            [
                'label' => 'Home',
                'route' => 'home',
            ],
            [
                'label' => 'Blog',
                'route' => 'blog',
                'pages' => [
                    [
                        'label'  => 'All',
                        'route'  => 'blog',
                        'action' => 'index',
                    ],
                    [
                        'route'  => 'blog/detail',
                        'action' => 'detail',
                    ],
                    [
                        'route'=>'blog/category',
                        'action'=>'category',
                    ],

                ],
            ],
        ],

    ],*/

    $navigation
];

and used the $page in the detail page here:

$page = $this->navigation('default')->findOneBy('route', 'blog/detail');
$page->setParams(['id' => $post->getId()]);
$page->setLabel($post->getTitle());

but it does not work where am i wrong?

You misunderstood me! Extend your configuration array in Application/config/module.config.php and do not add an extra navigation object!
Please remove this and then check again.

1 Like

what is the differences between using ‘navigation’ as a key in configuration array in Application/config/module.config.php ,as the file returns an array, and $navigation = new Zend\Navigation\Navigation([])? I know how to use the ‘navigation’ key in script but i cannot put nested ‘pages’ in this way:

'navigation' => [
    'default' => [
        [
            'label' => 'Home',
            'route' => 'home',
        ],
        [
            'label' => 'Blog',
            'route' => 'blog',
            'pages' => [
                [
                    'label'  => 'All',
                    'route'  => 'blog',
                    'action' => 'index',
                ],

                [
                    'label'=>'category',
                    'route'=>'blog/category',
                    'action'=>'category',
                    'pages'=>[
                            'label'=>'detail',
                            'route'  => 'blog/detail',
                            'action' => 'detail',

                    ]
                ],

            ],
        ],
    ],

],

the ‘page’ under the label ‘category’ causes an error. my main problem I don’t exactly know how to use the code you provided as an example with $navigation = new Zend\Navigation\Navigation([]) in the configuration array.would you please give me a sample to bring me out of this confusion?

Only the configuration array will work in the config file. To be clear: my code was only an example and not for copy and paste!

Compare the pages array under “blog” and the pages array under “category”. Brackets (new array) are missing!
The name is “pages” – plural. You can add more than one page.

1 Like

i really appreciate your help it seems to work but i receive an error that says ‘cid’ is missing in my detail.php and that is caused by the parent of detail page category, because in the category 's action in controller is called when i want to use detail in navigation container and it seems that i have to pass the required parameter through params[].
is there anywhere not to do so?
I mean prevent action to be called in category page?
thank you in advance

Then do the same for the category page:

$category = $post->getCategory(); // maybe like this (!)

$categoryPage = $this->navigation('default')->findOneBy('route', 'blog/category');
$categoryPage->setParams(['cid' => $category->getId()]);
$categoryPage->setLabel($category->getName());

Btw. if you need this function calls more than once, then a listener which uses the MvcEvent::EVENT_RENDER is a better choice.

1 Like

it works all the way thank you for your Big help!:heart_eyes:

I think, we should add this as example to the documentation and also an example with a listener which sets the parameters.

1 Like

that’s wise! let me know if you would like to cooperate to achieve it. I’m looking forward to hearing from you.

could you please provide me a sample on how to do it!?

In the bug tracker of zend-navigation you can find an example for a listener:

hi, I’ve been trying for weeks to get to know with event manager mechanism, although there are lots of documentation out there, I couldn’t work with it. I read your example but there are few things I can’t handle.
the case is I want to add a listener which reacts to MvcEvent::EVENT_RENDER, I mean I want to get to URL and output them automatically in view template.
this is my listener in NavigationListener.php :

 namespace Blog\Events;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\Mvc\MvcEvent;

class NavigationListener  implements ListenerAggregateInterface
{
    protected $listener;
    public function attach(EventManagerInterface $events,$priority = 1){
        $shared = $events->getSharedManager();
        $shared->attach('Zend\Mvc\View\Console\DefaultRenderingStrategy',
            MvcEvent::EVENT_RENDER,[$this,'addParams']
            ,1000);
        //$this->listener[] = $events->attach(MvcEvent::EVENT_RENDER
          //  ,[$this,'addParams']
            //,1000);
    }
    public function detach(EventManagerInterface $events){}
    public function addParams(MvcEvent $event){
        // Get request URI
        $uri = $event->getResult()->getUri();
        return $uri;
    }
} 

first of all, I know when we want to listen to a private Event we have to use shared event manager, is this particular event private?
second of all, is it a MUST to register this listener via onBootstrap() or init() methods?
I put this code in module.config.php of my module:

'service_manager' => [
    
    'invokables'=>[
        Events\NavigationListener::class => Events\NavigationListener::class,
    ]
],
'listeners'=> [
    Events\NavigationListener::class,
],

how should I know what are MvcEvent::EVENT_RENDER parameters? what is the target of this event that is to set as Identifires(I used ‘Zend\Mvc\View\Console\DefaultRenderingStrategy’)?
how to get the return value in the listener method at the end?

Sorry, maybe I misunderstand something here, but the output of a single URL has nothing to do with a navigation!
Please stay in your original posting: https://discourse.zendframework.com/t/getting-uri-using-eventmanager/385

1 Like

thanks, what about this method?does it work? if so where can I retrieve the $uri parameter?

Again: this is the wrong topic!