Containers

Containers have methods for adding, retrieving, deleting, and iterating pages. Containers implement the SPL interfaces RecursiveIterator and Countable, meaning that a container can be iterated using the SPL RecursiveIteratorIterator class.

Creating containers

Zend\Navigation\AbstractContainer can not be instantiated directly. Use Zend\Navigation\Navigation if you want to instantiate a container.

Zend\Navigation\Navigation can be constructed entirely empty, or take an array or a Traversable object with pages to put in the container. Each page provided via options will eventually be passed to the addPage() method of the container class, which means that each element in the options can be also be an array, Traversable object, or a Zend\Navigation\Page\AbstractPage instance.

Creating a container using an array

use Zend\Navigation\Navigation;

/*
 * Create a container from an array
 *
 * Each element in the array will be passed to
 * Zend\Navigation\Page\AbstractPage::factory() when constructing.
 */
$container = new Navigation([
    [
        'label' => 'Page 1',
        'id' => 'home-link',
        'uri' => '/',
    ],
    [
        'label' => 'Zend',
        'uri' => 'http://www.zend-project.com/',
        'order' => 100,
    ],
    [
        'label' => 'Page 2',
        'controller' => 'page2',
        'pages' => [
            [
                'label' => 'Page 2.1',
                'action' => 'page2_1',
                'controller' => 'page2',
                'class' => 'special-one',
                'title' => 'This element has a special class',
                'active' => true,
            ],
            [
                'label' => 'Page 2.2',
                'action' => 'page2_2',
                'controller' => 'page2',
                'class' => 'special-two',
                'title' => 'This element has a special class too',
            ],
        ],
    ],
    [
        'label' => 'Page 2 with params',
        'action' => 'index',
        'controller' => 'page2',
        // specify a param or two,
        'params' => [
            'format' => 'json',
            'foo' => 'bar',
        ]
    ],
    [
        'label' => 'Page 2 with params and a route',
        'action' => 'index',
        'controller' => 'page2',

        // specify a route name and a param for the route
        'route' => 'nav-route-example',
        'params' => [
            'format' => 'json',
        ],
    ],
    [
        'label' => 'Page 3',
        'action' => 'index',
        'controller' => 'index',
        'module' => 'mymodule',
        'reset_params' => false,
    ],
    [
        'label' => 'Page 4',
        'uri' => '#',
        'pages' => [
            [
                'label' => 'Page 4.1',
                'uri' => '/page4',
                'title' => 'Page 4 using uri',
                'pages' => [
                    [
                        'label' => 'Page 4.1.1',
                        'title' => 'Page 4 using mvc params',
                        'action' => 'index',
                        'controller' => 'page4',
                        // let's say this page is active
                        'active' => '1',
                    ]
                ],
            ],
        ],
    ],
    [
        'label' => 'Page 0?',
        'uri' => '/setting/the/order/option',

        // setting order to -1 should make it appear first
        'order' => -1,
    ],
    [
        'label' => 'Page 5',
        'uri' => '/',

        // this page should not be visible
        'visible' => false,
        'pages' => [
            [
                'label' => 'Page 5.1',
                'uri' => '#',
                'pages' => [
                    [
                        'label' => 'Page 5.1.1',
                        'uri' => '#',
                        'pages' => [
                            [
                                'label' => 'Page 5.1.2',
                                'uri' => '#',

                                // let's say this page is active
                                'active' => true,
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
    [
        'label' => 'ACL page 1 (guest)',
        'uri' => '#acl-guest',
        'resource' => 'nav-guest',
        'pages' => [
            [
                'label' => 'ACL page 1.1 (foo)',
                'uri' => '#acl-foo',
                'resource' => 'nav-foo',
            ],
            [
                'label' => 'ACL page 1.2 (bar)',
                'uri' => '#acl-bar',
                'resource' => 'nav-bar',
            ],
            [
                'label' => 'ACL page 1.3 (baz)',
                'uri' => '#acl-baz',
                'resource' => 'nav-baz',
            ],
            [
                'label' => 'ACL page 1.4 (bat)',
                'uri' => '#acl-bat',
                'resource' => 'nav-bat',
            ],
        ],
    ],
    [
        'label' => 'ACL page 2 (member)',
        'uri' => '#acl-member',
        'resource' => 'nav-member',
    ],
    [
        'label' => 'ACL page 3 (admin',
        'uri' => '#acl-admin',
        'resource' => 'nav-admin',
        'pages' => [
            [
                'label' => 'ACL page 3.1 (nothing)',
                'uri' => '#acl-nada',
            ],
        ],
    ],
    [
        'label' => 'Zend Framework',
        'route' => 'zf-route',
    ],
]);

Adding pages

Adding pages to a container can be done with the methods addPage(), addPages(), or setPages(). See examples below for explanation.

use Zend\Config\Config;
use Zend\Navigation\Navigation;
use Zend\Navigation\Page\AbstractPage;

// create container
$container = new Navigation();

// add page by giving a page instance
$container->addPage(AbstractPage::factory([
    'uri' => 'http://www.example.com/',
]]);

// add page by giving an array
$container->addPage([
    'uri' => 'http://www.example.com/',
]);

// add page by giving a Traversable object; in this case, a zend-config
// instance.
$container->addPage(Config([
    'uri' => 'http://www.example.com/',
]));

$pages = [
    [
        'label'  => 'Save',
        'action' => 'save',
    ],
    [
        'label' =>  'Delete',
        'action' => 'delete',
    ],
];

// add two pages
$container->addPages($pages);

// remove existing pages and add the given pages
$container->setPages($pages);

Removing pages

Removing pages can be done with removePage() or removePages(). removePage() accepts an instance of a page or an integer. Integer arguments correspond to the order a page has. removePages() will remove all pages in the container.

use Zend\Navigation\Navigation;

$container = new Navigation([
    [
        'label'  => 'Page 1',
        'action' => 'page1',
    ],
    [
        'label'  => 'Page 2',
        'action' => 'page2',
        'order'  => 200,
    ],
    [
        'label'  => 'Page 3',
        'action' => 'page3',
    ],
]);

// remove page by implicit page order
$container->removePage(0);      // removes Page 1

// remove page by instance
$page3 = $container->findOneByAction('page3');
$container->removePage($page3); // removes Page 3

// remove page by explicit page order
$container->removePage(200);    // removes Page 2

// remove all pages
$container->removePages();      // removes all pages

Remove a page recursively

Removing a page recursively can be done with the second parameter of the removePage() method, which expects a boolean value.

use Zend\Navigation\Navigation;

$container = new Navigation(
    [
        [
            'label' => 'Page 1',
            'route' => 'page1',
            'pages' => [
                [
                    'label' => 'Page 1.1',
                    'route' => 'page1/page1-1',
                    'pages' => [
                        [
                            'label' => 'Page 1.1.1',
                            'route' => 'page1/page1-1/page1-1-1',
                        ],
                    ],
                ],
            ],
        ],
    ]
);

// Removes Page 1.1.1
$container->removePage(
    $container->findOneBy('route', 'page1/page1-1/page1-1-1'),
    true
);

Finding pages

Containers have three finder methods for retrieving pages. Each recursively searches the container testing for properties with values that match the one provided.

The finder methods can also be used magically by appending the property name to findBy, findOneBy, or findAllBy. As an example, findOneByLabel('Home') will return the first matching page with label 'Home'.

Other combinations include findByLabel(...), findOneByTitle(...), findAllByController(...), etc. Finder methods also work on custom properties, such as findByFoo('bar').

use Zend\Navigation\Navigation;

$container = new Navigation([
    [
        'label' => 'Page 1',
        'uri'   => 'page-1',
        'foo'   => 'bar',
        'pages' => [
            [
                'label' => 'Page 1.1',
                'uri'   => 'page-1.1',
                'foo'   => 'bar',
            ],
            [
                'label' => 'Page 1.2',
                'uri'   => 'page-1.2',
                'class' => 'my-class',
            ],
            [
                'type'   => 'uri',
                'label'  => 'Page 1.3',
                'uri'    => 'page-1.3',
                'action' => 'about',
            ],
        ],
    ],
    [
        'label'      => 'Page 2',
        'id'         => 'page_2_and_3',
        'class'      => 'my-class',
        'module'     => 'page2',
        'controller' => 'index',
        'action'     => 'page1',
    ],
    [
        'label'      => 'Page 3',
        'id'         => 'page_2_and_3',
        'module'     => 'page3',
        'controller' => 'index',
    ],
]);

// The 'id' is not required to be unique, but be aware that
// having two pages with the same id will render the same id attribute
// in menus and breadcrumbs.

// Returns "Page 2":
$found = $container->findBy('id', 'page_2_and_3');

// Returns "Page 2":
$found = $container->findOneBy('id', 'page_2_and_3');

// Returns "Page 2" AND "Page 3":
$found = $container->findBy('id', 'page_2_and_3', true);

// Returns "Page 2":
$found = $container->findById('page_2_and_3');

// Returns "Page 2":
$found = $container->findOneById('page_2_and_3');

// Returns "Page 2" AND "Page 3":
$found = $container->findAllById('page_2_and_3');

// Find all pages matching the CSS class "my-class":
// Returns "Page 1.2" and "Page 2":
$found = $container->findAllBy('class', 'my-class');
$found = $container->findAllByClass('my-class');

// Find first page matching CSS class "my-class":
// Returns "Page 1.2":
$found = $container->findOneByClass('my-class');

// Find all pages matching the CSS class "non-existent":
// Returns an empty array.
$found = $container->findAllByClass('non-existent');

// Find first page matching the CSS class "non-existent":
// Returns null.
$found = $container->findOneByClass('non-existent');

// Find all pages with custom property 'foo' = 'bar':
// Returns "Page 1" and "Page 1.1":
$found = $container->findAllBy('foo', 'bar');

// To achieve the same magically, 'foo' must be in lowercase.
// This is because 'foo' is a custom property, and thus the
// property name is not normalized to 'Foo':
$found = $container->findAllByfoo('bar');

// Find all with controller = 'index':
// Returns "Page 2" and "Page 3":
$found = $container->findAllByController('index');

Iterating containers

Zend\Navigation\AbstractContainer implements RecursiveIterator. iterate a container recursively, use the RecursiveIteratorIterator class.

use RecursiveIteratorIterator;
use Zend\Navigation\Navigation;

/*
 * Create a container from an array
 */
$container = new Navigation([
    [
        'label' => 'Page 1',
        'uri'   => '#',
    ],
    [
        'label' => 'Page 2',
        'uri'   => '#',
        'pages' => [
            [
                'label' => 'Page 2.1',
                'uri'   => '#',
            ],
            [
                'label' => 'Page 2.2',
                'uri'   => '#',
            ],
        ],
    ],
    [
        'label' => 'Page 3',
        'uri'   => '#',
    ],
]);

// Iterate flat using regular foreach:
// Output: Page 1, Page 2, Page 3
foreach ($container as $page) {
    echo $page->label;
}

// Iterate recursively using RecursiveIteratorIterator
$it = new RecursiveIteratorIterator(
    $container,
    RecursiveIteratorIterator::SELF_FIRST
);

// Output: Page 1, Page 2, Page 2.1, Page 2.2, Page 3
foreach ($it as $page) {
    echo $page->label;
}

Other operations

hasPage

hasPage(AbstractPage $page) : bool

Check if the container has the given page.

hasPages

hasPages() : bool

Checks if there are any pages in the container, and is equivalent to count($container) > 0.

toArray

toArray() : array

Converts the container and the pages in it to a (nested) array. This can be useful for serializing and debugging.

use Zend\Navigation\Navigation;

$container = new Navigation([
    [
        'label' => 'Page 1',
        'uri'   => '#',
    ],
    [
        'label' => 'Page 2',
        'uri'   => '#',
        'pages' => [
            [
                'label' => 'Page 2.1',
                'uri'   => '#',
            ],
            [
                'label' => 'Page 2.2',
               'uri'   => '#',
            ],
        ],
    ],
]);

var_dump($container->toArray());

/* Output:
array(2) {
  [0]=> array(15) {
    ["label"]=> string(6) "Page 1"
    ["id"]=> NULL
    ["class"]=> NULL
    ["title"]=> NULL
    ["target"]=> NULL
    ["rel"]=> array(0) {
    }
    ["rev"]=> array(0) {
    }
    ["order"]=> NULL
    ["resource"]=> NULL
    ["privilege"]=> NULL
    ["active"]=> bool(false)
    ["visible"]=> bool(true)
    ["type"]=> string(23) "Zend\Navigation\Page\Uri"
    ["pages"]=> array(0) {
    }
    ["uri"]=> string(1) "#"
  }
  [1]=> array(15) {
    ["label"]=> string(6) "Page 2"
    ["id"]=> NULL
    ["class"]=> NULL
    ["title"]=> NULL
    ["target"]=> NULL
    ["rel"]=> array(0) {
    }
    ["rev"]=> array(0) {
    }
    ["order"]=> NULL
    ["resource"]=> NULL
    ["privilege"]=> NULL
    ["active"]=> bool(false)
    ["visible"]=> bool(true)
    ["type"]=> string(23) "Zend\Navigation\Page\Uri"
    ["pages"]=> array(2) {
      [0]=> array(15) {
        ["label"]=> string(8) "Page 2.1"
        ["id"]=> NULL
        ["class"]=> NULL
        ["title"]=> NULL
        ["target"]=> NULL
        ["rel"]=> array(0) {
        }
        ["rev"]=> array(0) {
        }
        ["order"]=> NULL
        ["resource"]=> NULL
        ["privilege"]=> NULL
        ["active"]=> bool(false)
        ["visible"]=> bool(true)
        ["type"]=> string(23) "Zend\Navigation\Page\Uri"
        ["pages"]=> array(0) {
        }
        ["uri"]=> string(1) "#"
      }
      [1]=>
      array(15) {
        ["label"]=> string(8) "Page 2.2"
        ["id"]=> NULL
        ["class"]=> NULL
        ["title"]=> NULL
        ["target"]=> NULL
        ["rel"]=> array(0) {
        }
        ["rev"]=> array(0) {
        }
        ["order"]=> NULL
        ["resource"]=> NULL
        ["privilege"]=> NULL
        ["active"]=> bool(false)
        ["visible"]=> bool(true)
        ["type"]=> string(23) "Zend\Navigation\Page\Uri"
        ["pages"]=> array(0) {
        }
        ["uri"]=> string(1) "#"
      }
    }
    ["uri"]=> string(1) "#"
  }
}
*/