Sunday, December 4, 2011

Re: how to use this Menu Helper

<?php
/**
* Menu Helper
*
* @author John Elliott (http://www.flipflops.org)
* @package app
* @subpackage app.views.helpers
* @version 1.0
* @lastmodified 2009-09-29
*
* This helper is designed to work with the data arrays returned by
the Tree behaviour or $model->find('threaded');
* More specifically it is designed to generate menus composed of
nested ULs for use in CMS systems where all the URLs are pretty:
* - naviagtion menus (usually at the top of the page)
* - context menus (usually found at the side) and showing the links
in the current branch
* - sitemaps
*
* Overview (A bit of history)
*
* The helper is designed to deal with navigation / page structures
organised in simple /parent/child/grand-child like structures
* for instance:
*
* /home
* /about
* /about/company-history
* /about/company-history/gallery
* /about/ethos
* /about/vacancies
* /news
* /news/2009/jan
* /news/2009/feb
* /news/2009/mar
* /contact
* (etc.)
*
* -------------------
* URLs are generated in the model or controller not in the view /
helper
* -------------------
* In the system from which this is taken, I create a full slug for
each page on $model->save();
* Some of the pages are actual pages of text and others are
palceholders for content in other models
* but they allow you do things like control the content of left /
right columns, set default meta tags etc.
* The purpose being to create really easy site structures with nice
urls, the
* In the example above /home, /about etc. are all from a single model
'Article' which is my master model
* /news however is a placeholder for the 'News' model and /contact
for the 'Contact' model
*
*
*
* Required Fields
* ------------------
* title / name (defaults to 'title')
* page url (defaults to 'slug_url' e.g. /about/ethos)
*
* Optional Fields
* ------------------
* title_for_navigation
* redirect_url
* redirect_target
*
* Example data
* ------------------
* [Article] => Array
(
[name] => Find out about us
[slug_url] => /about-us
[title_for_navigation] => About Us
[redirect_url] =>
[redirect_target] =>
[lft] => 15
[id] => 105
[rght] => 24
[parent_id] =>
)

[children] => Array
(
[0] => Array
(
[Article] => Array
(
[name] => Philosophy
[slug_url] => /about-us/philosophy
[title_for_navigation] =>
[redirect_url] =>
[redirect_target] =>
[lft] => 16
[id] => 111
[rght] => 21
[parent_id] => 105
)
*
*
*
*
* Usage:
*
* Include the helper in your controller as usual.
*
* The helper operates in 2 modes 'tree' (which is default) and
'context'
* 'tree' will produce a whole list of nested Uls - can be used to
generate top level navigation (works well with things like suckerfish)
* 'context' will only produce nested ULs for the current branch
*
* Using the example above if you were on the /about/ethos page and
you wanted the navigation fragment in the sidebar you would use
context
* /about
* /about/company-history
* /about/company-history/gallery
* /about/ethos
* /about/vacancies
*
* In a view / element to generate complete menu
* echo $menu->setup($menu_data, array('selected' => $this->here));
*
* In a view / element to generate a context menu from the same data,
set the class of the parent UL
* echo $menu->setup($menu_data, array('selected' => $this->here,
'type' => 'context', 'menuClass' => 'context-menu'));
*
*
* In a view / element to generate a sitemap from different data, let
the helper know to use the 'Sitemap' model rather than the default
'Article' model and set the parent UL class.
* $sitemap = new MenuHelper();
* echo $sitemap->setup($data, array('modelName' => 'Sitemap',
'menuClass' => 'sitemap'));
*
* Version Details
*
* 1.0
* + Initial release.
*/
class MenuHelper extends AppHelper {

/**
* Current page in application
*
* @var string
*/
private $selected = '';

/** Internal variable for the data
*
* @var array
*/
private $array = array();

/**
* Default css class applied to the menu
*
* @var string
*/
private $menuClass = 'menu';

/**
* Default DOM id applied to menu
*
* @var string
*/
private $menuId = 'top-menu';

/**
* CSS class applied to the selected node and its parent nodes
*
* @var string
*/
private $selectedClass = 'selected';

/**
* CSS class applied to the exact selected node in the tree - in
addition to $selectedClass
*
* @var unknown_type
*/
private $selectedClassItem = 'item-selected';

/**
* Default Slug
*
* @var string
*/
private $defaultSlug = 'home';

/**
* Type of menu to be generated:
* 'tree' - to generate a complete tree
* 'context' - to only render the specific barnch under the current
page
*
* @var string
*/
private $type = 'tree';

/**
* Model name used in $array e.g. $data[0]['Article']['name']
*
* @var string
*/
private $modelName = 'Article';

/**
* Database column name - (i.e. a shorter version of the name / title
for use only in naviagtion)
* e.g. A page called 'Welcome to the giant flea circus'
* might be set to show up on navigation as 'home'
*
* @var string
*/
private $titleForNavigation = 'title_for_navigation';

/**
* Database column name for title / name
* @var string
*/
private $title = 'name';

/**
* Database column name for complete page slug e.g. /about/history/
early-years
*
* @var string
*/
private $slugUrl = 'slug_url';

/**
* Database column name for redirect_url for instance if /about/blog
redirects to http://blog.somewebsite.com
*
* @var string
*/
private $redirectUrl = 'redirect_url';

/**
* Target for redirect (see redirectUrl)
*
* @var string
*/
private $redirectTarget = 'redirect_target';

/**
* Minumum number of items required to render a context menu
*
* @var int
*/
private $contextMinLength = 2;

/**
* Internal Counter used in type: 'context'
*
* @var int
*/
private $li_count = 0;

/**
* Internal flag to see if the page has been matched to an item
*
* @var bool
*/
private $matched = false;

/**
* Internal counter
*
* @var int
*/
private $i = 0;

/**
* Enter description here...
*
* @var unknown_type
*/
private $rootNode = '';

function __construct(){

}

public function setOption($key, $value){
$this->{$key} = $value;
}

public function getOption($key){
return $this->{$key};
}

/**
* Setup the helper and return a string to echo
*
* @param array $array Data array containing the lists
* @param array $config Configuration variables to override the
defaults
* @return string
*/
public function setup($array, $config = array()){

// update and override the default variables
if(!empty($config)){
foreach ($config as $key => $value) {
$this->setOption($key, $value);
}
}

// set the default slug selected if the current page does not match
if($this->selected == '/'){
$this->selected = $this->defaultSlug;
}

$this->array = $array;

// get the root node of the selected tree if this a context menu
if($this->type == 'context'){
$this->rootNode = $this->getRootNode($this->selected);
}

$str = $this->buildMenu();

// if the current page has matched one of the links in the tree
// then get rid of the 'default_slected' placeholder
if($this->matched == true){
$str = str_replace('default_selected', '', $str);
} else {
$s = ' class="' . $this->selectedClass . '" ';
$str = str_replace('default_selected', $s, $str);
}

// if this is a context menu, it looks daft if it only has 1 item
// if this is the case hide it
if($this->type == 'context'){
if($this->li_count < $this->contextMinLength){
$str = '';
}
}


return $this->output($str);

}
/**
* Call the menu iterator method and if it returns a string warp it
up in a UL
*
* @return string
*/
protected function buildMenu(){

$str = $this->menuIterator($this->array);

if($str != ''){
$str = '<ul id="' . $this->menuId . '" class="' . $this-
>menuClass . '">' . $str . '</ul>';
}

return $str;
}

/**
* Explode a url slug and get the root page
*
* @param string $string
* @return string
*/
protected function getRootNode($string){
$rootNode = '';
if($string != ''){
$node = explode('/', $string);
// $node[0] will always be empty becuase the first char of $this-
>selected will always be '/'
$rootNode = $node[1];
}
return $rootNode;
}


/**
* Recursive method to loop down through the data array building
menus and sub menus
*
* @param array $array
* @param int $depth
* @return string
*/
protected function menuIterator($array){

$str = '';
$is_selected = false;
foreach($array as $var){

$continue = true;
$selected = '';
$sub = '';

if($this->type == 'context' && ($this->getRootNode($var[$this-
>modelName][$this->slugUrl]) != $this->rootNode)){
$continue = false;
}

if($continue == true){

// if this is the first list item set default_selected placeholder
$default_selected = '';
if($this->i == 0){
$this->i = 1;
$default_selected = 'default_selected';
}

if(!empty($var['children'])){
$sub .= '<ul>';
$sub .= $this->menuIterator($var['children']);
$sub .= '</ul>';
}

$p = strpos($this->selected, $var[$this->modelName][$this-
>slugUrl]);


if($p === false){

} elseif($p == 0){
// this is the selected item or a parent node of the selected
item
$selected = ' class="' . $this->selectedClass . '" ';
$is_selected = true;
$this->matched = true;
}

if($this->selected == $var[$this->modelName][$this->slugUrl]){
// this is the exact selected item
$selected = ' class="' . $this->selectedClass . ' ' . $this-
>selectedClassItem . '" ';
}

// keep track if this is a contextual menu
if($this->type == 'context'){
$this->li_count++;
}


// Get the name / title to be used for the link text
$name = $this->getName($var);
// Get the URL / target for the link
$url = $this->getUrl($var);

$str .= '<li ' . $selected . ' ' . $default_selected . '>';
$str .= '<a href="' . $url['url'] . '" ' . $url['target'] .
'><span>' . $name . '</span></a>';
$str .= $sub;
$str .= '</li>';

}
}
return $str;
}
/**
* Look in the data and check if this is a straight url
* or whether it is actually a redirect
*
* @param array $var
* @return array
*/
protected function getUrl($var = null){
$url = array();

if(isset($var[$this->modelName][$this->redirectUrl]) && !
empty($var[$this->modelName][$this->redirectUrl])){
$url['url'] = $var[$this->modelName][$this->redirectUrl];
if(isset($var[$this->modelName][$this->redirectTarget]) && !
empty($var[$this->modelName][$this->redirectTarget])){
$url['target'] = ' target="' . $var[$this->modelName][$this-
>redirectTarget] . '" ';
}
} else {
$url['url'] = $var[$this->modelName][$this->slugUrl];
$url['target'] = '';
}
return $url;
}

/**
* See if there is a title_for_navigation
*
* @param array $var
* @return string
*/
protected function getName($var){
if(isset($var[$this->modelName][$this->titleForNavigation]) && !
empty($var[$this->modelName][$this->titleForNavigation])){
$name = $var[$this->modelName][$this->titleForNavigation];
} else {
$name = $var[$this->modelName][$this->title];
}
return $name;
}

}
?>

--
Our newest site for the community: CakePHP Video Tutorials http://tv.cakephp.org
Check out the new CakePHP Questions site http://ask.cakephp.org and help others with their CakePHP related questions.


To unsubscribe from this group, send email to
cake-php+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/cake-php

No comments: