Install Dependencies:
composer require stof/doctrine-extensions-bundle sonata-project/admin-bundle redcode/tree-bundle
Ensure StofDoctrineExtensionsBundle and SonataAdminBundle are properly configured.
Enable Nested Set Behavior:
Add Gedmo\Tree\TreeableTrait to your entity and configure the TreeExtension in config.yml:
stof_doctrine_extensions:
orm:
default:
tree: true
Create a Tree Entity:
use Gedmo\Mapping\Annotation as Gedmo;
class Category
{
use TreeableTrait;
// Your fields...
}
Extend Admin Class:
use RedCode\TreeBundle\Admin\AbstractTreeAdmin;
class CategoryAdmin extends AbstractTreeAdmin
{
protected $baseRouteName = 'category';
protected $baseRoutePattern = 'category';
}
Register Admin as Service:
app.admin.category:
class: AppBundle\Admin\CategoryAdmin
arguments: [~, AppBundle\Entity\Category, AppBundle:CategoryAdmin, 'name']
tags:
- {name: sonata.admin, manager_type: orm, group: Content, label: Category}
Clear Cache:
php bin/console cache:clear
Display a hierarchical category tree in Sonata Admin with drag-and-drop functionality. The tree loads asynchronously, making it suitable for large datasets.
Tree Configuration:
AbstractTreeAdmin to inherit tree-specific behaviors (e.g., configureTree(), configureFormFields()).getTreeField() to specify the field displayed in the tree (e.g., 'name').Customizing Tree Behavior:
TreeAdminController to modify tree actions (e.g., moveNodeAction, deleteNodeAction).class CategoryAdminController extends TreeAdminController
{
public function moveNodeAction(Request $request, $id, $leftId, $position)
{
// Custom logic before moving nodes
return parent::moveNodeAction($request, $id, $leftId, $position);
}
}
Form Field Customization:
configureFormFields() to add/remove fields or customize their behavior:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', 'text')
->add('parent', 'sonata_type_model', [
'required' => false,
'by_reference' => false,
'class' => 'AppBundle\Entity\Category',
]);
}
Tree-Specific Actions:
move, delete, or create via Sonata’s CRUD interface.configureListFields() or configureTabMenu():
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('name')
->add('_action', 'actions', [
'actions' => [
'move' => [],
'delete' => [],
],
]);
}
Asynchronous Loading:
getTreeData() if needed:
protected function getTreeData($id = null)
{
$queryBuilder = $this->getModelManager()->createQueryBuilder('o');
return $queryBuilder->getQuery()->getResult();
}
Nested Set Behavior Conflicts:
Gedmo\Tree\TreeableTrait for hierarchical data. Mixing with other tree behaviors (e.g., MaterializedPath) may cause inconsistencies.Parent-Child Relationships:
parent field in your entity must be of type Category (or your entity class) and annotated with @Gedmo\TreeParent./**
* @Gedmo\TreeParent
*/
protected $parent;
Routing Conflicts:
/admin. Ensure no other bundles or custom routes conflict with:
redcode_tree_move_noderedcode_tree_delete_nodeTree Field Misconfiguration:
arguments array in the admin service must include the tree field name (4th argument). Omitting it breaks the tree display.'name'):
arguments: [~, AppBundle\Entity\Category, AppBundle:CategoryAdmin, 'name']
Asynchronous Loading Issues:
getTreeData() method returns valid data.TreeExtension is enabled in config.yml.dd($this->getTreeData()) or check Symfony logs.Enable Doctrine Debugging:
Add to config.yml to log tree queries:
doctrine:
orm:
logging: true
Check jsTree Console Errors:
Open browser dev tools (F12) and inspect the Network tab for failed AJAX requests to /admin/redcode_tree/....
Validate Entity Metadata: Run:
php bin/console doctrine:schema:validate
Ensure the TreeableTrait is recognized.
Clear Cache Aggressively: After changes, run:
php bin/console cache:clear --env=prod
php bin/console cache:pool:clear cache.apcu
Custom Tree Templates:
Override the default jsTree template by extending the bundle’s Resources/views/CRUD/tree.html.twig:
{% extends 'RedCodeTreeBundle:CRUD:tree.html.twig' %}
{% block tree_js %}
{{ parent() }}
<script>
// Custom jsTree initialization
</script>
{% endblock %}
Add Custom Node Metadata:
Extend getTreeData() to include additional fields in the tree response:
protected function getTreeData($id = null)
{
$queryBuilder = $this->getModelManager()->createQueryBuilder('o');
$queryBuilder->select('o', 'partial o.{id, name, lft, rgt, level}');
return $queryBuilder->getQuery()->getResult();
}
Integrate with Sonata Blocks:
Use the tree in a Sonata block by extending Sonata\BlockBundle\Block\BlockContextInterface and embedding the tree via:
{{ render(url('redcode_tree_tree', {'id': node.id})) }}
Add Bulk Actions:
Extend the configureListFields() to include bulk tree operations:
$listMapper->add('_action', 'actions', [
'actions' => [
'bulk_move' => ['template' => 'RedCodeTreeBundle:CRUD:bulk_move.html.twig'],
],
]);
How can I help you explore Laravel packages today?