Search code examples
phpsymfonymenubreadcrumbs

Building breadcrumbs in Symfony2


I'm creating a website and have successfully got my navigation Menu working using a tree structure stored in a database.

A Menu object as a active property, but I don't know how to go about setting it to active! If I can do that, then I can get the path of the currently active Menu and render the breadcrumbs using WhiteOctober's BreadcrumbsBundle or something similar.

I've read tons of questions on SO, and also looked at the different bundles available, but I'm still stuck on how to do this.

How would I go about finding out menu item to set as active?

EDIT:

EDIT 2:

Ok, I've almost done it. I've managed to compare the current path, using $this->container->get('request')->getPathInfo(); to a Menu's set url.

To do this I needed to flatten the menu tree structure into an array. As I'm using the DoctrineExtensions' Tree extension, I used its repository's getChildren($node) to do this. So now iterating over the array is easy and can compare the url and path.

In the breadcrumb controller, I can pass the $current_item from the menu controller, and again use the Tree repository and its getPath() method which recursively gets parents of the given node.

However, there is still a problem, as some pages on the website don't have a menu entry. So for example if I visit site.com/news/tags/stuff, there is no menu entry for it and so breaks the site.

So far:


Solution

  • I finally managed it albeit with a lot more code than I thought it would take.

    In my header template I call a few custom Twig extensions to render the main menu. In the MenuController, setCurrentMenuItemAction is called during the rendering of a menu in order to find out the current menu item. The current menu item needs to be known so that the breadcrumbs can be created. In essence, this method tries to match the current url path (using getPathInfo() to a menu url in the main menu.

    The easy part was finding an exact match by just comparing the url and menu urls (I chose not to handle multiple exact matches at this time).

    If no exact matches were found, i.e. the url does not match any menus, then it kinda needs to guess what to set as the current item. Firstly, the current url path and menu paths are exploded, I then used a for loop to compare the values of each array:

    $count = count($url_parts);
    //echo $count;
    $intersects = 0;
    for($i = 0; $i < $count; $i++){
            if(array_key_exists($i, $child_parts)){
            if($child_parts[$i] == $url_parts[$i]){
                //echo $child_parts[$i];
                $intersects++;
            }
        }
    }
    return $intersects;
    

    The returned value is used to calculate which menu item should be the selected one. This gives a similar result to array_intersect. The problem with array_intersect was that it would compare the arrays regardless of the order the the values were in. For example, say the current url is /news/tag/sports, this gives the intersection values of:

    Menu item urls:

    1. /news Intersects: 1
    2. /news/general Intersects: 1
    3. /news/sports Intersects: 2

    So using array_intersect, the url /news/tag/sports would result in /news/sports being the selected menu. The desired selected menu item would be /news, which is why I had to ditch array_intersect.

    As both menu #1 and #2 above have intersect values of 1, simply finding the menu with the least items its exploded array will give the desired menu.

    Finally, if there are still no matches: there is no similararities between the url and any menu item, then a temporary menu must be created. I wanted to create an instance of Menu to do this but ran into problems using this with the MenuRepository (I assume it needs a menu needs to be stored in the db), so i just created an array with the current url and the end() of the url as the name. Slightly messy - will probably need improving at some point.