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 Zend\Config\Config object with pages to put in the container. Each page in the given array/config will eventually be passed to the addPage() method of the container class, which means that each element in the array/config can be an array, a config object or a Zend\Navigation\Page\AbstractPage instance.

Creating a container using an array

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * Create a container from an array
 *
 * Each element in the array will be passed to
 * Zend\Navigation\Page\AbstractPage::factory() when constructing.
 */
$container = new Zend\Navigation\Navigation(array(
    array(
        'label' => 'Page 1',
        'id' => 'home-link',
        'uri' => '/',
    ),
    array(
        'label' => 'Zend',
        'uri' => 'http://www.zend-project.com/',
        'order' => 100,
    ),
    array(
        'label' => 'Page 2',
        'controller' => 'page2',
        'pages' => array(
            array(
                'label' => 'Page 2.1',
                'action' => 'page2_1',
                'controller' => 'page2',
                'class' => 'special-one',
                'title' => 'This element has a special class',
                'active' => true,
            ),
            array(
                'label' => 'Page 2.2',
                'action' => 'page2_2',
                'controller' => 'page2',
                'class' => 'special-two',
                'title' => 'This element has a special class too',
            ),
        ),
    ),
    array(
        'label' => 'Page 2 with params',
        'action' => 'index',
        'controller' => 'page2',
        // specify a param or two,
        'params' => array(
            'format' => 'json',
            'foo' => 'bar',
        )
    ),
    array(
        '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' => array(
            'format' => 'json',
        ),
    ),
    array(
        'label' => 'Page 3',
        'action' => 'index',
        'controller' => 'index',
        'module' => 'mymodule',
        'reset_params' => false,
    ),
    array(
        'label' => 'Page 4',
        'uri' => '#',
        'pages' => array(
            array(
                'label' => 'Page 4.1',
                'uri' => '/page4',
                'title' => 'Page 4 using uri',
                'pages' => array(
                    array(
                        'label' => 'Page 4.1.1',
                        'title' => 'Page 4 using mvc params',
                        'action' => 'index',
                        'controller' => 'page4',
                        // let's say this page is active
                        'active' => '1',
                    )
                ),
            ),
        ),
    ),
    array(
        'label' => 'Page 0?',
        'uri' => '/setting/the/order/option',
        // setting order to -1 should make it appear first
        'order' => -1,
    ),
    array(
        'label' => 'Page 5',
        'uri' => '/',
        // this page should not be visible
        'visible' => false,
        'pages' => array(
            array(
                'label' => 'Page 5.1',
                'uri' => '#',
                'pages' => array(
                    array(
                        'label' => 'Page 5.1.1',
                        'uri' => '#',
                        'pages' => array(
                            array(
                                'label' => 'Page 5.1.2',
                                'uri' => '#',
                                // let's say this page is active
                                'active' => true,
                            ),
                        ),
                    ),
                ),
            ),
        ),
    ),
    array(
        'label' => 'ACL page 1 (guest)',
        'uri' => '#acl-guest',
        'resource' => 'nav-guest',
        'pages' => array(
            array(
                'label' => 'ACL page 1.1 (foo)',
                'uri' => '#acl-foo',
                'resource' => 'nav-foo',
            ),
            array(
                'label' => 'ACL page 1.2 (bar)',
                'uri' => '#acl-bar',
                'resource' => 'nav-bar',
            ),
            array(
                'label' => 'ACL page 1.3 (baz)',
                'uri' => '#acl-baz',
                'resource' => 'nav-baz',
            ),
            array(
                'label' => 'ACL page 1.4 (bat)',
                'uri' => '#acl-bat',
                'resource' => 'nav-bat',
            ),
        ),
    ),
    array(
        'label' => 'ACL page 2 (member)',
        'uri' => '#acl-member',
        'resource' => 'nav-member',
    ),
    array(
        'label' => 'ACL page 3 (admin',
        'uri' => '#acl-admin',
        'resource' => 'nav-admin',
        'pages' => array(
            array(
                'label' => 'ACL page 3.1 (nothing)',
                'uri' => '#acl-nada',
            ),
        ),
    ),
    array(
        'label' => 'Zend Framework',
        'route' => 'zf-route',
    ),
));

Creating a container using a config object

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* CONTENTS OF /path/to/navigation.xml:
<?xml version="1.0" encoding="UTF-8"?>
<nav>

  <zend>
      <label>Zend</label>
      <uri>http://www.zend-project.com/</uri>
      <order>100</order>
  </zend>

  <page1>
      <label>Page 1</label>
      <uri>page1</uri>
      <pages>

          <page1_1>
              <label>Page 1.1</label>
              <uri>page1/page1_1</uri>
          </page1_1>

      </pages>
  </page1>

  <page2>
      <label>Page 2</label>
      <uri>page2</uri>
      <pages>

          <page2_1>
              <label>Page 2.1</label>
              <uri>page2/page2_1</uri>
          </page2_1>

          <page2_2>
              <label>Page 2.2</label>
              <uri>page2/page2_2</uri>
              <pages>

                  <page2_2_1>
                      <label>Page 2.2.1</label>
                      <uri>page2/page2_2/page2_2_1</uri>
                  </page2_2_1>

                  <page2_2_2>
                      <label>Page 2.2.2</label>
                      <uri>page2/page2_2/page2_2_2</uri>
                      <active>1</active>
                  </page2_2_2>

              </pages>
          </page2_2>

          <page2_3>
              <label>Page 2.3</label>
              <uri>page2/page2_3</uri>
              <pages>

                  <page2_3_1>
                      <label>Page 2.3.1</label>
                      <uri>page2/page2_3/page2_3_1</uri>
                  </page2_3_1>

                  <page2_3_2>
                      <label>Page 2.3.2</label>
                      <uri>page2/page2_3/page2_3_2</uri>
                      <visible>0</visible>
                      <pages>

                              <page2_3_2_1>
                                  <label>Page 2.3.2.1</label>
                                  <uri>page2/page2_3/page2_3_2/1</uri>
                                  <active>1</active>
                              </page2_3_2_1>

                              <page2_3_2_2>
                                  <label>Page 2.3.2.2</label>
                                  <uri>page2/page2_3/page2_3_2/2</uri>
                                  <active>1</active>

                                  <pages>
                                      <page_2_3_2_2_1>
                                          <label>Ignore</label>
                                          <uri>#</uri>
                                          <active>1</active>
                                      </page_2_3_2_2_1>
                                  </pages>
                              </page2_3_2_2>

                      </pages>
                  </page2_3_2>

                  <page2_3_3>
                      <label>Page 2.3.3</label>
                      <uri>page2/page2_3/page2_3_3</uri>
                      <resource>admin</resource>
                      <pages>

                              <page2_3_3_1>
                                  <label>Page 2.3.3.1</label>
                                  <uri>page2/page2_3/page2_3_3/1</uri>
                                  <active>1</active>
                              </page2_3_3_1>

                              <page2_3_3_2>
                                  <label>Page 2.3.3.2</label>
                                  <uri>page2/page2_3/page2_3_3/2</uri>
                                  <resource>guest</resource>
                                  <active>1</active>
                              </page2_3_3_2>

                      </pages>
                  </page2_3_3>

              </pages>
          </page2_3>

      </pages>
  </page2>

  <page3>
      <label>Page 3</label>
      <uri>page3</uri>
      <pages>

          <page3_1>
              <label>Page 3.1</label>
              <uri>page3/page3_1</uri>
              <resource>guest</resource>
          </page3_1>

          <page3_2>
              <label>Page 3.2</label>
              <uri>page3/page3_2</uri>
              <resource>member</resource>
              <pages>

                  <page3_2_1>
                      <label>Page 3.2.1</label>
                      <uri>page3/page3_2/page3_2_1</uri>
                  </page3_2_1>

                  <page3_2_2>
                      <label>Page 3.2.2</label>
                      <uri>page3/page3_2/page3_2_2</uri>
                      <resource>admin</resource>
                  </page3_2_2>

              </pages>
          </page3_2>

          <page3_3>
              <label>Page 3.3</label>
              <uri>page3/page3_3</uri>
              <resource>special</resource>
              <pages>

                  <page3_3_1>
                      <label>Page 3.3.1</label>
                      <uri>page3/page3_3/page3_3_1</uri>
                      <visible>0</visible>
                  </page3_3_1>

                  <page3_3_2>
                      <label>Page 3.3.2</label>
                      <uri>page3/page3_3/page3_3_2</uri>
                      <resource>admin</resource>
                  </page3_3_2>

              </pages>
          </page3_3>

      </pages>
  </page3>

  <home>
      <label>Home</label>
      <order>-100</order>
      <module>default</module>
      <controller>index</controller>
      <action>index</action>
  </home>

</nav>
 */

$reader = new Zend\Config\Reader\Xml();
$config = $reader->fromFile('/path/to/navigation.xml');
$container = new Zend\Navigation\Navigation($config);

Adding pages

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// create container
$container = new Zend\Navigation\Navigation();

// add page by giving a page instance
$container->addPage(
    Zend\Navigation\Page\AbstractPage::factory(
        array(
            'uri' => 'http://www.example.com/',
        )
    )
);

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

// add page by giving a config object
$container->addPage(
    new Zend\Config\Config(
        array(
            'uri' => 'http://www.example.com/',
        )
    )
);

$pages = array(
    array(
        'label'  => 'Save',
        'action' => 'save',
    ),
    array(
        '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(). The first method accepts a an instance of a page, or an integer. The integer corresponds to the order a page has. The latter method will remove all pages in the container.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$container = new Zend\Navigation\Navigation(array(
    array(
        'label'  => 'Page 1',
        'action' => 'page1',
    ),
    array(
        'label'  => 'Page 2',
        'action' => 'page2',
        'order'  => 200,
    ),
    array(
        '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

Finding pages

Containers have finder methods for retrieving pages. They are findOneBy($property, $value), findAllBy($property, $value), and findBy($property, $value, $all = false). Those methods will recursively search the container for pages matching the given $page->$property == $value. The first method, findOneBy(), will return a single page matching the property with the given value, or NULL if it cannot be found. The second method will return all pages with a property matching the given value. The third method will call one of the two former methods depending on the $all flag.

The finder methods can also be used magically by appending the property name to findBy, findOneBy, or findAllBy, e.g. findOneByLabel('Home') to return the first matching page with label ‘Home’. Other combinations are findByLabel(...), findOneByTitle(...), findAllByController(...), etc. Finder methods also work on custom properties, such as findByFoo('bar').

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
$container = new Zend\Navigation\Navigation(array(
    array(
        'label' => 'Page 1',
        'uri'   => 'page-1',
        'foo'   => 'bar',
        'pages' => array(
            array(
                'label' => 'Page 1.1',
                'uri'   => 'page-1.1',
                'foo'   => 'bar',
            ),
            array(
                'label' => 'Page 1.2',
                'uri'   => 'page-1.2',
                'class' => 'my-class',
            ),
            array(
                'type'   => 'uri',
                'label'  => 'Page 1.3',
                'uri'    => 'page-1.3',
                'action' => 'about',
            )
        )
    ),
    array(
        'label'      => 'Page 2',
        'id'         => 'page_2_and_3',
        'class'      => 'my-class',
        'module'     => 'page2',
        'controller' => 'index',
        'action'     => 'page1',
    ),
    array(
        '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.
$found = $container->findBy('id',
                            'page_2_and_3');      // returns Page 2
$found = $container->findOneBy('id',
                               'page_2_and_3');   // returns Page 2
$found = $container->findBy('id',
                            'page_2_and_3',
                            true);                // returns Page 2 and Page 3
$found = $container->findById('page_2_and_3');    // returns Page 2
$found = $container->findOneById('page_2_and_3'); // returns Page 2
$found = $container->findAllById('page_2_and_3'); // returns Page 2 and Page 3

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

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

// Find all matching CSS class non-existent
$found = $container->findAllByClass('non-existent'); // returns array()

// Find first matching CSS class non-existent
$found = $container->findOneByClass('non-existent'); // returns null

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

// 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'
$found = $container->findAllByController('index'); // returns Page 2 and Page 3

Iterating containers

Zend\Navigation\AbstractContainer implements RecursiveIterator, and can be iterated using any Iterator class. To iterate a container recursively, use the RecursiveIteratorIterator class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
 * Create a container from an array
 */
$container = new Zend\Navigation\Navigation(array(
    array(
        'label' => 'Page 1',
        'uri'   => '#',
    ),
    array(
        'label' => 'Page 2',
        'uri'   => '#',
        'pages' => array(
            array(
                'label' => 'Page 2.1',
                'uri'   => '#',
            ),
            array(
                'label' => 'Page 2.2',
                'uri'   => '#',
            )
        )
    )
    array(
        '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

The method hasPage(Zend\Navigation\Page\AbstractPage $page) checks if the container has the given page. The method hasPages() checks if there are any pages in the container, and is equivalent to count($container) > 0.

The toArray() method converts the container and the pages in it to an array. This can be useful for serializing and debugging.

Converting a container to an array

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
$container = new Zend\Navigation\Navigation(array(
    array(
        'label' => 'Page 1',
        'uri'   => '#',
    ),
    array(
        'label' => 'Page 2',
        'uri'   => '#',
        'pages' => array(
            array(
                'label' => 'Page 2.1',
                'uri'   => '#',
            ),
            array(
                '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) "#"
  }
}
*/