Affichage des articles dont le libellé est zend framework. Afficher tous les articles
Affichage des articles dont le libellé est zend framework. Afficher tous les articles

dimanche 19 décembre 2010

Zend framework: imaging the menu

Hmm

This is more of a reminder to ::self than a post. Because it took me nearly a full day to figure out how to be able to put just a logo image in the menu with Zend_View_Helper_Navigation_Menu. So here goes.
Note 1: I used ZF 1.11
Note 2: For simplicity, I used "fullmoon" where you should use your application name. Take notice of case within the code, it's important and it changes depending on the part of code you're in.

Step 1: Extend Zend_View_Helper_Navigation_Menu

In the library, create your own menu view helper:
library/Fullmoon/View/Helper/Navigation/FullmoonMenu.php
with the following code:
class Fullmoon_View_Helper_Navigation_FullmoonMenu extends Zend_View_Helper_Navigation_Menu
{
    public function fullmoonMenu(Zend_Navigation_Container $container = null)
    {
        return $this->menu($container);
    }
}


Step 2: Modify the layout view script

This will allow you to make the following call in your layout view script (application/layouts/scripts/layout.phtml):
echo $this->navigation()->fullmoonMenu();


Step 3: Modify the bootstrap

In application/bootstrap.php, add the following:
    /**
     * Menu
     */
    protected function _initNavigation()
    {
        // Load menu data from XML file. First argument is the XML file,
        // second is the XML node containing the menu XML data
        $config = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', 'nav');
        // Retrieve the view object
        $view = $this->getResource('view');
        // Add our own Navigation Menu helper to the view plugins path
        $view->addHelperPath(
            'Fullmoon/View/Helper/Navigation',
         'Fullmoon_View_Helper_Navigation_'
        );
        // Load the menu into the view
        $view->navigation(new Zend_Navigation($config));
    }
Note: You can find examples of XML menu files here.
At this point, your menu should be loading normally, albeit still without an image.

Step 4: Adding the image (aahh, finally!)

Adding the image to the XML file:
<img>
  <class>title</class>
  <src>/img/logo-small.png</src>
  <module>default</module>
  <controller>index</controller>
  <action>index</action>
</img>


Step 5: Amending your menu helper

As a final step, we need to render the image (in library/Fullmoon/View/Helper/Navigation/FullmoonMenu.php) by overriding the protected _renderMenu() method from library/Zend/View/Helper/Navigation/Menu.php:
    /**
     * Renders a normal menu (called from {@link renderMenu()})
     *
     * @param  Zend_Navigation_Container $container   container to render
     * @param  string                    $ulClass     CSS class for first UL
     * @param  string                    $indent      initial indentation
     * @param  int|null                  $minDepth    minimum depth
     * @param  int|null                  $maxDepth    maximum depth
     * @param  bool                      $onlyActive  render only active branch?
     * @return string
     */
    protected function _renderMenu(Zend_Navigation_Container $container,
                                   $ulClass,
                                   $indent,
                                   $minDepth,
                                   $maxDepth,
                                   $onlyActive)
    {
        $html = '';

        // find deepest active
        if ($found = $this->findActive($container, $minDepth, $maxDepth)) {
            $foundPage = $found['page'];
            $foundDepth = $found['depth'];
        } else {
            $foundPage = null;
        }

        // create iterator
        $iterator = new RecursiveIteratorIterator($container,
                            RecursiveIteratorIterator::SELF_FIRST);
        if (is_int($maxDepth)) {
            $iterator->setMaxDepth($maxDepth);
        }

        // iterate container
        $prevDepth = -1;
        foreach ($iterator as $page) {
            $depth = $iterator->getDepth();
            $isActive = $page->isActive(true);
            if ($depth < $minDepth || !$this->accept($page)) {
                // page is below minDepth or not accepted by acl/visibilty
                continue;
            } else if ($onlyActive && !$isActive) {
                // page is not active itself, but might be in the active branch
                $accept = false;
                if ($foundPage) {
                    if ($foundPage->hasPage($page)) {
                        // accept if page is a direct child of the active page
                        $accept = true;
                    } else if ($foundPage->getParent()->hasPage($page)) {
                        // page is a sibling of the active page...
                        if (!$foundPage->hasPages() ||
                            is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
                            // accept if active page has no children, or the
                            // children are too deep to be rendered
                            $accept = true;
                        }
                    }
                }

                if (!$accept) {
                    continue;
                }
            }

            // make sure indentation is correct
            $depth -= $minDepth;
            $myIndent = $indent . str_repeat('        ', $depth);

            if ($depth > $prevDepth) {
                // start new ul tag
                if ($ulClass && $depth ==  0) {
                    $ulClass = ' class="' . $ulClass . '"';
                } else {
                    $ulClass = '';
                }
                $html .= $myIndent . '<ul' . $ulClass . '>' . self::EOL;
            } else if ($prevDepth > $depth) {
                // close li/ul tags until we're at current depth
                for ($i = $prevDepth; $i > $depth; $i--) {
                    $ind = $indent . str_repeat('        ', $i);
                    $html .= $ind . '    </li>' . self::EOL;
                    $html .= $ind . '</ul>' . self::EOL;
                }
                // close previous li tag
                $html .= $myIndent . '    </li>' . self::EOL;
            } else {
                // close previous li tag
                $html .= $myIndent . '    </li>' . self::EOL;
            }
            // Manage menu image
            if ($page->src != '') {
                $html .= $myIndent . '    <li class="title">' . self::EOL
                      . '        <img src="' . $page->src . '" />' . self::EOL;
            } else {
                // render li tag and page
                $liClass = $isActive ? ' class="active"' : '';
                $html .= $myIndent . '    <li' . $liClass . '>' . self::EOL
                       . $myIndent . '        ' . $this->htmlify($page) . self::EOL;
            }
            // store as previous depth for next iteration
            $prevDepth = $depth;
        }

        if ($html) {
            // done iterating container; close open ul/li tags
            for ($i = $prevDepth+1; $i > 0; $i--) {
                $myIndent = $indent . str_repeat('        ', $i-1);
                $html .= $myIndent . '    </li>' . self::EOL
                       . $myIndent . '</ul>' . self::EOL;
            }
            $html = rtrim($html, self::EOL);
        }

        return $html;
    }
This part is what does the trick:
            // Manage menu image
            if ($page->src != '') {
                $html .= $myIndent . '    <li class="title">' . self::EOL
                      . '        <img src="' . $page->src . '" />' . self::EOL;
            } else {
                // render li tag and page
                $liClass = $isActive ? ' class="active"' : '';
                $html .= $myIndent . '    <li' . $liClass . '>' . self::EOL
                       . $myIndent . '        ' . $this->htmlify($page) . self::EOL;
            }


Conclusion

I had a hard time gathering the necessary information over the Internet, hopefully this may help out those wanting to add a logo to the menu with Zend Framework.
Note that the logo does not have link attached to it, but with a little tweaking of the last code snippet that shouldn't be too difficult ;)

There may be a far simpler solution to this but I don't know of it nor have I found it on the Internet.