Skip to content

Latest commit

 

History

History
168 lines (138 loc) · 4.3 KB

Tree.md

File metadata and controls

168 lines (138 loc) · 4.3 KB

Tree Helper

A CakePHP helper to handle tree structures.

By default, it uses the core TreeBehavior and MPTT (Modified Preorder Tree Traversal). But it sure can work with any tree like input as nested object or array structure.

It can work with both arrays and Entity objects. The latter should be preferred as you can then use all properties and getters on that object.

Usage

Basic usage

Include helper in your AppView class as

$this->loadHelper('Tools.Tree', [
    ...
]);

Then you can use it in your templates as

echo $this->Tree->generate($articles);

Templated usage

By default, just outputting the display name is usually not enough. You want to create some Template/Element/tree_element.ctp element instead:

echo $this->Tree->generate($articles, ['element' => 'tree_element']);

That template can then contain all normal template additions, including full helper access:

<?php
/**
 * @var \App\View\AppView $this
 * @var \App\Model\Entity\Article|\Cake\Collection\CollectionInterface $data
 * @var bool $activePathElement
 */

if (!$data->visible) { // You can do anything here depending on the record content
    return;
}
$label = $data->title;
if ($activePathElement) {
    $label .= ' (active)';
}
?>
<li>
<?php echo $this->Html->link($label, ['action' => 'view', $data->id]); ?>
</li>

So the current entity object is available as $data variable inside this snippet.

Available element data

  • $data : object|object[]|array
  • $parent : object|array|null
  • $depth : int
  • $hasChildren : int
  • $numberOfDirectChildren : int
  • $numberOfTotalChildren : int
  • $firstChild : bool
  • $lastChild : bool
  • $hasVisibleChildren : bool
  • $activePathElement : string
  • $isSibling : bool

plus all config values.

Callback usage

Here the same keys are available on the first argument ($data array). So the above $data would actually be $data['data'] and usually be the entity. If you are passing entities, it helps to inline annotate in this case:

    $closure = function(array $data) {
        /** @var \Cake\ORM\Entity $entity */
        $entity = $data['data'];

        return h($entity->name) . ($data['activePathElement'] ? ' (active)' : '');
    };

Active path

When using the TreeHelper for navigation structures, you would usually want to set the active path as class elements ("active") on the <li> elements. You can do that by passing in the current path.

// Your controller fetches the navigation tree
$tree = $this->Table->find('threaded')->toArray();

// The current active element in the tree (/view/6)
$id = 6;

// We need to get the current path for this element
$nodes = $this->Table->find('path', ['for' => $id]);
$path = $nodes->extract('id')->toArray();

// In your view
$options = [
    'autoPath' => [$current->lft, $current->rght],
    'treePath' => $path,
    'element' => 'tree', // src/Template/Element/tree.ctp
];
echo $this->Tree->generate($tree, $options);

The autoPath setting passed using [lft, rght] of your current element will auto-add "active" into your elements. You can also just pass the current entity ('autoPath' => $current) and it will extract lft and rght properties based on the config.

The treePath is optional and needed for additional things like hiding unrelated siblings etc.

Custom data array

You can also use any custom data array for building a tree, as long as it contains children elements per element:

$treeData = [
    [
        'name' => 'Foo',
        'children' => [
            [
                'name' => 'Bar',
                'children' => [
                ],
            ],
            [
                'name' => 'Baz',
                'children' => [
                    [
                        'name' => 'Baz Child',
                        'children' => [
                        ],
                    ],
                ],
            ],
        ],
    ],
];

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Invalid Tree Structure: Array');

echo $this->Tree->generate($treeData);

will output

<ul>
	<li>Foo
	<ul>
		<li>Bar</li>
		<li>Baz
		<ul>
			<li>Baz Child</li>
		</ul>
		</li>
	</ul>
	</li>
</ul>

Outview

You can read some more tutorial like details in my blog post.