Creating a routing-based menu in symfony 1.2, part 4: the full package

April 6th, 2009 | by David |

During the last 3 articles I showed you how to define the menu structure, generate sfMenuItem objects and create a builder that does all this for you. Today I want to present you the full package of my sfMenuBuilder and describe all the additional options I included.
Owing to a comment by Hugo HAMON, I decided to remove all external dependencies, so now the routing and i18n have to be injected, making i18n optional.

Download the package

For trying the sfMenuBuilder conveniently I have prepared a zip package containing all the classes and some examples (yes, that’s the download link). Feel free to use it any way you like to!

The package contains the following files:

config/
  example_app.yml
lib/
  menu_builder/
    sfMenuBuilder.class.php
    sfMenuItem.class.php
  resolver/
    sfObjectResolver.class.php
    sfObjectResolverInterface.class.php
examples.php
LICENSE

config/example_app.yml contains the markup and comprehensive documentation of a menu item in YAML – of course the parameters have to be the same wherever your menu structure is generated. In examples.php I placed some code showing how to create an sfMenuBuilder instance and how to render the menu either in tab levels or as a tree.

New options in sfMenuBuilder

sfMenuBuilder now works as a multi-instance holder for sfMenuBuilder objects. It’s like a singleton with multiple named singletons, allowing you to create several menu definitions in parallel and at the same time holding them in a static container – this is especially important for helper functions where you would have to manually store the menus somewhere else.

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
class sfMenuBuilder {
  protected static 
    $instances    = array();
 
  protected
    $menuItems    = array(),
    $route        = null,
    $routeName    = null,
    $routeObject  = null,
    $i18n         = null;
 
  /**
   * Initialize an instance of sfMenuBuilder with the given data and name.
   * If there is an existing instance with the same name it will be overwritten.
   * 
   * Since sfMenuBuilder needs the current sfContext to access the routing and 
   * request objects the context has to be provided as parameter. 
   * 
   * @param array $itemsData    Array of data to create sfMenuItem objects
   * @param sfRoute $route      The current sfRoute
   * @param string $routeName   The name of the current sfRoute
   * @param string $name        The name of the instance, optional, defaults to "default"
   * @param sfI18n $i18n        An instance of sfI18n to perform translations using __()
   * 
   * @return sfMenuBuilder      The newly created instance
   */
  public static function initInstance(array $itemsData, sfRoute $route, $routeName, $name = 'default', sfI18n $i18n = null) {
    self::$instances[$name] = new sfMenuBuilder($itemsData, $route, $routeName, $i18n);
    return self::$instances[$name];
  }
 
  /**
   * Get the instance for the given name parameter.
   * 
   * Warning: this does not create instances automatically! If you 
   * try to get an instance that doesn't exist, null will be returned!
   * 
   * @param string $name    The name of the instance to fetch
   * 
   * @return sfMenuBuilder  An sfMenuBuilder instance for the given name.
   */
  public static function getInstance($name = 'default') {
    if (isset(self::$instances[$name])) {
      return self::$instances[$name];
    }
    return null;
  }
 
  /**
   * Set to protected to prevent access from outside the singleton instance holder.
   * 
   * @param array $itemsData    Array of data to create sfMenuItem objects
   * @param sfRoute $route      The current sfRoute
   * @param string $routeName   The name of the current sfRoute
   * @param sfI18n $i18n        An instance of sfI18n to perform translations using __()
   */
  protected function __construct(array &$itemsData, sfRoute $route, $routeName, sfI18n $i18n = null) {
    $this->routeName = $routeName;
    $this->route = $route;
    $this->i18n = $i18n;
 
    if ($this->route instanceof sfObjectRoute) {
      $options = $this->route->getOptions();
      if ($options['type'] == 'object') {
        $this->routeObject = $this->route->getObject();
      }
    }
 
    foreach ($itemsData as $routeName => $itemData) {
      $this->menuItems[$routeName] = new sfMenuItem($routeName, $itemData);
      $this->menuItems[$routeName]->initialize($this->routeName, $this->route, $this->routeObject, $this->i18n);
    }
  }
 
  /**
   * Render all items of a given level of the menu.
   * 
   * By default, only items inside the current route branch will be rendered.
   * To render all items regardless of their branch set the $forceAll 
   * parameter to true.
   * 
   * @param int $level          Level to render. 0 = top level, 1 = second level and so on
   * @param boolean $forceAll   Set to true to render all items even if their part of the tree is inactive
   * 
   * @return array              Array of items' XHTML code grouped by position
   */
  public function renderLevel($level = 0, $forceAll = false) {
    $rendered = array();
    foreach ($this->menuItems as $item) {
      $item->renderLevel($level, $rendered, $forceAll);
    }
    return $rendered;
  }
 
  /**
   * Render all menu items into a tree structure.
   * 
   * @param string $container   The XHTML container tag to use. Defaults to 'ul'.
   * 
   * @return string             Rendered items and child items, wrapped in container tag
   */
  public function renderTree($container = 'ul') {
    $rendered = '';
    foreach ($this->menuItems as $item) {
      $rendered .= $item->renderTree($container);
    }
    return content_tag($container, $rendered);
  }
}

As you can see now the sfRoute, its name and the sfI18n instance have to be injected into the initInstance() method. The constructor cannot be called from outside the class, so you have to use initInstance() and getInstance() in combination with $name (which defaults to “default”) to access your builder instances.

sfMenuItem revisited

The constructor then creates all the sfMenuItem objects and injects the routing and i18n information. Let’s take a look at the full sfMenuItem class next:

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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
class sfMenuItem {
  protected
    $myRouteName        = null,
    $children           = array(),
    $allRouteNames      = array(),
    $title              = '',
    $position           = 'default',
    $container          = 'li',
    $requireObjectClass = null,
    $customFunction     = null,
    $customUrl          = null,
    $route              = null,
    $routeName          = null,
    $routeObject        = null,
    $i18n               = null;
 
  /**
   * Generate a new sfMenuItem object using its own route name and the node
   * data from the structural schema.
   * 
   * @param string $myRouteName   This item's sfRoute name (as in routing.yml)
   * @param array $itemData       The data definition for this item
   */
  public function __construct($myRouteName, array $itemData) {
    $this->myRouteName = $myRouteName;
    $this->allRouteNames[] = $myRouteName;
 
    if (isset($itemData['title'])) {
      $this->title = $itemData['title'];
    }
    if (isset($itemData['position'])) {
      $this->position = $itemData['position'];
    }
    if (isset($itemData['container'])) {
      $this->container = $itemData['container'];
    }
    if (isset($itemData['alias_routes'])) {
      $this->allRouteNames = array_merge($this->allRouteNames, (array) $itemData['alias_routes']);
    }
    if (isset($itemData['require_object_class'])) {
      $this->requireObjectClass = $itemData['require_object_class'];
    }
    if (isset($itemData['custom_function'])) {
      $this->customFunction = new sfCallable($itemData['custom_function']);
    }
    if (isset($itemData['custom_url'])) {
      $this->customUrl = $itemData['custom_url'];
    }
 
    if (isset($itemData['children']) && is_array($itemData['children'])) {
      foreach ($itemData['children'] as $subRouteName => $subItemData) {
        $this->children[$subRouteName] = new sfMenuItem($subRouteName, $subItemData);
        $this->allRouteNames = array_merge($this->allRouteNames, $this->children[$subRouteName]->getAllRouteNames());
      }
    }
  }
 
  /**
   * Initialize the menu item and all child menu items using the current routing 
   * status of the application.
   * By passing the routing information you can either use routes from outside
   * the context (e.g. testing data) or just improve performance because this
   * would have to be repeated for all the child menu items. 
   * 
   * @param string $routeName   Name of the currently active route
   * @param sfRoute $route      Currently active route object
   * @param mixed $routeObject  Object that is transported through the route if it is an sfObjectRoute
   * @param sfI18n $i18n        An instance of sfI18n to perform translations using __()
   */
  public function initialize($routeName, sfRoute $route, $routeObject, sfI18n $i18n = null) {
    $this->routeName = $routeName;
    $this->route = $route;
    $this->routeObject = $routeObject;
    $this->i18n = $i18n;
 
    foreach ($this->children as $child) {
      $child->initialize($this->routeName, $this->route, $this->routeObject, $this->i18n);
    }
  }
 
  /**
   * Get this menu item's route name.
   * 
   * @return string   Route name of this single menu item
   */
  public function getMyRouteName() {
    return $this->myRouteName;
  }
 
  /**
   * Get all child sfMenuItem objects.
   * 
   * @return array    Array of sfMenuItem objects, using the route names as keys.
   */
  public function getChildren() {
    return $this->children;
  }
 
  /**
   * Get an array of all route names and alias route names this menu item
   * might use. Also includes all sub items' routes recursively.
   * 
   * @return array    Array of route names
   */
  public function getAllRouteNames() {
    return $this->allRouteNames;
  }
 
  /**
   * Get the title that will be used to render this item.
   * 
   * @return string   Title text
   */
  public function getTitle() {
    return $this->title;
  }
 
  /**
   * Get the position this item will be rendered at.
   * 
   * @return string   Position. Defaults to "default"
   */
  public function getPosition() {
    return $this->position;
  }
 
  /**
   * Get this menu item's wrapper container.
   * 
   * @return string   HTML wrapper Container name
   */
  public function getContainer() {
    return $this->container;
  }
 
  /**
   * Get the object class that is required for this item to 
   * be rendered as "enabled".
   * 
   * @return string   Object class name 
   */
  public function getRequireObjectClass() {
    return $this->requireObjectClass;
  }
 
  /**
   * Get this menu item's custom URL.
   * 
   * @return string   Custom URL
   */
  public function getCustomUrl() {
    return $this->customUrl;
  }
 
  /**
   * If an i18n instance was provided, this will do the translation.
   * @see sfI18n::__()
   * 
   * @param string $string  The string to translate
   * 
   * @return string         Translated string
   */
  public function __($string) {
    if (is_null($this->i18n)) {
      return $string;
    }
    return $this->i18n->__($string);
  }
 
  /**
   * Render the current menu item's content text.
   * 
   * The content always contains an 'a' tag containing either a href
   * link (if the item is enabled) or the item title only.
   * 
   * All titles are translated using i18n.
   * 
   * @return string   Rendered menu item content HTML code
   */
  public function renderContent() {
    if ($this->customFunction instanceof sfCallable) {
      return $this->customFunction->call($this);
    }
 
    if (!$this->isEnabled()) {
      return content_tag('a', $this->__($this->title));
    } else {
      if (!is_null($this->customUrl)) {
        return link_to($this->__($this->title), $this->customUrl);
      } elseif (is_null($this->requireObjectClass)) {
        return link_to($this->__($this->title), '@'.$this->myRouteName);
      } else {
        return link_to($this->__($this->title), $this->myRouteName, sfObjectResolver::resolve($this->routeObject, $this->requireObjectClass));
      }
    }
  }
 
  /**
   * Check if the item is enabled or not.
   * An item will be disabled if it requires an object class
   * that cannot be found by the resolver.
   * 
   * @return boolean  True if the item is enabled
   */
  public function isEnabled() {
    return is_null($this->requireObjectClass) || !is_null(sfObjectResolver::resolve($this->routeObject, $this->requireObjectClass));
  }
 
  /**
   * Check if the item is currently selected.
   * An item will be selected if the current routeName matches
   * any route in the items allRouteNames property.
   * 
   * @return boolean  True if the item is currently selected in the menu
   */
  public function isCurrent() {
    return in_array($this->routeName, $this->allRouteNames);
  }
 
  /**
   * Wrap an item with the container that is set. If no container
   * is defined, just return the item.
   * 
   * @param string $text    Text content
   * @param array $options  Options array passed to content_tag() helper
   * 
   * @return string         Rendered content tag
   */
  protected function containerTag($text, $options = array()) {
    if ($this->container != '') {
      return content_tag($this->container, $text, $options);
    }
    return $text;
  }
 
  /**
   * Render the current menu item.
   * 
   * The item is rendered inside the element given as "container" parameter.
   * The container defaults to the HTML list element ("li").
   * 
   * Items that require an object route without existing object of that type
   * have their class attribute set to "disabled". Items that match the current
   * route have their class attribute set to "current".
   * 
   * @return string   Rendered menu item HTML code
   */
  public function render() {
    $text = $this->renderContent();
    if (!$this->isEnabled()) {
      return $this->containerTag($text, array('class' => 'disabled'));
    } elseif ($this->isCurrent()) {
      return $this->containerTag($text, array('class' => 'current'));
    }
    return $this->containerTag($text);
  }
 
  /**
   * Get the sfRoute object associated with all the menu items.
   * 
   * @return sfRoute  sfRoute object
   */
  public function getRoute() {
    return $this->route;
  }
 
  /**
   * Get the route name associated with all the menu items.
   * 
   * @return string   Route name
   */
  public function getRouteName() {
    return $this->routeName;
  }
 
  /**
   * Get the route object out of sfObjectRoute.
   * 
   * @return mixed    Route object generated by sfObjectRoute
   */
  public function getRouteObject() {
    return $this->routeObject;
  }
 
  /**
   * Render all menu items of a given child level related to this item.
   * 
   * The rendered items are added to the $rendered array parameter grouped 
   * by their position.
   * 
   * @param int $level          Level to render. 0 will return the current item, 1 all items of the first child level and so on.
   * @param array $rendered     Contains all items rendered so far. Rendered items will be appended to the array.
   * @param boolean $forceAll   Force the rendering of all sub items, even if their tree is not in the current route
   */
  public function renderLevel($level, array &$rendered, $forceAll = false) {
    if ($level == 0) {
      $rendered[$this->position][] = $this->render();
    } elseif ($level > 0 && ($forceAll || in_array($this->routeName, $this->allRouteNames))) {
      $level--;
      foreach ($this->children as $child) {
        $child->renderLevel($level, $rendered, $forceAll);
      }
    }
  }
 
  /**
   * Render all menu items into a tree structure.
   * 
   * @param string $container   The HTML container tag to use. Defaults to 'ul'.
   * 
   * @return string             Rendered item and child items, wrapped in container tag
   */
  public function renderTree($container = 'ul') {
    $children = '';
    foreach ($this->children as $child) {
      $children .= $child->renderTree($container);
    }
 
    $text = $this->renderContent();
    if ($children != '') {
      $text .= content_tag($container, $children);
    }
    if (!$this->isEnabled()) {
      return $this->containerTag($text, array('class' => 'disabled'));
    } elseif ($this->isCurrent()) {
      return $this->containerTag($text, array('class' => 'current'));
    }
    return $this->containerTag($text);
  }
}

All the configuration options

Now you can see the full parameter set of sfMenuItem – but what do all those values actually mean? I think looking at the commented parameter definitions in my sample yaml file will make it more understandable:

all:
  menu:
    items:
 
      # Each item must use its route name as in routing.yml as its index.
      # If you provide the custom_url parameter, the route name will be
      # ignored, so you may provide a name that does not exist.
      first_item_route_name:
 
        # The title is the text that is displayed for the item.
        # This text will automatically be translated if an i18n instance is passed
        # to the sfMenuItem's initialize() method.
        title:                My first item title
 
        # Alias routes are route names that should set the same menu item as current.
        # If your applications uses different routes for the same page (e.g. "update" 
        # routes in form actions) you must provide them here, otherwise the menu
        # will not display correctly.
        alias_routes:         [first_item_update_route_name, ...]
 
        # Using the position option items can be grouped into separate arrays.
        # This is used for the rendering of tab levels, where the level contains
        # an array with the positions as indexes.
        # Typical applications might use options like "left" and "right" to separate
        # the tabs of a level into different positions. If no position is provided it 
        # defaults to 'default'.
        position:             default
 
        # This is the HTML container to use for rendering the item's content. If no container
        # is provided, it defaults to 'li'. You may also provide an empty container (''), 
        # which will make the item content return without an outer tag. Beware though, that
        # without this tag the options "disabled" and "current" have no element to be applied to.
        container:            li
        container:            ''
 
        # This option indicates that this item requires an object of a specific type.
        # The sfMenuItem will try to get this object through the sfObjectRoute
        # and (if the route object is different) through sfObjectResolver.
        # If no object of this type is found the item will be set to "disabled".
        require_object_class: SomeClass
 
        # If you provide a custom function, it will be called instead of rendering the
        # item's content using the title. You can provide any callable, either a globally
        # accessible function name or an array holding a class name and a static method name.
        # The sfMenuItem object is passed to the function, so the item's public methods can 
        # be used to access nearly all the parameters and settings.
        # 
        # The function footprint should be something like this:
        # 
        # function some_function_name(sfMenuItem $item) {
        #   // do custom rendering here
        #   return $itemContent;
        # }
        custom_function:      some_function_name
        custom_function:      [SomeClass, someMethod]
 
        # Providing a custom url you can generate menu items that link somewhere else.
        # It will be used in an url_for() call, so everything that works in there will be fine.
        custom_url:           http://www.symfony-project.org/my_custom_url
 
        # In the children option you can provide child items of the given menu item.
        # Each child item may have children itself, recursively. Of course all the options
        # as for this item may be defined for any child item.
        children:
          child1_route_name:
            child1_title:       Some title
 
          child2_route_name:
            child2_title:       Some other title
 
            children:
              # I think you get it...

Any questions?

I think those comments should explain the options quite well. Since I can’t think of another example at the moment and also may not publish what the menu I use at work actually looks like I can only ask for user feedback if you don’t understand something or just can’t see its usage. If I see that something is still unclear I will try to make it more specific. I can only tell you that at the moment every single option except custom_url is in use in my application and I’m glad for having all of them (and I may need custom_url in the future myself).

Dynamic menus

As promised in part 3, I want to think about how sfMenuBuilder could be used dynamically. The first thing that comes to mind is having a tree-like database structure (if you need nested menus), using nestedSet features provided (AFAIK) both by Propel and Doctrine. The basic schema for a dynamic menu could look like this:

propel:
  menu_item:
    id:
    route_name:           { type: varchar(255), required: true }
    title:                { type: varchar(255), required: true }
    alias_routes:         { type: varchar(255), required: false }
    position:             { type: varchar(10), required: false, default: 'default' }
    container:            { type: varchar(10), required: false, default: 'li' }
    require_object_class: { type: varchar(255), required: false }
    custom_function:      { type: varchar(255), required: false }
    custom_url:           { type: varchar(255), required: false }

Just add the code for transforming these database objects into the arrays needed for sfMenuItem and you’re done!

You could also extend statically defined menu arrays by merging with dynamically created arrays. Due to the simple array syntax nearly anything can be used to describe a menu.

That’s it!

Finally we have come to an end and the sfMenuBuilder is ready to use. I hope my articles were not too bulky and rather helpful for you guys out there. When designing something as large as this and writing about it, I always want my readers to understand what goes through my mind and how I arrived at my destination. In the end, my articles shouldn’t be about the final code but more about designing things and working them out.

What started as a proof of concept for sfObjectResolver has in the end become a fully usable little library that will hopefully evolve and be used in various applications, and at the same time teach people about OOp and clean design. I know, I always exaggerate, but that’s just me…

Have fun!

  1. 19 Responses to “Creating a routing-based menu in symfony 1.2, part 4: the full package”

  2. By lloyd27 on Apr 6, 2009 | Reply

    Have you ever thought of releasing it as a plugin?

  3. By David on Apr 7, 2009 | Reply

    Actually I have, but 3 reasons have held me back so far:

    • I don’t know yet if anyone else really wants to use this or to what extend
    • I think at the moment it’s too small to be a stand-alone plugin, I see it more as a big snippet or advanced helper
    • Creating a plugin means more work ;-)

    I think the place for sfMenuBuilder might be somewhere inside one of the CMS plugins, although I haven’t had a look what means of navigation generation they already provide. Since symfony’s 1.2 routing system is still quite young it will take some time until the new technique finds its way into more projects.

  4. By Maksym on May 12, 2009 | Reply

    I am just a beginner in Symfony, and I do not understand something here – for example, how to call examples.php? I’ve put everything according to structure in ZIP, but for now I do not know how to call examples.php.

    Another thing – am I correct to put all the structure inside the /apps/frontend/ folder?

  5. By admin on May 14, 2009 | Reply

    The structure is intended for the lib/ folder, the example.php file states how the code could be called – I’m doing this actually inside a helper (MenuHelper.php) in my app’s lib/helper folder.

  6. By Hugo on Jun 17, 2009 | Reply

    Hello,

    in Example.php I need make 2 updates, for my use.

    The error sfBuilder not founded!!

    I change sfBuilder for sfMenuBuilder and the finishing…

    Thanks :)

    I Install

    Project
    apps
    config
    example_apps.yml (change name for app.yml)
    cache
    config
    data
    doc
    lib
    menu_builder
    resolver
    log
    plugins
    test
    web

    And the code of example.php I use in my codes for make the layouts and templates.

  7. By David on Jun 20, 2009 | Reply

    Thanks for pointing this out, I will fix it in the package!

  8. By Łukasz Wojciechowski on Jun 22, 2009 | Reply

    Hi

    I like Your articles very much and I decided to use Your solution to implement my 3levels menu.

    However I run into a problem.

    Lets assume menu defined that way.

    menu:
    items:
    homepage:
    title: home page
    children:
    about_us:
    title: about us
    children:
    peoples:
    title: peoples

    Now I want to display only level 0 menu (home page link) in my top horizontal navigation (that is easy) and level1 and level2 menu (about us and nested peoples) in my left side vertical navigation.

    How can I accomplish that ?

    thanks in advance


    best regards
    Łukasz Wojciechowski

  9. By kevin on Jul 24, 2009 | Reply

    This is GREAT stuff,
    but i’m also newbie to symfony and could not get it to work just yet… because i don’t where to put the files!

    “The structure is intended for the lib/ folder, the example.php file states how the code could be called – I’m doing this actually inside a helper (MenuHelper.php) in my app’s lib/helper folder.”
    -> What’s in MenuHelper.php exactly? confused there…

    Anyway, imho this should definitely be a plugin for the community to use! :) so useful!

  10. By kevin on Jul 24, 2009 | Reply

    Got it to work… more or less!
    So for the REAL newbies like me :

    mkdir -p apps/backend/lib/helper/
    then move the content of “lib” from the archive to this directory.

    Config files :
    — apps/backend/config/app.yml
    all:
    menu:
    items:
    submissions:
    title: Submissions
    reports:
    title: Reports
    children:
    reports:
    title: Default report
    reports_summary:
    title: Summary report
    — apps/backend/config/routing.yml
    # default rules
    (…)

    submissions:
    class: sfDoctrineRouteCollection
    options:
    model: Submissions
    module: submissions
    prefix_path: submissions
    column: id
    with_wildcard_routes: true

    reports:
    url: /reports
    param: { module: reports, action: index }
    reports_summary:
    url: /reports/summary
    param: { module: reports, action: summary }

    Added a dummy function in myUser so i can get the menu anytime from anywhere in the app
    — apps/backend/lib/myUser.class.php
    getRequest()->getAttribute(‘sf_route’);

    $routeName = sfContext::getInstance()->getRouting()->getCurrentRouteName();
    $builder = sfMenuBuilder::initInstance(sfConfig::get(‘app_menu_items’), $route, $routeName, ‘default’);
    return $builder->renderTree();
    }
    }

    Because i want this menu to be “global”
    — apps/backend/templates/_menu.php
    getMenu(); ?>

    — apps/backend/templates/layout.php
    (…)

    (…)

    et voila! Well almost, i have an issue : the html is escaped so the menu is still unusable, any idea why? :|

  11. By kevin on Jul 24, 2009 | Reply

    Sorry for the spam,
    simple solution : forget about the myUser bit, and sick the content of the getMenu() function directly into the partial:

    getRequest()->getAttribute(‘sf_route’);

    $routeName = sfContext::getInstance()->getRouting()->getCurrentRouteName();
    $builder = sfMenuBuilder::initInstance(sfConfig::get(‘app_menu_items’), $route, $routeName, ‘default’);
    echo $builder->renderTree(); // echo $sf_user->getMenu();

    what’s with the myUser escape the html tags?

  12. By Sean on Jul 30, 2009 | Reply

    Hey, the sfMenuBuilder is still referred to as sfBuilder.

    Fatal error: Class ‘sfBuilder’ not found in …/apps/frontend/modules/main/actions/menuComponent.class.php on line 12

  13. By Tin Nguyen on May 12, 2010 | Reply

    I have just look through your post about routing-based menu in symfony. May I ask you if your way support internationization. I mean menu’s language could be changed with user. thanks by advanced.

  14. By Christian Ducrot on Mar 7, 2011 | Reply

    Great article. It’s the only solution which supports ‘alias_routes’.
    Any plans for making it symfony 1.4 ready?

    For example there is an exception when you click on “new” in an admin generated form:

    Unable to find the sfGuardUserPeer object with the following parameters “array ( ‘sf_format’ => ‘html’,)”).

    stack trace:
    1. at ()
    in SF_ROOT_DIR/lib/vendor/symfony/lib/routing/sfObjectRoute.class.php line 111
    2. t sfObjectRoute->getObject()
    in SF_ROOT_DIR/apps/backend/modules/helper/lib/menu_builder/sfMenuBuilder.class.php line 77

    I think the problem is that there is no object in the route at the time this form is generated.

  15. By David on Mar 7, 2011 | Reply

    I’ve been heavily modifying the menu system since it has been released here. It is now an sf1.4 compatible standalone plugin and even supports multiple menus in separate definition files (menu.yml), breadcrumb rendering, custom item classes and much more. I can’t afford the time to re-publish it as a package, but I think I will upload the code to my github. Just give me something like 2 weeks and it will be public.

  16. By Christian Ducrot on Apr 5, 2011 | Reply

    I’m looking forward to the github version of the menu system ;-)

  17. By Gayatri on Apr 20, 2011 | Reply

    What’s the name you’re planning for the plugin?
    Have you made it public yet? Can you give the github link?

  18. By Gayatri on Apr 20, 2011 | Reply

    Will this work for cross application links in the menu? I suppose (from the code you’ve posted), two instances of sfMenuBuilder will have to be created …?

  19. By digital audio 2000 speakers on Apr 23, 2013 | Reply

    I was curious if you ever thought of changing the layout of your site?
    Its very well written; I love what youve got to say.
    But maybe you could a little more in the way of content so people
    could connect with it better. Youve got an awful
    lot of text for only having one or 2 pictures. Maybe you could space
    it out better?

  20. By More information on Apr 25, 2013 | Reply

    A Japanese study examined nine patients who were not
    their partners. Men in the ads also look much younger than Pfizer’s earlier farmacia on line pitchman, former Kansas Sen. Many collectors in Dolpa returned without any,” said Shrestha, referring to the popular anti-impotence treatment.

Post a Comment