Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Admin Tree Bundle Laravel Package

fsi/admin-tree-bundle

FSI Admin Tree Bundle adds hierarchical tree management to PHP admin interfaces, helping you organize and reorder nested records such as categories, menus, and parent-child data through a lightweight Symfony bundle.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. This is a single-purpose, zero-star, zero-dependent bundle whose last release was February 2020. Before writing any code, budget an hour to read the source — you are adopting it as a reference implementation as much as a dependency, and you should expect to vendor-patch or fork it.

Install and inspect:

composer require fsi/admin-tree-bundle

If resolution fails, check composer.json in the packagist metadata: a 2020 FSi bundle will constrain Symfony to ^3.4|^4.4 and PHP to ^7.x. On Symfony 5.4+/6+/7+ or PHP 8.2+, expect to fork, bump constraints, and run the test suite yourself. Do not let Composer silently downgrade a shared Symfony runtime.

Where to look first, in this order:

  1. vendor/fsi/admin-tree-bundle/composer.json — actual supported stack and which sibling FSi/Sonata packages it expects.
  2. src/FSi/Bundle/AdminTreeBundle/Resources/config/ — every service it registers, plus routing.
  3. The bundle root class (likely FSi\Bundle\AdminTreeBundle\FSiAdminTreeBundle) — what it adds to the DI container.
  4. The action/controller classes — this is the whole feature; everything else is wiring.

Register the bundle in config/bundles.php (or AppKernel on legacy Symfony):

return [
    // ...
    FSi\Bundle\AdminBundle\FSiAdminBundle::class => ['all' => true],
    FSi\Bundle\AdminTreeBundle\FSiAdminTreeBundle::class => ['all' => true],
];

Confirm the routes exist — this is the fastest sanity check that wiring succeeded:

bin/console debug:router | grep -i tree

First use case: a category/taxonomy entity shown in a FSi (Sonata-based) admin data grid, where editors need explicit Move up / Move down buttons instead of drag-and-drop — typically because the frontend renders a nested menu or an ordered list from a position column. The bundle's job is exactly: render two row actions, dispatch a move on the entity/repository, redirect back to the grid.


Implementation Patterns

Entity contract

The bundle needs a way to reorder siblings. Two shapes work; pick one deliberately:

Adjacency list + integer position — simplest, but you own the renumbering:

class Category
{
    /** @ORM\Column(type="integer") */
    private int $position = 0;

    /** @ORM\ManyToOne(targetEntity="Category", inversedBy="children") */
    private ?Category $parent = null;

    public function moveUp(): void { $this->position--; }
    public function moveDown(): void { $this->position++; }
}

Doctrine tree (Gedmo Tree / NestedTreeRepository) — the safer bet if the bundle was written with moveUp/moveDown repository calls in mind. Verify by grepping the vendor source for the method names it actually calls:

grep -rn "moveUp\|moveDown\|getTree\|sort" vendor/fsi/admin-tree-bundle/src

Match your entity to whatever it calls; do not assume.

Wiring the actions into a grid

The integration point is the data grid action column. In FSi/Sonata style:

$datagrid->addColumn('move', new Column\ActionColumn([
    'label'  => '',
    'actions' => [
        'move_up'   => new \FSi\Bundle\AdminTreeBundle\Action\MoveUpAction(),
        'move_down' => new \FSi\Bundle\AdminTreeBundle\Action\MoveDownAction(),
    ],
]));

Class names and option keys vary by version — read Resources/views for the Twig partial that renders them, then mirror its expectations exactly.

Grid configuration that must match the move

  • Order by position ascending in the grid's data source query, otherwise the buttons appear to do nothing.
  • Disable user sorting on the position column — a manual-order feature and arbitrary sort are mutually exclusive.
  • Scope the query to one tree/root if you support multiple root categories, or moves will swap positions across unrelated trees.

Workflow tips

  • Put the move actions in a dedicated action column, not batch actions; per-row is the intent.
  • Guard the first/last sibling: have the action (or its Twig template) hide the button when there is no previous/next sibling, and re-check server-side — a stale grid will happily submit an out-of-range move.
  • Always redirect back to the exact grid URL (page, filters, sort) after the move, not to a bare list route. Capture the referer/grid parameters in the action and pass them through.
  • Add a functional test that moves an item and asserts positions remain contiguous from 0..n-1. This is the one test that catches 90% of regressions, especially with the nested-set variant where Doctrine caches tree state in the entity manager.
  • Wrap the move in a transaction and clear()/refresh() the related entities afterwards.

Gotchas and Tips

Maintenance risk is the headline. Zero stars, zero dependents, no release since 2020 means: no CVE triage, no Symfony-version catch-up, no Stack Overflow answers. Treat the dependency as frozen. Fork it into your VCS, tag it 1.0.0-fork.1, and use repositories: { vcs: ... } in composer.json so the rest of your team installs your patched copy. Add the fork to your dependency-review watchlist.

Version constraints are the first wall. Verify with composer why-not fsi/admin-tree-bundle before assuming it's installable. If you are on Symfony 6/7, porting is usually small (bundle class, DI config, a Twig include, one action class) — often faster than making the package resolve.

Nested-set vs adjacency list confusion is the top functional bug. With Gedmo Tree, "up/down" means moving among siblings of the same parent; with a plain position column it means swapping numbers. Mixing the two (e.g. position field plus a lft/rgt tree) produces duplicate positions. Pick one and delete the other from the entity.

Pagination breaks the mental model. On page 2, "move up" moves the row into page 1. Editors report this as a bug. Either disable pagination for the tree grid, or show a "first page only" hint, or implement move-across-boundary handling in your action.

GET-based actions and CSRF. Row action links are almost always plain anchors. If the bundle's actions are GET routes that mutate, you have a CSRF-by-link issue (crawlers, prefetchers, browser back-buttons). Either accept it (internal admin, auth required) or, better, wrap moves in a POST form rendered in the action column.

Missing/stale position data. On legacy rows where position is null or duplicated, moves appear random. Run a one-off backfill script before enabling the buttons, and add a DB unique index on (parent_id, position) if your workflow allows it.

Sibling scope and multi-root trees. If your entity has two or more "container" types (e.g. Categories and Tags both ordered), make sure each action is registered only on the right grid and that the move query filters by the correct parent/root.

Translations and translatable fields. FSi bundles often pair with prezent/doctrine-translatable. Moving a translated entity is fine, but if your grid sorts by a translated label, the visible order won't match position. Sort by position only.

Extension points you actually have:

  • Decorate or subclass the action classes and register your version with a higher priority / service override — the cleanest way to add audit logging, permission checks, or cache invalidation on move.
  • Override the Twig template via the standard bundle resource-override path (templates/bundles/FSiAdminTreeBundle/...) to change icon markup or hide disabled buttons.
  • Event hooks: dispatch your own tree.moved event from the action so frontend caches and search indexes invalidate.

Permissions. Check whether the actions implement getRole() (FSi's action interface does). If not, add one in your subclass — moving nodes is a write operation and should require EDIT, not just view access.

Debugging recipe when a move does nothing: (1) confirm the route fires in the profiler; (2) dump the entity's position and parent before/after in the action; (3) check the grid's ORDER BY; (4) check the entity manager's unit of work — nested-set bundles frequently need an explicit flush() plus clear() before the redirect.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package