TreeBrowserBundle

2.0 version
Maintained Unmaintained

TreeBrowserBundle

The TreeBrowserBundle provides a tree navigation on top of a PHPCR repository. The front-end implementation is based on the jQuery plugin jsTree.

This bundle consists of two parts:

  • Generic Tree Browser with a TreeInterface
  • PHPCR tree implementation and GUI for a PHPCR browser

Installation

You can install this bundle with composer using the symfony-cmf/tree-browser-bundle package on Packagist.

Both the CmfTreeBrowserBundle and FOSJsRoutingBundle must be registered in the AppKernel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// app/appKernel.php
// ...
public function registerBundles()
{
    $bundles = [
        // ...
        new FOS\JsRoutingBundle\FOSJsRoutingBundle(),
        new Symfony\Cmf\Bundle\TreeBrowserBundle\CmfTreeBrowserBundle(),
    ];
    // ...

    return $bundles;
}

Routing

The bundle will create routes for each tree implementation found. In order to make those routes available you need to include the following in your routing configuration:

  • YAML
    1
    2
    3
    4
    # app/config/routing.yml
    cmf_tree:
        resource: .
        type: 'cmf_tree'
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    <!-- app/config/routing.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
            http://symfony.com/schema/routing/routing-1.0.xsd">
    
        <import resource="." type="cmf_tree" />
    
    </routes>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    // app/config/routing.php
    use Symfony\Component\Routing\RouteCollection;
    
    $collection = new RouteCollection();
    $collection->addCollection($loader->import('.', 'cmf_tree'));
    
    return $collection;
    

Usage

The TreeBrowserBundle provides the select_tree.js and admin_tree.js files, which are wrappers to build a jQuery tree. Use them with SelectTree.initTree resp. AdminTree.initTree.

  • SelectTree in select_tree.js is a tree to select a target tree node, e.g. in a form. The id of the target is put into a text field;
  • AdminTree in admin_tree.js is a tree to create, move and edit nodes.

Both have the following options when creating:

  • config.selector: jQuery selector where to hook in the js tree;
  • config.rootNode: id to the root node of the tree, defaults to /;
  • config.selected: id of the selected node;
  • config.ajax.children_url: URL to the controller that provides the children of a node;
  • config.routing_defaults: array for route parameters (e.g. _locale);
  • config.path.expanded: tree path where the tree should be expanded to at the moment;
  • config.path.preloaded: tree path what node should be preloaded for faster user experience.

select_tree.js only

  • config.output: where to write the id of the selected node.

admin_tree.js only

  • config.labels: array containing the translations for the labels of the context menu (keys createItem and deleteItem);
  • config.ajax.move_url: Url to the controller for moving a child (i.e. giving it a new parent node);
  • config.ajax.reorder_url: Url to the controller for reordering siblings;
  • config.types: array indexed with the node types containing information about valid_children, icons and available routes, used for the creation of context menus and checking during move operations.

Examples

Look at the templates in the SonataDoctrinePHPCRAdminBundle for examples how you could build a tree:

In the same bundle, the PhpcrOdmTree implements the tree interface and provides an example how to implement the methods.

Customizing the Tree Behavior

The TreeBrowserBundle is based on jsTree. jsTree works with events, dispatched every time the user does an action. A simple way to customize the tree behavior is to bind your actions to those events.

If you have a look at admin_tree.js and select_tree.js, you will notice that actions are already bound to some of the tree events. If the default behavior is not what you need, jQuery provide the unbind method to remove the default events. Here is a simple way to remove the context menu from the admin tree:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    {% render 'sonata.admin.doctrine_phpcr.tree_controller:treeAction' with {
        'root':     sitePath ~ "/menu",
        'selected': menuNodeId
    } %}
    <script type="text/javascript">
        $(document).ready(function() {
            $('#tree').bind("before.jstree", function (e, data) {
                if ("contextmenu" === data.plugin) {
                    e.stopImmediatePropagation(); // stops executing of default event
    
                    return false;
                }
            });
        });
    </script>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <?php
    $view['actions']->render('sonata.admin.doctrine_phpcr.tree_controller:treeAction', [
        'root'     => $sitePath . '/menu',
        'selected' => $menuNodeId,
    ])?>
    <script type="text/javascript">
        $(document).ready(function() {
            $('#tree').bind("before.jstree", function (e, data) {
                if ("contextmenu" === data.plugin) {
                    e.stopImmediatePropagation(); // stops executing of default event
    
                    return false;
                }
            });
        });
    </script>
    

Note

This example assumes you have the SonataDoctrinePHPCRAdminBundle available, to have a tree implementation.

By default, the item selection opens the edit route of the admin class of the element. This action is bind to the select_node.jstree. If you want to remove it, you just need to call the unbind function on this event:

1
2
3
$(document).ready(function() {
    $('#tree').unbind('select_node.jstree');
});

Then you can bind it on another action.

For example, if your want to open a custom action:

  • Twig
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $('#tree').bind("select_node.jstree", function (event, data) {
        if ((data.rslt.obj.attr("rel") == 'Symfony_Cmf_Bundle_MenuBundle_Doctrine_Phpcr_MenuNode'
            && data.rslt.obj.attr("id") != '{{ menuNodeId }}'
        ) {
            var routing_defaults = {'locale': '{{ locale }}', '_locale': '{{ _locale }}'};
            routing_defaults["id"] = data.rslt.obj.attr("url_safe_id");
            window.location = Routing.generate('presta_cms_page_edit', routing_defaults);
        }
    });
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $('#tree').bind("select_node.jstree", function (event, data) {
        if ((data.rslt.obj.attr("rel") == 'Symfony_Cmf_Bundle_MenuBundle_Doctrine_Phpcr_MenuNode'
            && data.rslt.obj.attr("id") != '<?php echo $menuNodeId ?>'
        ) {
            var routing_defaults = {'locale': '<?php echo $locale ?>', '_locale': '<?php echo $_locale ?>'};
            routing_defaults["id"] = data.rslt.obj.attr("url_safe_id");
            window.location = Routing.generate('presta_cms_page_edit', routing_defaults);
        }
    });
    

Note

This bundle automatically exposes routes with the FOSJsRoutingBundle to allow the tree to work.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.