PHPackages                             umanit/tree-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. umanit/tree-bundle

ActiveSymfony-bundle[Database &amp; ORM](/categories/database)

umanit/tree-bundle
==================

Manages content tree in your database

1.0.7(2y ago)51.9k5[6 issues](https://github.com/umanit/tree-bundle/issues)MITPHPPHP ^8.0

Since Mar 12Pushed 2y ago7 watchersCompare

[ Source](https://github.com/umanit/tree-bundle)[ Packagist](https://packagist.org/packages/umanit/tree-bundle)[ Docs](https://github.com/umanit/tree-bundle)[ RSS](/packages/umanit-tree-bundle/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (6)Dependencies (4)Versions (19)Used By (0)

Purpose
-------

[](#purpose)

The bundle allows you to easily manage content type routing, with the slug, breadcrumb and SEO

General configuration
---------------------

[](#general-configuration)

As the routing for all content is managed by the bundle, you have to register the unique route **at the end** of your `config/routes.yaml` (in order to not override your custom routes):

```
umanit_tree:
    resource: "@UmanitTreeBundle/Resources/config/routes.yaml"
    prefix:   /

```

Register the bundle to your `config/bundles.php` if you don't use Symfony Flex.

```
    Umanit\TreeBundle\UmanitTreeBundle::class => ['all' => true],
```

Add Gedmo configuration in your `services.yaml` or in a separate `packages/gedmo.yaml`. [See documentation here](https://github.com/doctrine-extensions/DoctrineExtensions/blob/main/doc/symfony4.md)

```
services:
  # Doctrine Extension listeners to handle behaviors
  gedmo.listener.tree:
    class: Gedmo\Tree\TreeListener
    tags:
      - { name: doctrine.event_subscriber, connection: default, priority: 10 }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]

  Gedmo\Translatable\TranslatableListener:
    tags:
      - { name: doctrine.event_subscriber, connection: default }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]
      - [setDefaultLocale, ['%locale%']]
      - [setTranslationFallback, [false]]

  gedmo.listener.timestampable:
    class: Gedmo\Timestampable\TimestampableListener
    tags:
      - { name: doctrine.event_subscriber, connection: default }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]

  gedmo.listener.sluggable:
    class: Gedmo\Sluggable\SluggableListener
    tags:
      - { name: doctrine.event_subscriber, connection: default, priority: 100 }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]

  gedmo.listener.sortable:
    class: Gedmo\Sortable\SortableListener
    tags:
      - { name: doctrine.event_subscriber, connection: default }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]

  gedmo.listener.loggable:
    class: Gedmo\Loggable\LoggableListener
    tags:
      - { name: doctrine.event_subscriber, connection: default }
    calls:
      - [setAnnotationReader, ['@annotation_reader']]
```

Update your database schema to add our model

```
bin/console doctrine:schema:update --force

```

or if you use [DoctrineMigrationsBundle](https://github.com/doctrine/DoctrineMigrationsBundle):

```
bin/console doctrine:migrations:migrate

```

Now, you have to create the root node. The default object is RootEntity, but you can override it in configuration (param `umanit_tree.root_class`).

To create the root node, execute the following command

```
bin/console umanit:tree:initialize

```

Create a new node type
----------------------

[](#create-a-new-node-type)

You can now easily manage a new node type :

- Create an entity
- Implement `Umanit\TreeBundle\Model\TreeNodeInterface` and `Umanit\TreeBundle\Model\SeoInterface`
- You can now use the `Umanit\TreeBundle\Model\TreeNodeTrait` and `Umanit\TreeBundle\Model\SeoTrait` to have a default implementation for most of the methods to implement

The 4 methods of TreeNodeInterface are :

- `public function getTreeNodeName(): string;` : returns the node name, used to build a slug of your route
- `public function getParents(): array;` : returns the parent objects. For example, if you want a path /category/my-product for "My Product", must returns an array with an object "Category" that implements TreeNodeInterface too
- `public function createRootNodeByDefault(): bool;` : if the node has or hasn't parent, should a node be created ? (e.g /my-product)
- `public function getLocale(): ?string` : locale of the object (a locale recognize by `$request->getLocale()` of Symfony)

You can manage some SEO options such as title, description and keywords with the `Umanit\TreeBundle\Model\SeoInterface`and use the `Umanit\TreeBundle\Model\SeoTrait` to automatically add an attribute "seoMetadata" to your entity

Bind a controller and get the entity
------------------------------------

[](#bind-a-controller-and-get-the-entity)

In order to bind a controller (or a template) to an entity, you have to configure the key `node_types`

```
    node_types:
      -   class: App\Entity\Page # your entity
          controller: App\Controller\PageController::get # action to call
```

The entity (or node) will be available in the request attribute.

```
    public function getAction(Request $request)
    {
        $entity = $request->attributes->get('contentObject'); // Your entity
        $node = $request->attributes->get('contentNode'); // TreeBundle's node
    }
```

Create a Parent Node selector
-----------------------------

[](#create-a-parent-node-selector)

### Usage example:

[](#usage-example)

```
$builder
    ->add('parents', \Umanit\TreeBundle\Form\Type\TreeNodeType::class, [
        'required'     => false,
        'by_reference' => false,
    ]);
```

Create SEO Metadata Form
------------------------

[](#create-seo-metadata-form)

### Usage example:

[](#usage-example-1)

```
$builder
    ->add('seoMetadata', \Umanit\TreeBundle\Form\Type\SeoMetadataType::class, [
        'required'     => false,
    ]);
```

Create a link selector
----------------------

[](#create-a-link-selector)

It's possible to create links to one or more nodes (or external links).

In your entity that will have the link, add a relation with the entity `Umanit\TreeBundle\Entity\Link`.

In your forms, you can materialize the relation with the `Umanit\TreeBundle\Form\Type\LinkType`. By default, you'll have two fields, "internal link" (a textfield) and "external link" (a select). By default, the select will be empty. You have to populate it by giving the models allowed. You can keep only one field with the options : `allow_internal: false`or `allow_external: false`. Note : only one field can be filled at the same time.

You can define labels with `label_internal` and `label_external`

### Usage example :

[](#usage-example-)

```
$builder
    ->add('link', 'umanit_link_type_translatable', [
            'label' => 'Link',
            // List of content types available
            'models' => [
                'Page'    => 'Umanit\App\Entity\Page',
                'Article' => 'Umanit\App\Entity\Article',
            ],
        ], [
            // Filters for some content types (if needed)
            'query_filters' => [
                'App\Entity\Page' => ['locale' => 'en'],
            ],
            'allow_external' => false,
        ]
    ]);
```

Events
------

[](#events)

You can subscribe to some events in order to alter some behaviors

(in order)

- `umanit.node.before_update`: called before any node save for an entity
- `umanit.node.parent_register`: allows to add/remove parents to an entity
- `umanit.node.updated`: called once an entity saved its nodes and parents

Twig helpers
------------

[](#twig-helpers)

- `get_seo_title(default = "", override = false)`
- `get_seo_description(default = "", override = false)`
- `get_seo_keywords(default = "", override = false)`

Returns the title, description and keywords of the current document if the route is managed by an entity that implements SeoInterface. Otherwise, the default value (from config) will be used, or the value from "default" parameter if it is set. If you set override to true, the value of the "default" parameter will be always used.

- `get_breadcrumb(elements = array())`

Returns the breadcrumb (array of name/link). It will parse all parents of the current entity if the route is managed by an entity. You can add additional links with the "elements" parameter. An array of name/link.

- `get_path(object, parentObject = null, root = false, absolute = false, parameters = [)`

Returns the route for the given entity (if the entity implements TreeNodeInterface)

- `get_path_from_node(node, absolute = false, parameters = [])`

Returns the route for the given node (instance of `Umanit\TreeBundle\Entity\Node`)

- `get_path_from_link(link)`

Returns the path for the given link instance (instance of `Umanit\TreeBundle\Entity\Link`).

- `is_external_link(link)`

Returns true if the given link targets an external URL (instance of `Umanit\TreeBundle\Entity\Link`).

Using the menu admin
--------------------

[](#using-the-menu-admin)

> **/!\\ For performance concerns, we chose to support PostgreSQL only.**

Follow those two steps to get started:

### 1. Create your Menu entity (use annotations or attributes)

[](#1-create-your-menu-entity-use-annotations-or-attributes)

```
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Umanit\TreeBundle\Entity\AbstractMenu as BaseMenu;
use Umanit\TreeBundle\Repository\MenuRepository;

/**
 * Menu
 *
 * @ORM\Table(name="menu")
 * @ORM\Entity(repositoryClass="Umanit\TreeBundle\Repository\MenuRepository") // Using TreeBundle's repository is
 *                                                                            mandatory
 * @ORM\HasLifecycleCallbacks() // This is mandatory too
 */
#[ORM\Table(name: 'menu')]
#[ORM\Entity(repositoryClass: MenuRepository::class)] // Using TreeBundle's repository is mandatory
#[ORM\HasLifecycleCallbacks] // Mandatory
class Menu extends BaseMenu
{
}
```

### 2. Configure TreeBundle to use your Menu entity

[](#2-configure-treebundle-to-use-your-menu-entity)

```
# app/config/config.yml
umanit_tree:
  # ...
  menu_entity_class: App\Entity\Menu
```

### Usage

[](#usage)

#### Front

[](#front)

TreeBundle doesn't come with a template for the menu. A global twig variable is injected to your site, use it to build your menu template(s).

**Example:**

```

    {% for menu in menus %}
      {% if menu.position == 'primary' %}

            {{- menu.title|raw -}}
            {#-  -#}

          {% if menu.children is not empty %}

              {% for subMenu in menu.children %}
                {%  if subMenu.link is empty %}
                  {{ subMenu.title|raw }}
                {% else %}

                    {{ subMenu.title|raw }}

                {% endif %}
              {% endfor %}

          {% endif %}

      {%  endif %}
    {% endfor %}

```

#### Menu admin

[](#menu-admin)

A CRUD is provided in order to administrate your menus. It's available on the route `tree_admin_menu_dashboard`, /admin/menu.

/!\\ You need to have the role `ROLE_TREE_MENU_ADMIN` in order to be able to access the route.

Start by running `php bin/console assets:install` to get the assets in your web/public directory.

##### Customizing the admin layout

[](#customizing-the-admin-layout)

The layout can be customized to your needs by setting the `admin_layout` configuration value.

Example if you want to use Sonata Admin's layout:

```
# config.yml
umanit_tree:
  # ...
  admin_layout: '@SonataAdmin/standard_layout.html.twig' # Default is '@UmanitTree/admin/default_layout.html.twig'
```

The menu admin has 3 javascript dependencies, you ought to include them as well. Have a look in the default\_layout.html.twig.

```

```

TreeBundle ships with those assets, you may use them or your own.

Again, if you want to use it with SonataAdmin, configure it as follows:

```
sonata_admin:
  # ...
  assets:
    extra_stylesheets:
      # TreeBundle's assets
      - bundles/umanittree/css/admin.css
      - bundles/umanittree/css/vendor/ui.fancytree.min.css
    extra_javascripts:
      # TreeBundle's assets
      - bundles/umanittree/js/vendor/jquery-ui.min.js
      - bundles/umanittree/js/vendor/jquery.fancytree-all-deps.min.js
      - bundles/umanittree/js/vendor/jquery.fancytree.dnd.js
```

##### Customizing the admin form

[](#customizing-the-admin-form)

Let's assume you added an image attribute on your Menu entity and want to use VichUploader to administrate it.

First, Create a form type:

```
namespace App\Form;

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Vich\UploaderBundle\Form\Type\VichImageType;
use Umanit\TreeBundle\Form\Type\MenuType as BaseMenuType;

class MenuType extends BaseMenuType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);
        $builder
            ->add('imageFile', VichImageType::class, [
                'label'        => 'Image',
                'required'     => false,
                'allow_delete' => true,
                'attr'         => [
                    'imagine_pattern' => 'admin',
                ],
            ])
            ->add('altImage')
        ;
    }
}
```

Then add your form type to TreeBundle's configuration:

```
umanit_tree:
  # ...
  menu_form_class: App\Form\MenuType
```

Configuration reference
-----------------------

[](#configuration-reference)

```
umanit_tree:
  locale: '%locale%'                                    # Optional. Default locale to use
  root_class: '\Umanit\TreeBundle\Entity\RootEntity' # Optional. Class for the root node. If you have a homepage object, put it there
  admin_layout: '@UmanitTree/admin/default_layout.html.twig'  # Optional. Default layout for the menu admin section
  menu_form_class: 'Umanit\TreeBundle\Form\Type\MenuType' # Optional. Default form for Menu
  menu_entity_class: 'App\Entity\Menu'                             # Optional. Your menu entity. Required if you want to use the menu admin
  menus: ['primary']                                   # Optional. Configure you menus.

  # Defines configuration per node types. You can set a specific controller per class and set if the node type must appear in the menu admin.
  node_types:
    # Prototype
    -   class: ~ # Required. Ex. : App\Entity\Page
        controller: ~ # Optional. Default FrameworkBundle:Template:template. Ex. : App:Page:show
        template: ~ # Required if controller is not set.
        menu: ~ # Optional. Default is false

  # Seo default values and translation domain
  seo:
    redirect_301: true   # Redirect old URLs to new ones
    default_title: 'Umanit Tree'
    default_description: 'Umanit tree bundle'
    default_keywords: 'umanit, web, bundle, symfony2'
    translation_domain: 'messages'

  # Root node and translation domain for breadcrumb elements
  breadcrumb:
    root_name: 'Home'
    translation_domain: 'messages'
```

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance0

Infrequent updates — may be unmaintained

Popularity23

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity77

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~131 days

Recently: every ~109 days

Total

18

Last Release

762d ago

Major Versions

0.x-dev → 1.0.02023-01-18

PHP version history (3 changes)0.1PHP &gt;=5.4

0.3PHP &gt;=5.6

1.0.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/27f13221fde4957c93a4ed7293a6460272d626e53df227e3aae4f35aa9289e69?d=identicon)[DjLeChuck](/maintainers/DjLeChuck)

---

Top Contributors

[![Peekmo](https://avatars.githubusercontent.com/u/3881523?v=4)](https://github.com/Peekmo "Peekmo (48 commits)")[![Nedeas](https://avatars.githubusercontent.com/u/4601729?v=4)](https://github.com/Nedeas "Nedeas (30 commits)")[![DjLeChuck](https://avatars.githubusercontent.com/u/696780?v=4)](https://github.com/DjLeChuck "DjLeChuck (16 commits)")[![GeneraleCauchemar](https://avatars.githubusercontent.com/u/38556469?v=4)](https://github.com/GeneraleCauchemar "GeneraleCauchemar (9 commits)")[![ValentinMerlet](https://avatars.githubusercontent.com/u/2642302?v=4)](https://github.com/ValentinMerlet "ValentinMerlet (5 commits)")[![artggd](https://avatars.githubusercontent.com/u/1837559?v=4)](https://github.com/artggd "artggd (3 commits)")[![titoff3](https://avatars.githubusercontent.com/u/3337478?v=4)](https://github.com/titoff3 "titoff3 (1 commits)")

---

Tags

symfonytree

### Embed Badge

![Health badge](/badges/umanit-tree-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/umanit-tree-bundle/health.svg)](https://phpackages.com/packages/umanit-tree-bundle)
```

###  Alternatives

[hautelook/alice-bundle

Symfony bundle to manage fixtures with Alice and Faker.

19519.4M34](/packages/hautelook-alice-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[kimai/kimai

Kimai - Time Tracking

4.6k7.4k1](/packages/kimai-kimai)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

1022.4k](/packages/rcsofttech-audit-trail-bundle)[ahmed-bhs/doctrine-doctor

Runtime analysis tool for Doctrine ORM integrated into Symfony Web Profiler. Unlike static linters, it analyzes actual query execution at runtime to detect performance bottlenecks, security vulnerabilities, and best practice violations during development with real execution context and data.

813.1k](/packages/ahmed-bhs-doctrine-doctor)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1714.8k8](/packages/2lenet-crudit-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
