Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "navigation"

CSS link class adder

<?php
 
/**
 * Automatically adds a class name to a link (or a parent tag) if the link route matches
 * the current module or route.
 * (based on the work made by scott meves on 2007-02-24 : http://snippets.symfony-project.org/snippet/153)
 *
 * @author  ESCANDELL Stéphane <stephane.escandell[a]gmail.com>
 * @date    2010-07-16
 *
 *
 * @param  <name> : string name of the link, i.e. string to appear between the <a> tags
 * @param  <internal_uri> string 'module/action' or '@rule' of the action
 * @param  <options> array additional style options
 * @return string XHTML compliant <a href> tag (encapsulated by an optionnal tag)
 * @see    link_to
 * 
 *
 *
 * <b>Available Options:</b>
 *    -> All classic options from link_to() method
 *
 *    -> 'moduleOnly'
 *      recognized values:
 *        -> true
 *        -> false
 *      If set to <true>, the link is identify as matched only if the module of the route associated to <internal_uri>
 *      is matching to the current module.
 *      If not set, the default value is false
 *
 *    -> 'currentClass'
 *      recognized values : 
 *        -> string 
 *      The class identifier added to the link (or the parent tag, see below) if <internal_uri> is matching to
 *      the current page.
 *      If not set, the default class added is 'active'.
 *
 *    -> 'separator'
 *      recognized values:
 *        -> string
 *      Used only if the <class> option is set. If the link match, this separator will be used between the <class>
 *      option and <currentClass> option (the generated class will be <class>.<separator>.<currentClass>).
 *      If not set, the default value is a space caracter ' '.
 *
 *    ->  'tag'
 *      recognized values:
 *        -> html tag name
 *      Identify an html tag to encapsulate the link. If this option appears, the <currentClass> name will be added
 *      to this tag rather than the <a> tag.
 *
 *    -> 'tagOptions'
 *      recognized values:
 *        -> array of html parameters
 *      Used only if the parameter <tag> exists. <tagOptions> is used to generate the tag to encapsulate the link with
 *      user specific options.
 *
 * <b>Examples:</b>
 * <code>
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home')); ?>
 *   if the current route is '@homepage', then:
 *     => <a href="path/to/homepage/action" class="active" title="Home">home</a>
 * 
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home', 'currentClass' => 'myActiveClass')); ?>
 *   if the current route is '@homepage', then:
 *     => <a href="path/to/homepage/action" class="myActiveClass" title="Home">home</a>
 *
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home', 'separator' => '_')); ?>
 *   if the current route is '@homepage', then:
 *     => <a href="path/to/homepage/action" class="active" title="Home">home</a>
 *
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home', 'class' => 'myClass tab', 'separator' => '_')); ?>
 *   if the current route is '@homepage', then:
 *     => <a href="path/to/homepage/action" class="myClass tab_active" title="Home">home</a>
 *
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home', 'currentClass' => 'myActiveClass',
 *                                 'tag' => 'li')); ?>
 *   if the current route is '@homepage', then:
 *     => <li class="myActiveClass"><a href="path/to/homepage/action" title="Home">home</a></li>
 *
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home',
 *                                 'tag' => 'li',
 *                                  'tagOptions' => array('class' => 'anotherClass',
 *                                                        'width' => '250px')
 *                                )); ?>
 *   if the current route is '@homepage', then:
 *     => <li class="anotherClass active" width="250px"><a href="path/to/homepage/action" title="Home">home</a></li>
 *
 *   <?php echo link_to_styled( 'home',
 *                           '@homepage',
 *                           array('title' => 'Home',
 *                                 'currentClass' => myActiveClass,
 *                                 'tag' => 'li',
 *                                  'tagOptions' => array('class' => 'anotherClass other',
 *                                                        'width' => '250px')
 *                                )); ?>
 *   if the current route is '@homepage', then:
 *     => <li class="anotherClass other myActiveClass" width="250px"><a href="path/to/homepage/action" title="Home">home</a></li>
 * </code>
 *
 *
 * Changelog :
 *    2010/07/16 :
 *      Author : ESCANDELL Stéphane <stephane.escandell[a]gmail.com>
 *      Modification :
 *        First version (based on the work made by scott meves on 2007-02-24 : http://snippets.symfony-project.org/snippet/153)
 */
function link_to_styled($name, $internal_uri, $options = array())
{
  static $context = null;
  static $currentModuleName = null;
  static $applicationConfiguration = null;
 
  if(is_null($context))
  {
    $context = sfContext::getInstance();
  }
  if(is_null($currentModuleName))
  {
    $currentModuleName = $context->getModuleName();
  }
  if(is_null($applicationConfiguration))
  {
    $applicationConfiguration = $context->getConfiguration();
  }
 
  $options    = _parse_attributes($options);
  $separator  = isset($options['separator']) ? $options['separator'] : ' ';
  $tag        = isset($options['tag']) ? $options['tag'] : null;
  $tagOptions = isset($options['tagOptions']) ? $options['tagOptions'] : array();
 
  $isCurrent = false;
  // In order to findRoute() works, we need to delete the reference to
  // the file controller used
  //
  $route = $context->getRouting()->findRoute( str_replace('/'.$applicationConfiguration->getApplication().'_'.$applicationConfiguration->getEnvironment().'.php',
                                                      '',
                                                      url_for($internal_uri,false)
                                                      ));
  if(isset($options['moduleOnly']) && true===$options['moduleOnly'])
  {
    $isCurrent = isset($route['parameters']) ? (isset($route['parameters']['module']) ? $route['parameters']['module']==$currentModuleName : false) : false;
  }
  else
  {
    $isCurrent = url_for($internal_uri,false) == url_for($context->getRouting()->getCurrentInternalUri(),false);
  }
 
  if($isCurrent)
  {
    if(is_null($tag))
    {
      if(!isset($options['class']))
        $options['class'] = isset($options['currentClass']) ? $options['currentClass'] : 'active';
      else
        $options['class'] .= isset($options['currentClass']) ? $separator.$options['currentClass'] : $separator.'active';
    }
    else
    {
      if(!isset($tagOptions['class']))
        $tagOptions['class']  = isset($options['currentClass']) ? $options['currentClass'] : 'active';
      else
        $tagOptions['class'] .= isset($options['currentClass']) ? $separator.$options['currentClass'] : $separator.'active';
    }
  }
 
 
  unset($options['moduleOnly'],$options['currentClass'],$options['separator'],$options['tag'],$options['tagOptions']);
 
  if(is_null($tag))
    return link_to($name, $internal_uri, $options);
  else
    return content_tag($tag, link_to($name, $internal_uri, $options), $tagOptions);
}
 
by Stéphane Escandell on 2010-07-16, tagged css  helper  link  menu  navigation 
(2 comments)

Switch culture from any page of your site and improve SEO

CONTEXT

Symfony internationalization is normally managed using session variables. To switch culture you can visit a page that first changes you session variable and then send you back to the page you were visiting, but this potentially breaks your SEO.

Adding culture in every URL is a better solution, but if you want to go back to the page you were visiting, you have to deal with redirection by yourself.

This widget solves the redirection issue by creating localized urls.

CODE

Save your file here: apps/appname/lib/helper/I18nUrlHelper.php

/*
 * Generate a localized version for the URL you are visiting.
 */
 
function localized_current_url($sf_culture = null)
{
  if (! $sf_culture)
  {
    throw new sfException(sprintf('Invalid parameter $sf_culture "%s".', $sf_culture));
  }
 
  $routing    = sfContext::getInstance()->getRouting();
  $request    = sfContext::getInstance()->getRequest();
  $controller = sfContext::getInstance()->getController();
 
  // depending on your routing configuration, you can set $route_name = $routing->getCurrentRouteName()
  $route_name = '';
 
  $parameters = $controller->convertUrlStringToParameters($routing->getCurrentInternalUri());
  $parameters[1]['sf_culture'] = $sf_culture;
 
  return $routing->generate($route_name, array_merge($request->getGetParameters(), $parameters[1]));
}
 

EXAMPLE

Obviously, you must add the :sf_culture token in every rule of your application routing.yml:

product_page:
  url:   /:sf_culture/product/:id
  requirements: { sf_culture: (?:en|es|fr|it|de) }
  param: { module: product, action: show }
 
homepage:
  url:   /:sf_culture
  requirements: { sf_culture: (?:en|es|fr|it|de) }
  param: { module: page, action: home }

default_symfony:
  url:   /symfony/:sf_culture/:action/*
  requirements: { sf_culture: (?:en|es|fr|it|de) }
  param: { module: default }
 
default_index:
  url:   /:sf_culture/:module
  param: { action: index }
  requirements: { sf_culture: (?:en|es|fr|it|de) }
 
default:
  url:   /:sf_culture/:module/:action/*
  requirements: { sf_culture: (?:en|es|fr|it|de) }
 

This code let you create a list of link to the localized versions. The list of languages/cultures is taken from your config file.

<div>
  <?php foreach(sfConfig::get('app_languages_available') as $language) { ?>
    <a href="<?php echo localized_current_url($language); ?>"><?php echo $language; ?></a>  
  <?php } ?>
</div>
 
by Davide Fedrigo on 2009-09-28, tagged culture  helper  i18n  navigation  routing 
(3 comments)

Nested Set Navigation Component Module (PART 4)

Custom Queries

Full tree

      SELECT n.name, COUNT(*)-1 AS level
      FROM md_components AS n,md_components AS p
      WHERE n.tree_left BETWEEN p.tree_left AND p.tree_left
      GROUP BY n.tree_left
      ORDER BY n.tree_left;
 

Tree with further info (level, children counter etc.):

 SELECT n.*,
 round((n.tree_right-n.tree_left-1)/2,0) AS countChildren,
 count(*)-1+(n.tree_left>1) AS level,
 ((min(p.tree_right)-n.tree_right-(n.tree_left>1))/2) > 0 AS countPredecessors,
 (((n.tree_left-max(p.tree_left)>1))) AS countSuccessors
 FROM md_components n, md_components p
 WHERE n.tree_left BETWEEN p.tree_left AND p.tree_right AND (p.component_id != n.component_id OR n.tree_left = 1)
 GROUP BY n.component_id
 ORDER BY n.tree_left;
 

Get parent nodes up to root node. Used for breadcrumbs.

SELECT p.*
FROM md_components n, md_components p
WHERE n.tree_left BETWEEN p.tree_left AND p.tree_right
AND n.component_id = 18
ORDER BY n.tree_left;
 

Getting the first level items of root 1

SELECT 
    o.*,
    COUNT(p.component_id)-1 AS level
FROM md_components AS n, 
    md_components AS p, 
    md_components AS o 
WHERE o.tree_left BETWEEN p.tree_left AND p.tree_right 
    AND o.tree_left BETWEEN n.tree_left AND n.tree_right
    AND n.component_id = 1 
GROUP BY o.tree_left
HAVING level = 1
ORDER BY o.tree_left
 

user's navigation tree filtered by group permissions using sf_guard plugin:

SELECT
mc.*
FROM
(
    md_components AS mc
    Left Join md_components_has_sf_guard_permission AS cp ON cp.md_components_component_id = mc.component_id
    Left Join sf_guard_permission AS pm ON pm.id = cp.sf_guard_permission_id
    Inner Join sf_guard_group_permission AS pg ON pm.id = pg.permission_id
    Left Join sf_guard_user_group AS ug ON pg.group_id = ug.group_id 
), md_components AS cm
 
WHERE
    mc.tree_left BETWEEN cm.tree_left AND cm.tree_left
AND
    ug.user_id =  1
GROUP BY mc.tree_left
      ORDER BY mc.tree_left;
 

user's navigation tree filtered by group permissions and additional user permissions using sf_guard plugin:

SELECT
mc.*
FROM
(
    md_components AS mc
    Left Join md_components_has_sf_guard_permission AS cp ON cp.md_components_component_id = mc.component_id
    Left Join sf_guard_permission AS pm ON pm.id = cp.sf_guard_permission_id
    Inner Join sf_guard_group_permission AS pg ON pm.id = pg.permission_id
    Left Join sf_guard_user_group AS ug ON pg.group_id = ug.group_id 
    Left Join sf_guard_user_permission AS um ON pm.id = um.permission_id
), md_components AS cm
 
WHERE
    mc.tree_left BETWEEN cm.tree_left AND cm.tree_left
AND
    (ug.user_id =  1 AND um.user_id=1)
GROUP BY mc.tree_left
      ORDER BY mc.tree_left;
 
by Thomas Schäfer on 2008-03-19, tagged model  navigation  nested  set 

Nested Set Navigation Component Module (PART 3)

NestedSet Model for Navigation

model scheme

enhanced nested set model class

enhanced module action class

navigation list tabular cell template

Features:

TEMPLATES

NestedSet Action Controls

Look at module action class for further information.

/apps/backend/modules/navigation/templates/_list_td_tabular.php
 
 
 
 
// delete action for node enabled if it has parent
<?php if($navigation->getParent()>0): ?>
<?php echo link_to(__('delete'), 'navigation/delete?component_id='.$navigation->getComponentId(), array ('post' => true,'confirm' => __('Are you sure?'),));?>
<?php endif; ?>
 
// edit node
<?php echo link_to(__('edit'), 'navigation/edit?component_id='.$navigation->getComponentId());?>
 
 
// tree actions if node level greater zero
<?php if($navigation->hasChilds()>0 && $navigation->isLevel() > 0): ?>
 
// perform tree delete action
<?php echo link_to(__('delete tree'), 'navigation/deleteDescendants?component_id='.$navigation->getComponentId(), array ('post' => true,'confirm' => __('Are you sure?')));?>
<?php if($navigation->hasChilds() > 0):?>
 
// allow tree copy action if node has children
<?php echo link_to(__('copy tree'), 'navigation/copyDescendants?component_id='.$navigation->getComponentId().'&root_id='.$navigation->selfRoot(), array ('post' => true,'confirm' => __('Are you sure?')));?>
<?php endif; ?>
<?php endif; ?>
 
// move operations
<?php if($navigation->isLevel() > 0): ?>
<?php
    // if a node's root has a successor then move to previous root
    if($navigation->nextRoot()){
        echo link_to(__('moveRootPrev'), 'navigation/moveRootPrev?component_id='. $navigation->getComponentId().'&root_id='.$navigation->selfRoot(), array('class' => 'moveRootPrev'));
    }
    // if a node's root has a predecessor then move to next root
    if($navigation->prevRoot()){
        echo link_to(__('moveRootNext'), 'navigation/moveRootNext?component_id='.$navigation->getComponentId().'&root_id='.$navigation->selfRoot(), array('class' => 'moveRootNext'));
    }
?>
<?php endif; ?>
 
// self-explaining
<?php echo link_to(__('insertAsFirstChildOf'), 'navigation/insertAsFirstChildOf?component_id='. $navigation->getComponentId(), array('class' => 'insertAsFirstChildOf'));?>
<?php echo link_to(__('insertAsLastChildOf'), 'navigation/insertAsLastChildOf?component_id='.$navigation->getComponentId(), array('class' => 'insertAsLastChildOf'));?>
if($navigation->isLevel() > 0){
    echo link_to(__('insertAsPrevSiblingOf'), 'navigation/insertAsPrevSiblingOf?component_id='.$navigation->getComponentId(), array('class' => 'insertAsPrevSiblingOf'));
    echo link_to(__('insertAsNextSiblingOf'), 'navigation/insertAsNextSiblingOf?component_id='.$navigation->getComponentId(), array('class' => 'insertAsNextSiblingOf'));
}
?>
<?php if($navigation->getParent()>0): ?>
    // move to parent node and place before
    if($navigation->isLevel()>1){
        echo link_to(__('moveLeftUp'), 'navigation/moveLeftUp?component_id='. $navigation->getComponentId(), array('class' => 'moveLeftUp'));
    }
 
    // move to parent node and place after
    if($navigation->isLevel()>1){
        echo link_to(__('moveLeftDown'), 'navigation/moveLeftDown?component_id='.$navigation->getComponentId(), array('class' => 'moveLeftDown'));
    }
 
    // place as predecessor's child
    if ( $navigation->isLevel() > 0 && $navigation->prevSibling() ) {
        echo link_to(__('moveRightPrev'), 'navigation/moveRightPrev?component_id='.$navigation->getComponentId(), array('class' => 'moveRightPrev'));
    }
 
    // place as successor's child
    if ( $navigation->isLevel() > 0 && $navigation->nextSibling() ){
        echo link_to(__('moveRightNext'), 'navigation/moveRightNext?component_id='.$navigation->getComponentId(), array('class' => 'moveRightNext'));
    }
?>
 
if($navigation->isLevel()>0){
    if($navigation->prevSibling()){
        echo link_to(__('moveToPrevSiblingOf'), 'navigation/moveToPrevSiblingOf?component_id='.$navigation->getComponentId(),array('class' => 'moveUp'));
    }
 
    if($navigation->nextSibling()){
        echo link_to(__('moveToNextSiblingOf'), 'navigation/moveToNextSiblingOf?component_id='.$navigation->getComponentId(), array('class' => 'moveDown'));
    }
}
?>
 

Enjoy it.

by Thomas Schäfer on 2008-03-19, tagged model  navigation  nested  set 

Nested Set Navigation Component Module (PART 2)

NestedSet Model for Navigation

model scheme

enhanced nested set model class

enhanced module action class

navigation list tabular cell template

Features:

enhanced module action class

/apps/backend/modules/navigation/actions/actions.class.php

<?php
 
/**
 * navigation actions.
 */
class navigationActions extends autonavigationActions
{
    public function executeList()
    {
 
        $this->typeOfList = 1;
 
        switch($this->typeOfList)
        {
            case 1:
                $pager = new Navigation();
                $this->pager = $pager->getTree(0,200);
                break;
            default:
 
                $this->processSort();
 
                $this->processFilters();
 
                // pager
 
                $navigation = new Navigation();
                $this->pager = new sfPropelPager('Navigation', 100);
                $c = new Criteria();
                $c->addAscendingOrderByColumn(NavigationPeer::TREE_LEFT);
 
                if($this->getUser()->hasCredential('SHOW_ALL_NAVIGATION_ROOTS')){
 
                } else {
                    $rootNodes = $navigation->getRootNodes();
                    $conditions = array();
                    foreach($rootNodes as $credentialName => $credential) {
                        if($this->getUser()->hasCredential($credentialName)){
                            $c1 = $c->getNewCriterion(NavigationPeer::TREE_LEFT, $credential["left"], Criteria::GREATER_THAN);
                            $c2 = $c->getNewCriterion(NavigationPeer::TREE_RIGHT, $credential["right"], Criteria::LESS_THAN);
                            $conditions[] = $c1->addAnd($c2);
                        }
                    }
                    if(count($conditions) > 1) {
                        foreach($conditions as $condition){
                            $c->addOr($condition);
                        }
                    } else {
                        $c->add($conditions[0]);
                    }
                }
 
 
                $this->addSortCriteria($c);
                $this->addFiltersCriteria($c);
                $this->pager->setCriteria($c);
 
                $this->pager->setPage($this->getRequestParameter('page', 1));
                $this->pager->init();
 
                break;
        }
    }
 
    public function executeShow() {
        $this->component = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $this->paths = $this->component->getPath();
    }
 
    public function executeBlockable() {
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $state = $this->getRequestParameter('state')==1 ? 1 : 0;
        $navigation->setIsBlocked($state);
        $navigation->save();
        $this->state = $state;
        $this->navigation = $navigation;
    }
 
    public function executeNavigatable() {
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $state = $this->getRequestParameter('state')==1 ? 1 : 0;
        $navigation->setIsNavigation($state);
        $navigation->save();
        $this->state = $state;
        $this->navigation = $navigation;
    }
 
 
    private function getPrimaryRoot(){
        return NavigationPeer::retrieveByPk(1);
    }
 
 
    // Default Root Node
    public function executeMakeRoot(){
 
        $root = new Navigation();
        $root->makeRoot();
        $root->save();
 
        return $this->redirect('navigation/edit?component_id='. $root->getNavigationId());
 
    }
 
    // Further Root Node
    public function executeMakeNextRoot(){
 
        $root = new Navigation();
        $root->makeNextRoot();
        $root->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $root->getNavigationId());
 
    }
 
    // Delete Branch
    public function executeDeleteDescendants(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $navigation->deleteDescendants();
        $navigation = $navigation->reload();
        $navigation->delete();
        return $this->redirect('navigation/list');
    }
 
    // insert first child node
    public function executeInsertAsFirstChildOf(){
        $this->message = "Nun wird ein Kindelement erzeugt";
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsFirstChildOf($navigation);
        $child->save();
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
 
    }
 
    // insert last child node
    public function executeInsertAsLastChildOf(){
 
        $this->message = "Nun wird ein Kindelement erzeugt";
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsLastChildOf($navigation);
        $child->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
 
    }
 
    // insert sibling before
    public function executeInsertAsNextSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsNextSiblingOf($navigation);
        $child->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
    }
 
    // insert sibling after
    public function executeInsertAsPrevSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsPrevSiblingOf($navigation);
        $child->save();
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
    }
 
    /*Tree Modification*/
    // UP
    public function executeMoveToPrevSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $siblingNavigation = $navigation->retrievePrevSibling($navigation);
 
        $navigation->moveToPrevSiblingOf($siblingNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
 
    }
    //DOWN
    public function executeMoveToNextSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $siblingNavigation = $navigation->retrieveNextSibling($navigation);
 
        $navigation->moveToNextSiblingOf($siblingNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
 
    }
 
 
    // LEFT UP
    public function executeMoveLeftUp(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
        $navigation->moveToPrevSiblingOf($parentNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // LEFT DOWN
    public function executeMoveLeftDown(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
        $navigation->moveToNextSiblingOf($parentNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
 
    }
 
    // RIGHT FIRST
    public function executeMoveRightPrev(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $prevSibling = $navigation->retrievePrevSibling($navigation);
        $navigation->moveToLastChildOf($prevSibling);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // RIGHT LAST
    public function executeMoveRightNext(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $nextSibling = $navigation->retrieveNextSibling($navigation);
        $navigation->moveToLastChildOf($nextSibling);
        $navigation->save();
        return $this->redirect('navigation/list');
 
    }
 
    // ROOT PREV
    public function executeMoveRootPrev(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $rootNavigation = NavigationPeer::retrieveByPk($this->getRequestParameter('root_id'));
        $prevRootNavigation = $rootNavigation->retrievePrevSibling($rootNavigation);
        $navigation->moveToLastChildOf($prevRootNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
    }
 
    // ROOT NEXT
    public function executeMoveRootNext(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $rootNavigation = NavigationPeer::retrieveByPk($this->getRequestParameter('root_id'));
        $nextRootNavigation = $rootNavigation->retrieveNextSibling($rootNavigation);
        $navigation->moveToLastChildOf($nextRootNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // Duplicate Tree
    public function executeCopyDescendants(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
 
        $newParentNavigation = $this->copyNode($navigation, $parentNavigation);
 
        $this->iterateOnDescendants($navigation->getDescendants() , $newParentNavigation);
 
        return $this->redirect('navigation/list');
    }
 
    private function iterateOnDescendants($navigations, $prevNavigation){
 
        foreach($navigations as $navigation){
            $actualNavigation = $this->copyNodeDescendants($navigation, $prevNavigation);
            $prevNavigation = $actualNavigation;
        }
    }
 
    private function transferData(Navigation $navigation){
 
        $newNavigation = new Navigation();
        $newNavigation->setName($navigation->getName());
        $newNavigation->setAction($navigation->getAction());
        $newNavigation->setParameter($navigation->getParameter());
        $newNavigation->setCaption($navigation->getCaption());
        $newNavigation->setDescription($navigation->getDescription());
        $newNavigation->setIsNavigation(0);
        $newNavigation->setIsBlocked(0);
        $newNavigation->setIsTemplate(0);
        return $newNavigation;
    }
 
    private function copyNodeDescendants($navigation, $prevNavigation){
 
        $newNavigation = $this->transferData($navigation);
 
        if($navigation->getLevel() == $prevNavigation->getLevel() ){
            $newNavigation->insertAsNextSiblingOf($prevNavigation);
        } elseif($navigation->getLevel() > $prevNavigation->getLevel()){
            $newNavigation->insertAsLastChildOf($prevNavigation);
        } else {
            $parentNavigation = NavigationPeer::retrieveByPk($prevNavigation->getParent());
            $newNavigation->insertAsNextSiblingOf($parentNavigation);
        }
        $newNavigation->save();
        return $newNavigation->reload();
    }
 
    private function copyNode($navigation, $parentNavigation){
 
        $newNavigation = $this->transferData($navigation);
 
        $newNavigation->insertAsLastChildOf($parentNavigation);
        $newNavigation->save();
 
        return $newNavigation->reload();
    }
 
}
 
by Thomas Schäfer on 2008-03-19, tagged module  navigation  nested  set 

Nested Set Navigation Component Module (PART 1)

NestedSet Model for Navigation

(This module is for administering big nested navigation trees, not for being used in a high performance web site. If you want to build a resultset for navigations then setup a custom query. There are enough examples out there for querying a nested set )

model scheme

enhanced nested set model class

enhanced module action class

navigation list tabular cell template

Features:

NavigationModel Scheme

  <table name="md_components" phpName="Navigation">
    <column name="component_id" type="INTEGER" size="11" primaryKey="true" required="true" autoIncrement="true"/>
    <column name="name" type="VARCHAR" size="50" required="true" phpName="Name"/>
    <column name="action" type="VARCHAR" size="50" phpName="Action"/>
    <column name="caption" type="VARCHAR" size="50" phpName="Caption"/>
    <column name="description" type="LONGVARCHAR" phpName="Description"/>
    <column name="parameter" type="VARCHAR" size="50" phpName="Parameter"/>
    <column name="image" type="VARCHAR" size="50" phpName="Image"/>
    <column name="tree_left" type="INTEGER" size="11" phpName="Left"/>
    <column name="tree_right" type="INTEGER" size="11" phpName="Right"/>
    <column name="tree_parent" type="INTEGER" size="11" default="0" phpName="Parent"/>
    <column name="tree_scope" type="INTEGER" size="11" default="0" phpName="Scope"/>
    <column name="created_at" type="TIMESTAMP" required="true" phpName="CreationDate"/>
    <column name="created_by" type="INTEGER" size="11" default="1" phpName="Creator"/>
    <column name="templatable" type="BOOLEAN" phpName="IsTemplate"/>
    <column name="blockable" type="BOOLEAN" phpName="IsBlocked"/>
    <column name="navigatable" type="BOOLEAN" phpName="IsNavigation"/>
    <column name="deleted_at" type="TIMESTAMP"/>
    <column name="deleted_by" type="INTEGER" size="11"/>
    <foreign-key foreignTable="sf_guard_user" name="creator" onDelete="setnull">
      <reference local="created_by" foreign="id"/>
    </foreign-key>
    <foreign-key foreignTable="sf_guard_user" name="FK_USER" onDelete="setnull">
      <reference local="deleted_by" foreign="id"/>
    </foreign-key>
  </table>
 

enhanced nested set model class

<?php
 
/**
 * Subclass for representing a row from the 'md_components' table.
 *
 *
 *
 * @package lib.model
 */
class Navigation extends BaseNavigation
{
    public function makeNextRoot()
    {
        $max = $this->getMaxRight();
        $this->setLeftValue($max + 1);
        $this->setRightValue($max + 2);
    }
 
    public function getNavigationId(){
        return $this->getComponentId();
    }
 
    public function getMaxRight(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
 
        $query = sprintf($query, NavigationPeer::TREE_RIGHT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getMaxLeft(){
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
        $query = sprintf($query, NavigationPeer::TREE_LEFT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getRootNodes(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT * FROM %s WHERE %s %s %s;';
 
        $query = sprintf($query, NavigationPeer::TABLE_NAME, NavigationPeer::TREE_PARENT, Criteria::EQUAL, 0);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $credential = array();
        foreach($resultset as $result){
            $credential[$result["name"]]["component_id"] = $result["component_id"];
            $credential[$result["name"]]["name"] = $result["name"];
            $credential[$result["name"]]["left"] = $result["tree_left"];
            $credential[$result["name"]]["right"] = $result["tree_right"];
        }
        return $credential;
    }
 
 
    public function getTree($offset = 0, $limit = 10, $options = null){
 
 
        $query = '
SELECT
    *,
    (
        SELECT
            CONCAT(COUNT(*))
        FROM md_components MA
        WHERE
        (
            (
                MA.TREE_LEFT < MM.TREE_LEFT AND MA.TREE_RIGHT > MM.TREE_RIGHT
            )
            AND MA.TREE_SCOPE = MM.TREE_SCOPE
        )
    )
    AS isLevel,
    (
        SELECT MZ4.COMPONENT_ID
                FROM md_components MZ4 , md_components MZ5
                WHERE MZ5.TREE_LEFT BETWEEN MZ4.TREE_LEFT AND MZ4.TREE_RIGHT
                AND MZ5.COMPONENT_ID = MM.COMPONENT_ID
                ORDER BY MZ4.TREE_LEFT  LIMIT 1,1
    )
    AS selfFirstLevel,
    (
        SELECT
            IF( MB.TREE_RIGHT - MB.TREE_LEFT > 1, 1, 0)
        FROM md_components MB
        WHERE MB.COMPONENT_ID = MM.COMPONENT_ID
        AND MB.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS hasChilds,
    (
        SELECT MC.COMPONENT_ID FROM md_components MC
        WHERE (MM.TREE_LEFT = MC.TREE_RIGHT + 1)
        AND MC.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS prevSibling,
    (
        SELECT
            MD.COMPONENT_ID
        FROM md_components MD
        WHERE (MM.TREE_RIGHT = MD.TREE_LEFT - 1)
        AND MD.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS nextSibling,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF
                        (
                            MQ.TREE_RIGHT - MQ.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MQ
                    WHERE MQ.COMPONENT_ID = MM.COMPONENT_ID
                    AND MQ.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                ME.COMPONENT_ID,
                NULL
            )
        FROM md_components ME
        WHERE ME.TREE_LEFT = MM.TREE_LEFT + 1
        AND ME.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS firstChild,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF(
                            MS.TREE_RIGHT - MS.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MS
                    WHERE MS.COMPONENT_ID = MM.COMPONENT_ID
                    AND MS.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                MF.COMPONENT_ID,
                NULL
            )
        FROM md_components MF
        WHERE MF.TREE_RIGHT = MM.TREE_RIGHT - 1
        AND MF.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS lastChild,
    (
              SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_LEFT - 1 =
        (
                  SELECT
            MZ.TREE_RIGHT
            FROM md_components MZ
            WHERE (MZ.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ.TREE_LEFT < MM.TREE_LEFT) AND (MZ.TREE_PARENT = 0)
            AND MZ.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS nextRoot,
    (
        SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_RIGHT + 1 =
        (
                  SELECT
            MZ2.TREE_LEFT
            FROM md_components MZ2
            WHERE (MZ2.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ2.TREE_LEFT < MM.TREE_LEFT) AND (MZ2.TREE_PARENT = 0)
            AND MZ2.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS prevRoot,
    (
            SELECT
            MZ3.COMPONENT_ID
            FROM md_components MZ3
            WHERE (MZ3.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ3.TREE_LEFT < MM.TREE_LEFT) AND (MZ3.TREE_PARENT = 0)
            AND MZ3.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS selfRoot
 
FROM md_components MM
';
 
        $query .= ' ORDER BY MM.TREE_LEFT ASC ';
        $query .= 'LIMIT '. $offset . ', ' . $limit;
 
        $connection = Propel::getConnection();
        $statement = $connection->createStatement(  );
        $result = $statement->executeQuery( $query , ResultSet::FETCHMODE_NUM);
 
        $objects = NavigationPeer::populateObjects( $result );
 
        $phpNames = NavigationPeer::getFieldNames(BasePeer::TYPE_PHPNAME);
 
        $additionalColumns =
            array_merge(
                $phpNames,
                array("isLevel", "selfFirstLevel", "hasChilds", "prevSibling", "nextSibling", "firstChild", "lastChild", "prevRoot", "nextRoot", "selfRoot")
            );
 
        foreach($result as $key => $object){
            foreach($object as $column => $value){
                if($column > count($phpNames)-1){
                    $method = $additionalColumns[$column];
 
                    $objects[$key-1]->$method = $value;
                }
            }
        }
        return $objects;
    }
 
    /*no setter functions*/
    public function lastChild(){
        return $this->lastChild;
    }
 
    public function selfFirstLevel(){
        return $this->selfFirstLevel;
    }
 
    public function firstChild(){
        return $this->firstChild;
    }
 
    public function prevRoot(){
        return $this->prevRoot;
    }
 
    public function nextRoot(){
        return $this->nextRoot;
    }
 
    public function selfRoot(){
        return $this->selfRoot;
    }
 
    public function nextSibling(){
        return $this->nextSibling;
    }
 
    public function prevSibling(){
        return $this->prevSibling;
    }
 
    public function isLevel(){
        return $this->isLevel;
    }
 
    public function hasChilds(){
        return $this->hasChilds;
    }
}
 
$columns_map = array
(
    'left'   => NavigationPeer::TREE_LEFT,
    'right'  => NavigationPeer::TREE_RIGHT,
    'parent' => NavigationPeer::TREE_PARENT,
    'scope'  => NavigationPeer::TREE_SCOPE
);
 
sfPropelBehavior::add('Navigation', array('actasnestedset' => array('columns' => $columns_map)));
 
by Thomas Schäfer on 2008-03-19, tagged module  navigation  nested  set 

Nested Set Module

NestedSet Model for Navigation

model scheme

enhanced nested set model class

enhanced module action class

navigation list tabular cell template

Features:

NavigationModel Scheme

  <table name="md_components" phpName="Navigation">
    <column name="component_id" type="INTEGER" size="11" primaryKey="true" required="true" autoIncrement="true"/>
    <column name="name" type="VARCHAR" size="50" required="true" phpName="Name"/>
    <column name="action" type="VARCHAR" size="50" phpName="Action"/>
    <column name="caption" type="VARCHAR" size="50" phpName="Caption"/>
    <column name="description" type="LONGVARCHAR" phpName="Description"/>
    <column name="parameter" type="VARCHAR" size="50" phpName="Parameter"/>
    <column name="image" type="VARCHAR" size="50" phpName="Image"/>
    <column name="tree_left" type="INTEGER" size="11" phpName="Left"/>
    <column name="tree_right" type="INTEGER" size="11" phpName="Right"/>
    <column name="tree_parent" type="INTEGER" size="11" default="0" phpName="Parent"/>
    <column name="tree_scope" type="INTEGER" size="11" default="0" phpName="Scope"/>
    <column name="created_at" type="TIMESTAMP" required="true" phpName="CreationDate"/>
    <column name="created_by" type="INTEGER" size="11" default="1" phpName="Creator"/>
    <column name="templatable" type="BOOLEAN" phpName="IsTemplate"/>
    <column name="blockable" type="BOOLEAN" phpName="IsBlocked"/>
    <column name="navigatable" type="BOOLEAN" phpName="IsNavigation"/>
    <column name="deleted_at" type="TIMESTAMP"/>
    <column name="deleted_by" type="INTEGER" size="11"/>
    <foreign-key foreignTable="sf_guard_user" name="creator" onDelete="setnull">
      <reference local="created_by" foreign="id"/>
    </foreign-key>
    <foreign-key foreignTable="sf_guard_user" name="FK_USER" onDelete="setnull">
      <reference local="deleted_by" foreign="id"/>
    </foreign-key>
  </table>
 

enhanced nested set model class

<?php
 
/**
 * Subclass for representing a row from the 'md_components' table.
 *
 *
 *
 * @package lib.model
 */
class Navigation extends BaseNavigation
{
    public function makeNextRoot()
    {
        $max = $this->getMaxRight();
        $this->setLeftValue($max + 1);
        $this->setRightValue($max + 2);
    }
 
    public function getNavigationId(){
        return $this->getComponentId();
    }
 
    public function getMaxRight(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
 
        $query = sprintf($query, NavigationPeer::TREE_RIGHT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getMaxLeft(){
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
        $query = sprintf($query, NavigationPeer::TREE_LEFT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getRootNodes(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT * FROM %s WHERE %s %s %s;';
 
        $query = sprintf($query, NavigationPeer::TABLE_NAME, NavigationPeer::TREE_PARENT, Criteria::EQUAL, 0);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $credential = array();
        foreach($resultset as $result){
            $credential[$result["name"]]["component_id"] = $result["component_id"];
            $credential[$result["name"]]["name"] = $result["name"];
            $credential[$result["name"]]["left"] = $result["tree_left"];
            $credential[$result["name"]]["right"] = $result["tree_right"];
        }
        return $credential;
    }
 
 
    public function getTree($offset = 0, $limit = 10, $options = null){
 
 
        $query = '
SELECT
    *,
    (
        SELECT
            CONCAT(COUNT(*))
        FROM md_components MA
        WHERE
        (
            (
                MA.TREE_LEFT < MM.TREE_LEFT AND MA.TREE_RIGHT > MM.TREE_RIGHT
            )
            AND MA.TREE_SCOPE = MM.TREE_SCOPE
        )
    )
    AS isLevel,
    (
        SELECT MZ4.COMPONENT_ID
                FROM md_components MZ4 , md_components MZ5
                WHERE MZ5.TREE_LEFT BETWEEN MZ4.TREE_LEFT AND MZ4.TREE_RIGHT
                AND MZ5.COMPONENT_ID = MM.COMPONENT_ID
                ORDER BY MZ4.TREE_LEFT  LIMIT 1,1
    )
    AS selfFirstLevel,
    (
        SELECT
            IF( MB.TREE_RIGHT - MB.TREE_LEFT > 1, 1, 0)
        FROM md_components MB
        WHERE MB.COMPONENT_ID = MM.COMPONENT_ID
        AND MB.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS hasChilds,
    (
        SELECT MC.COMPONENT_ID FROM md_components MC
        WHERE (MM.TREE_LEFT = MC.TREE_RIGHT + 1)
        AND MC.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS prevSibling,
    (
        SELECT
            MD.COMPONENT_ID
        FROM md_components MD
        WHERE (MM.TREE_RIGHT = MD.TREE_LEFT - 1)
        AND MD.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS nextSibling,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF
                        (
                            MQ.TREE_RIGHT - MQ.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MQ
                    WHERE MQ.COMPONENT_ID = MM.COMPONENT_ID
                    AND MQ.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                ME.COMPONENT_ID,
                NULL
            )
        FROM md_components ME
        WHERE ME.TREE_LEFT = MM.TREE_LEFT + 1
        AND ME.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS firstChild,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF(
                            MS.TREE_RIGHT - MS.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MS
                    WHERE MS.COMPONENT_ID = MM.COMPONENT_ID
                    AND MS.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                MF.COMPONENT_ID,
                NULL
            )
        FROM md_components MF
        WHERE MF.TREE_RIGHT = MM.TREE_RIGHT - 1
        AND MF.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS lastChild,
    (
              SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_LEFT - 1 =
        (
                  SELECT
            MZ.TREE_RIGHT
            FROM md_components MZ
            WHERE (MZ.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ.TREE_LEFT < MM.TREE_LEFT) AND (MZ.TREE_PARENT = 0)
            AND MZ.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS nextRoot,
    (
        SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_RIGHT + 1 =
        (
                  SELECT
            MZ2.TREE_LEFT
            FROM md_components MZ2
            WHERE (MZ2.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ2.TREE_LEFT < MM.TREE_LEFT) AND (MZ2.TREE_PARENT = 0)
            AND MZ2.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS prevRoot,
    (
            SELECT
            MZ3.COMPONENT_ID
            FROM md_components MZ3
            WHERE (MZ3.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ3.TREE_LEFT < MM.TREE_LEFT) AND (MZ3.TREE_PARENT = 0)
            AND MZ3.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS selfRoot
 
FROM md_components MM
';
 
        $query .= ' ORDER BY MM.TREE_LEFT ASC ';
        $query .= 'LIMIT '. $offset . ', ' . $limit;
 
        $connection = Propel::getConnection();
        $statement = $connection->createStatement(  );
        $result = $statement->executeQuery( $query , ResultSet::FETCHMODE_NUM);
 
        $objects = NavigationPeer::populateObjects( $result );
 
        $phpNames = NavigationPeer::getFieldNames(BasePeer::TYPE_PHPNAME);
 
        $additionalColumns =
            array_merge(
                $phpNames,
                array("isLevel", "selfFirstLevel", "hasChilds", "prevSibling", "nextSibling", "firstChild", "lastChild", "prevRoot", "nextRoot", "selfRoot")
            );
 
        foreach($result as $key => $object){
            foreach($object as $column => $value){
                if($column > 15){
                    $method = $additionalColumns[$column];
 
                    $objects[$key-1]->$method = $value;
                }
            }
        }
        return $objects;
    }
 
    /*no setter functions*/
    public function lastChild(){
        return $this->lastChild;
    }
 
    public function selfFirstLevel(){
        return $this->selfFirstLevel;
    }
 
    public function firstChild(){
        return $this->firstChild;
    }
 
    public function prevRoot(){
        return $this->prevRoot;
    }
 
    public function nextRoot(){
        return $this->nextRoot;
    }
 
    public function selfRoot(){
        return $this->selfRoot;
    }
 
    public function nextSibling(){
        return $this->nextSibling;
    }
 
    public function prevSibling(){
        return $this->prevSibling;
    }
 
    public function isLevel(){
        return $this->isLevel;
    }
 
    public function hasChilds(){
        return $this->hasChilds;
    }
}
 
$columns_map = array
(
    'left'   => NavigationPeer::TREE_LEFT,
    'right'  => NavigationPeer::TREE_RIGHT,
    'parent' => NavigationPeer::TREE_PARENT,
    'scope'  => NavigationPeer::TREE_SCOPE
);
 
sfPropelBehavior::add('Navigation', array('actasnestedset' => array('columns' => $columns_map)));
 

enhanced module action class

/apps/backend/modules/navigation/actions/actions.class.php

<?php
 
/**
 * navigation actions.
 *
 * @package    dispo
 * @subpackage navigation
 * @author     Your name here
 * @version    SVN: $Id: actions.class.php 2288 2006-10-02 15:22:13Z fabien $
 */
class navigationActions extends autonavigationActions
{
    public function executeList()
    {
 
        $this->typeOfList = 1;
 
        switch($this->typeOfList)
        {
            case 1:
                $pager = new Navigation();
                $this->pager = $pager->getTree(0,200);
                break;
            default:
 
                $this->processSort();
 
                $this->processFilters();
 
                // pager
 
                $navigation = new Navigation();
                $this->pager = new sfPropelPager('Navigation', 100);
                $c = new Criteria();
                $c->addAscendingOrderByColumn(NavigationPeer::TREE_LEFT);
 
                if($this->getUser()->hasCredential('SHOW_ALL_NAVIGATION_ROOTS')){
 
                } else {
                    $rootNodes = $navigation->getRootNodes();
                    $conditions = array();
                    foreach($rootNodes as $credentialName => $credential) {
                        if($this->getUser()->hasCredential($credentialName)){
                            $c1 = $c->getNewCriterion(NavigationPeer::TREE_LEFT, $credential["left"], Criteria::GREATER_THAN);
                            $c2 = $c->getNewCriterion(NavigationPeer::TREE_RIGHT, $credential["right"], Criteria::LESS_THAN);
                            $conditions[] = $c1->addAnd($c2);
                        }
                    }
                    if(count($conditions) > 1) {
                        foreach($conditions as $condition){
                            $c->addOr($condition);
                        }
                    } else {
                        $c->add($conditions[0]);
                    }
                }
 
 
                $this->addSortCriteria($c);
                $this->addFiltersCriteria($c);
                $this->pager->setCriteria($c);
 
                $this->pager->setPage($this->getRequestParameter('page', 1));
                $this->pager->init();
 
                break;
        }
    }
 
    public function executeShow() {
        $this->component = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $this->paths = $this->component->getPath();
    }
 
    public function executeBlockable() {
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $state = $this->getRequestParameter('state')==1 ? 1 : 0;
        $navigation->setIsBlocked($state);
        $navigation->save();
        $this->state = $state;
        $this->navigation = $navigation;
    }
 
    public function executeNavigatable() {
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $state = $this->getRequestParameter('state')==1 ? 1 : 0;
        $navigation->setIsNavigation($state);
        $navigation->save();
        $this->state = $state;
        $this->navigation = $navigation;
    }
 
 
    private function getPrimaryRoot(){
        return NavigationPeer::retrieveByPk(1);
    }
 
 
    // Default Root Node
    public function executeMakeRoot(){
 
        $root = new Navigation();
        $root->makeRoot();
        $root->save();
 
        return $this->redirect('navigation/edit?component_id='. $root->getNavigationId());
 
    }
 
    // Further Root Node
    public function executeMakeNextRoot(){
 
        $root = new Navigation();
        $root->makeNextRoot();
        $root->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $root->getNavigationId());
 
    }
 
    // Delete Branch
    public function executeDeleteDescendants(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $navigation->deleteDescendants();
        $navigation = $navigation->reload();
        $navigation->delete();
        return $this->redirect('navigation/list');
    }
 
    // insert first child node
    public function executeInsertAsFirstChildOf(){
        $this->message = "Nun wird ein Kindelement erzeugt";
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsFirstChildOf($navigation);
        $child->save();
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
 
    }
 
    // insert last child node
    public function executeInsertAsLastChildOf(){
 
        $this->message = "Nun wird ein Kindelement erzeugt";
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsLastChildOf($navigation);
        $child->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
 
    }
 
    // insert sibling before
    public function executeInsertAsNextSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsNextSiblingOf($navigation);
        $child->save();
 
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
    }
 
    // insert sibling after
    public function executeInsertAsPrevSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
 
        $child = new Navigation();
        $child->insertAsPrevSiblingOf($navigation);
        $child->save();
 
        return $this->redirect('navigation/edit?component_id='. $child->getNavigationId());
    }
 
    /*Tree Modification*/
    // UP
    public function executeMoveToPrevSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $siblingNavigation = $navigation->retrievePrevSibling($navigation);
 
        $navigation->moveToPrevSiblingOf($siblingNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
 
    }
    //DOWN
    public function executeMoveToNextSiblingOf(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $siblingNavigation = $navigation->retrieveNextSibling($navigation);
 
        $navigation->moveToNextSiblingOf($siblingNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
 
    }
 
 
    // LEFT UP
    public function executeMoveLeftUp(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
        $navigation->moveToPrevSiblingOf($parentNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // LEFT DOWN
    public function executeMoveLeftDown(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
        $navigation->moveToNextSiblingOf($parentNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
 
    }
 
    // RIGHT FIRST
    public function executeMoveRightPrev(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $prevSibling = $navigation->retrievePrevSibling($navigation);
        $navigation->moveToLastChildOf($prevSibling);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // RIGHT LAST
    public function executeMoveRightNext(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $nextSibling = $navigation->retrieveNextSibling($navigation);
        $navigation->moveToLastChildOf($nextSibling);
        $navigation->save();
        return $this->redirect('navigation/list');
 
    }
 
    // ROOT PREV
    public function executeMoveRootPrev(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $rootNavigation = NavigationPeer::retrieveByPk($this->getRequestParameter('root_id'));
        $prevRootNavigation = $rootNavigation->retrievePrevSibling($rootNavigation);
        $navigation->moveToLastChildOf($prevRootNavigation);
        $navigation->save();
 
        return $this->redirect('navigation/list');
    }
 
    // ROOT NEXT
    public function executeMoveRootNext(){
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $rootNavigation = NavigationPeer::retrieveByPk($this->getRequestParameter('root_id'));
        $nextRootNavigation = $rootNavigation->retrieveNextSibling($rootNavigation);
        $navigation->moveToLastChildOf($nextRootNavigation);
        $navigation->save();
        return $this->redirect('navigation/list');
    }
 
    // Duplicate Tree
    public function executeCopyDescendants(){
 
        $navigation = NavigationPeer::retrieveByPk($this->getRequestParameter('component_id'));
        $parentNavigation = NavigationPeer::retrieveByPk($navigation->getParent());
 
        $newParentNavigation = $this->copyNode($navigation, $parentNavigation);
 
        $this->iterateOnDescendants($navigation->getDescendants() , $newParentNavigation);
 
        return $this->redirect('navigation/list');
    }
 
    private function iterateOnDescendants($navigations, $prevNavigation){
 
        foreach($navigations as $navigation){
            $actualNavigation = $this->copyNodeDescendants($navigation, $prevNavigation);
            $prevNavigation = $actualNavigation;
        }
    }
 
    private function transferData(Navigation $navigation){
 
        $newNavigation = new Navigation();
        $newNavigation->setName($navigation->getName());
        $newNavigation->setAction($navigation->getAction());
        $newNavigation->setParameter($navigation->getParameter());
        $newNavigation->setCaption($navigation->getCaption());
        $newNavigation->setDescription($navigation->getDescription());
        $newNavigation->setIsNavigation(0);
        $newNavigation->setIsBlocked(0);
        $newNavigation->setIsTemplate(0);
        return <
by anonymous on 2008-03-19, tagged model  navigation  nested  set 

create styled navigation for current view / highlight active link v.2

I have rewritten my original link_to_styled helper to accommodate more common scenarios. This helper will append a class name to the link if the current route matches the one present in the link.

You can specify within the $style_options parameter to match only the module name rather than the entire route. This is useful for top-level navigation links that correspond to your modules.

You can also specify a tag that will encapsulate your link and receive the class name rather than the <a> tag if the active-state requirements are met.

Additionally, any existing class names given to your links are maintained rather than overwritten with the active class name.

As others have suggested, I'm sure it could still be improved to allow for partial route matching, perhaps excluding query parameters.

NOTE: Thanks to Fabian Lange's suggestion, I reordered the parameters so that the style parameters come before the less-used html options.

Enjoy!

in lib/helper/StyleHelper.php:

/**
 * Automatically adds a class name to the link if the link route matches
 * the current module or route.
 *
 * <b>Style Options:</b>
 * - 'class' - the class name to append to the link, defaults to 'current'
 * - 'module_only' - append class name if the current module and link module match
 * - 'tag' - an html to encapsulate the link, which will receive the class name rather than the <a> tag
 *
 * <b>Examples:</b>
 * <code>
 *   echo link_to_styled('Events', 'event/list', 'class=active', 'id=event_link', );
 *   if the current route is 'event/list', then:
 *     => <a href="path/to/event/list/action" id="event_link" class="active">Events</a>
 * 
 *   echo link_to_styled('Events', 'event/list', 'class=active module_only=true tag=li', 'id=event_link');
 *   if the current module matches the module present in the route, then:
 *     => <li class="active"><a href="path/to/event/list/action" id="event_link">Events</a></li>
 * </code>
 *
 * @param  string name of the link, i.e. string to appear between the <a> tags
 * @param  string 'module/action' or '@rule' of the action
 * @param  array additional style options
 * @param  array additional HTML compliant <a> tag parameters
 * @return string XHTML compliant <a href> tag
 * @see    link_to
 */
function link_to_styled($text, $route = '', $style_options = '', $options = '') {
 
    static $context, $current_module_name;
 
    $options = _parse_attributes($options);
    $style_options = _parse_attributes($style_options);
 
    if (!isset($style_options['class']))
    {
        $style_options['class'] = 'current';
    }
 
    if (isset($style_options['module_only']))
  {
        if (!isset($context))
        {
            $context = sfContext::getInstance();
        }
 
        if (!isset($current_module_name))
      {
        $current_module_name = $context->getModuleName();
      }
 
    list($current_route_name, $params) = $context->getController()->convertUrlStringToParameters($route);
        $is_current = $params['module'] == $current_module_name;
  } 
    else 
    {
        $current_route = sfRouting::getInstance()->getCurrentInternalUri();
        $is_current = $current_route == $route;
    }
 
    if (isset($style_options['tag']))
    {
        $tag_options = (true === $is_current) ? array('class'=>$style_options['class']) : array();
        $return_string = content_tag($style_options['tag'], link_to($text, $route, $options), $tag_options);
    }
    else
    {   
        if (true === $is_current)
        {
            $options['class'] = isset($options['class']) ? $options['class'] . ' ' . $style_options['class'] : $style_options['class'];
        }
        $return_string = link_to($text, $route, $options);
    }
 
    return $return_string;
}

and in the template:

<?php use_helper('Style') ?>
<ul id="navlist">
<!-- highlight this link if we are within the "events" module -->
<li><?php echo link_to_styled('My Events', 'events/list', 'module_only=true') ?></li>
</ul>
 
<!-- or you could add the style to the li tag -->
<?php echo link_to_styled('Event List', 'events/list', 'tag=li class=active') ?></li>
by scott meves on 2007-02-24, tagged css  helper  linkto  navigation 
(6 comments)

create styled navigation for current view

Often times in my navigation I want to change the style of the "current" tab, link, menu item, etc. to reflect that the user is in fact on that page. In order to do this I created a super simple helper called link_to_styled. I added this code to app/lib/helper/StyleHelper.php:

function link_to_styled($text, $route = '', $options = '', $class_name = 'current') {
    $current_route = sfRouting::getInstance()->getCurrentInternalUri();
    if($route == $current_route) {
        if(is_array($options)) {
            $options['class'] = $class_name;
        } else {
            $options .= ' class='.$class_name;
        }
    }
    return link_to($text, $route, $options);
}

and then in my global navigation template I do something like this:

<?php use_helper('Style') ?>
<ul id="navlist">
<li><?php echo link_to_styled('My Friends', 'friends/list') ?></li>
<li><?php echo link_to_styled('My Clubs', 'clubs/list') ?></li>
<li><?php echo link_to_styled('My Events', 'events/list')?></li>
</ul>

The helper will automatically add a class='current' to the link tag. This function could be made more robust, but for a quick and simple solution it seems to do the trick.

by scott meves on 2006-11-02, tagged css  helper  navigation 
(2 comments)