Nested Tree Behaviour

By Jaco (superkruger)
I quite like the Tree Behaviour.
But unfortunately the 'separator' idea is a little too primitive for flexible HTML.
The problem lies in the fact that generatetreelist always returns a flat structure.

Here's a way to return a structure grouped by parent_id, which would allow you to intelligently traverse the tree.
Subclass TreeBehaviour by creating a php file: APP/models/behaviours/group_tree.php
The class name would be GroupTreeBehaviour.

Implement a function called generatetreegrouped:

Model Class:

Download code <?php 
class GroupTreeBehavior extends TreeBehavior {
    function 
generatetreegrouped(&$Model$conditions null$keyPath null$valuePath null$groupPath null$recursive null) {
        
$overrideRecursive $recursive;
        
extract($this->settings[$Model->alias]);
        if (!
is_null($overrideRecursive)) {
            
$recursive $overrideRecursive;
        }

        if (
$keyPath == null && $valuePath == null && $groupPath == null && $Model->hasField($Model->displayField)) {
            
$fields = array($Model->primaryKey$Model->displayField'parent_id'$left$right);
        } else {
            
$fields null;
        }

        if (
$keyPath == null) {
            
$keyPath '{n}.' $Model->alias '.' $Model->primaryKey;
        }

        if (
$valuePath == null) {
            
$valuePath '{n}.' $Model->alias '.' $Model->displayField;
        }
        
        if (
$groupPath == null) {
            
$groupPath '{n}.' $Model->alias '.parent_id';
        }
        
        
$order $Model->alias '.' $left ' asc';
        
$results $Model->find('all'compact('conditions''fields''order''recursive'));
        
$stack = array();

        foreach (
$results as $i => $result) {
            while (
$stack && ($stack[count($stack) - 1] < $result[$Model->alias][$right])) {
                
array_pop($stack);
            }
            
$stack[] = $result[$Model->alias][$right];
        }
        if (empty(
$results)) {
            return array();
        }
        return 
Set::combine($results$keyPath$valuePath$groupPath);
    }
}
?>
There's 2 differences between the new function and 'generatetreelist':
1 - A 'groupPath' parameter has been added, and by default it would be '{n}.' . $Model->alias . '.parent_id';
2 - The 'spacer' parameter has been removed, since it would be unnecessary.

It would result in a tree looking like this:
Download code
Array
(
    [0] => Array
        (
            [1] => Fruit
            [2] => Vegetable
        )

    [1] => Array
        (
            [4] => Apple
            [5] => Banana
        )

    [2] => Array
        (
            [6] => Cauliflower
            [7] => Pumpkin
        )
)

All your tree models should now act as GroupTree instead of Tree.
i.e. place the following in your model: var $actsAs = array('GroupTree');

In your controller, you can now get the treelist by calling:
Download code
$categories = $this->Category->generatetreegrouped();

In your view, do the following to generate a nested list:

Helper Class:

Download code <?php 
class NestedTreeHelper extends AppHelper

function getCategories($key$categories, &$mainList) {
    
$result '<ul>';
    foreach(
$categories as $catKey => $name) {
        
$result .= $this->getCategory($catKey$name$mainList);
    }
    
$result .= '</ul>';
    return 
$result;
}

function 
getCategory($key$value, &$mainList) {
    
$result '<li>';
    
$result .= $value;
    if(
array_key_exists($key$mainList)) {
        
$result .= $this->getCategories($key$mainList[$key], $mainList);
    }
    
$result .= '</li>';
    return 
$result;
}
?>
?>

View Template:

Download code
if(array_key_exists(0, $categories)) {
    echo $nestedtree->getCategories(0, $categories[0], $categories);
}
In this simple example, the 'key' parameters aren't used, but if you do something more useful like creating nested checkboxes in a form, it would be needed.

That's it!
Your HTML should look like:

View Template:

Download code
<ul>
    <li>Fruit
        <ul>
            <li>Apple</li>
            <li>Banana</li>
        </ul>
    </li>
    <li>Vegetable
        <ul>
            <li>Cauliflower</li>
            <li>Pumpkin</li>
        </ul>
    </li>
</ul>

Footnote:
The same functionality could be achieved by using the model function 'findAllThreaded', but the resultset is much more cumbersome than the Tree structure returned above, and I couldn't figure out a way to use Set:combine on it in a way that would yield the same structure.

 

Comments 1338

CakePHP Team Comments Author Comments
 

Comment

1 annoying SPAM

i think its about time to include a captcha behavior for comments!
its really annoying lately - quite a bit of stupid spam posted these days...
Posted Feb 21, 2010 by Mark
 

Comment

2 Why not add it to Cake's tree behaviour

Hi!

Great behaviour, thanks a lot!

I think it would be great to add the generatetreegrouped method to Cake's Tree behaviour so, for example, the Acl component could use it.
Posted Feb 27, 2010 by Jesús Ángel del Pozo Domínguez
 

Comment

3 billi

Viagra, which is generically prescribed as vardenafil, is commonly used to treat erectile dysfunction. Viagra increases blood flow to the appropriate areas of the body to encourage an erection. It also relaxes muscles to encourage an erection.
buy cheap viagra online buy cheap generic viagra ordercheap generic viagra cheap generic viagra buy cheap generic viagra uk buy cheap generic viagra canada cheap generic viagra uk Viagra is not appropriate for everyone. A thorough medical history should be assessed prior to prescribing this medication. Patients with a medical history which includes a heart attack in the previous 6 months, angina, heart rhythm problems, high blood pressure, heart disease, congestive heart failure, stroke, blood clots, Long QT Syndrome, high blood pressure, low blood pressure, liver disease, kidney disease, blood cell disorder, bleeding disorders, stomach ulcer, physical deformity of the penis, retinis pigmentosa, or simply not healthy enough for sexual intercourse may not be able to take Viagra or may require careful monitoring while undergoing drug therapy with Viagra, depending on the condition or the severity of the condition.
cheap generic viagra london online cheap generic viagra generic viagra online internet cheap generic viagra generic viagra buy generic viagra order generic viagra The American Food and Drug Administration rated Viagra as a pregnancy risk category B. While Viagra is not appropriate for women, this medication is not expected to cause harm or birth defects to an unborn baby. Viagra has not been proven to pass through the mother’s breast milk and affect a nursing baby, however the prescribing physician would have no cause to prescribe this medication for a woman.
discount generic viagra sale generic viagra lowest price generic viagra cheap price generic viagra oder generic viagra byu generic viagra There is a risk of side effects associated with Viagra, some of which are severe. A patient who is experiencing a serious side effect or an allergic reaction should seek immediate emergency medical attention. An allergic reaction will present with symptoms such as facial swelling, including swelling of the lips, mouth, tongue, or throat, hives, and difficulty breathing. Other serious side effects which require emergency medical attention include symptoms such as sudden vision loss, dizziness, nausea, pain, tingling or numbness during sexual activity, chest pain or heaviness that includes the arm, irregular heart beat, swelling in the hands and feet, shortness of breath, vision changes, lightheadedness, fainting, an erection which lasts more than 4 hours.
viagra overnight no-prescription online viagra on line no prescripton uk buy online viagra buy cheap generic viagra online buy viagra online canada buy viagra online pharmacy buy viagra online using visa pay pal Other less serious side effects typically do not require emergency medical treatment but should be reported to the prescribing physician. Patients should be encouraged to report all side effects. Less serious side effects include symptoms such as headache, back pain, redness or warmth in the face, neck or chest, or upset stomach. Less serious side effects can often be reduced to a tolerable level by reducing the dosage of Viagra.
order viagra online without a prescription canadian pharmacy online viagra buy free viagra samples online oder viagra online online viagra sales buy viagra discount prescription online buy Viagra should be taken as it has been prescribed by the physician. Levitra is taken only as needed, and there is no dosing schedule. The patient should never take a double dose to improve performance or effectiveness as this can lead to an overdose. If an overdose is suspected, the patient should seek immediate emergency medical attention. An overdose will present with symptoms which include irregular heart rate, chest pain, nausea, lightheadedness, and fainting.
buy viagra online canada viagra online legitmate canada order viagra online without perscription usa where to buy online viagra 100mg pills buy discount viagra online uk buy online genuine pfizer viagra There is a risk of negative drug interactions associated with Viagra. A thorough medical history should be understood prior to prescribing this medication. Patients should be urged to inquire with the prescribing physician before taking any new medications, including over the counter medications and herbal remedies. Medications which have a known interaction with Viagra include rifampin, bosantan, HIV medications, cimetidine, erythromycin, doxazosin, itraconazole, and carbamazapine. Patients who take medication for heart problems can not take Viagra, as there is a significant risk of a serious and dangerous drop in blood pressure or other complications.
cialis levitra viagra online buy online viagra prescriptions canada purchase viagra online canada buy buy viagra medication online purchase viagra online uk

Posted Mar 16, 2010 by bill