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

Tree Bundle Laravel Package

awaresoft/tree-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require awaresoft/tree-bundle
    

    Note: Follow the README for local development setup if modifying the bundle.

  2. Enable the Bundle: Add to config/bundles.php:

    return [
        // ...
        Awaresoft\TreeBundle\AwaresoftTreeBundle::class => ['all' => true],
    ];
    
  3. First Use Case: Create a tree-aware entity (e.g., Category):

    use Awaresoft\TreeBundle\Model\TreeInterface;
    use Doctrine\ORM\Mapping as ORM;
    
    #[ORM\Entity]
    class Category implements TreeInterface
    {
        #[ORM\Id, ORM\GeneratedValue, ORM\Column]
        private ?int $id = null;
    
        #[ORM\Column]
        private ?string $name = null;
    
        #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
        private ?self $parent = null;
    
        #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
        private Collection $children;
    
        // Implement TreeInterface methods (getParent(), getChildren(), etc.)
    }
    
  4. Register Admin (Sonata Integration): Extend SonataAdmin and configure tree behavior in services.yaml:

    services:
        App\Admin\CategoryAdmin:
            arguments: [~, App\Entity\Category, Awaresoft\TreeBundle\Admin\TreeAdminExtension]
            tags: [sonata.admin]
    
  5. Run Migrations:

    php bin/console doctrine:migrations:diff
    php bin/console doctrine:migrations:migrate
    

Implementation Patterns

Common Workflows

1. Tree Entity Setup

  • Pattern: Use TreeInterface for all tree-aware entities.
  • Example:
    class ProductCategory implements TreeInterface
    {
        // ... (implement TreeInterface)
        public function getParent(): ?self { /* ... */ }
        public function getChildren(): Collection { /* ... */ }
        public function getRoot(): ?self { /* ... */ }
        public function getLevel(): int { /* ... */ }
    }
    
  • Tip: Use traits like Awaresoft\TreeBundle\Model\TreeTrait to reduce boilerplate.

2. Sonata Admin Integration

  • Pattern: Extend TreeAdminExtension in your admin class.
  • Example:
    use Awaresoft\TreeBundle\Admin\TreeAdminExtension;
    
    class CategoryAdmin extends AbstractAdmin
    {
        protected function configureListFields(ListMapper $listMapper): void
        {
            $listMapper
                ->add('name')
                ->add('parent.name', 'string', ['template' => 'AwaresoftTreeBundle:Admin:field_tree.html.twig'])
                ->add('_action', 'actions', [
                    'actions' => [
                        'edit' => [],
                        'delete' => [],
                    ],
                ]);
        }
    
        public function getTemplate($name): string
        {
            if ($name === 'layout') {
                return '@AwaresoftTreeBundle/Admin/layout.html.twig';
            }
            return parent::getTemplate($name);
        }
    }
    

3. Tree Queries

  • Pattern: Use TreeManager for common tree operations.
  • Example:
    $treeManager = $this->get('awaresoft_tree.manager.tree');
    $rootNodes = $treeManager->getRootNodes(Category::class);
    $subtree = $treeManager->getSubtree($rootNode);
    

4. Custom Tree Logic

  • Pattern: Override TreeManager or create custom services.
  • Example:
    // services.yaml
    App\Service\CustomTreeService:
        arguments: ['@awaresoft_tree.manager.tree']
        tags: [awaresoft_tree.tree_manager]
    

Integration Tips

  1. Doctrine Events: Use prePersist, preUpdate, and preRemove to maintain tree integrity:

    #[ORM\PrePersist]
    #[ORM\PreUpdate]
    public function updateLevel(): void
    {
        if ($this->parent) {
            $this->level = $this->parent->level + 1;
        } else {
            $this->level = 0;
        }
    }
    
  2. Twig Templates: Override default templates in templates/AwaresoftTreeBundle/:

    {# templates/AwaresoftTreeBundle/Admin/field_tree.html.twig #}
    {% extends '@AwaresoftTreeBundle/Admin/field_tree.html.twig' %}
    {% block field_tree %}
        {{ parent() }}
        <small>Custom suffix: {{ object.level }}</small>
    {% endblock %}
    
  3. API Responses: Serialize trees with custom DTOs:

    class CategoryDTO
    {
        public function __construct(
            private string $id,
            private string $name,
            private ?array $children = []
        ) {}
    
        public function toArray(): array
        {
            return [
                'id' => $this->id,
                'name' => $this->name,
                'children' => array_map(fn($child) => (new self(...))->toArray(), $this->children),
            ];
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Circular References:

    • Issue: Forgetting to set inversedBy or mappedBy in Doctrine relations can cause infinite loops.
    • Fix: Always define bidirectional relations explicitly:
      #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
      private ?self $parent = null;
      
      #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
      private Collection $children;
      
  2. Level Calculation:

    • Issue: Manual level updates can break if not handled in Doctrine events.
    • Fix: Use TreeTrait or override lifecycle callbacks:
      use Awaresoft\TreeBundle\Model\TreeTrait;
      
      class Category implements TreeInterface
      {
          use TreeTrait;
          // ...
      }
      
  3. Sonata Admin Caching:

    • Issue: Tree fields may not update immediately in the admin panel.
    • Fix: Clear Sonata cache after changes:
      php bin/console sonata:cache:clear
      
  4. Performance with Large Trees:

    • Issue: Querying deep subtrees can be slow.
    • Fix: Use TreeManager::getSubtree() with depth limits or lazy-loading:
      $subtree = $treeManager->getSubtree($rootNode, 2); // Limit to 2 levels
      

Debugging Tips

  1. Tree Structure Validation: Use TreeManager to debug:

    $treeManager = $this->get('awaresoft_tree.manager.tree');
    $root = $treeManager->getRootNodes(Category::class)->first();
    $subtree = $treeManager->getSubtree($root);
    dump($subtree); // Inspect structure
    
  2. Doctrine Query Logging: Enable SQL logging to check tree queries:

    # config/packages/dev/doctrine.yaml
    doctrine:
        dbal:
            logging: true
            profiling: true
    
  3. Symlink Issues:

    • If modifying the bundle locally, ensure symlinks are correct:
      ln -s /path/to/project/src/Awaresoft /path/to/vendor/awaresoft/tree-bundle/src/Awaresoft
      
    • Clear cache after changes:
      php bin/console cache:clear
      

Extension Points

  1. Custom Tree Managers: Extend Awaresoft\TreeBundle\Manager\TreeManager for custom logic:

    class CustomTreeManager extends TreeManager
    {
        public function getCustomSubtree(TreeInterface $node): array
        {
            // Custom logic
            return $this->getSubtree($node);
        }
    }
    

    Register in services.yaml:

    services:
        App\Manager\CustomTreeManager:
            parent: awaresoft_tree.manager.tree
            arguments: ['@doctrine.orm.entity_manager']
    
  2. Event Subscribers: Listen to tree events (e.g., TreeNodeMovedEvent):

    use Awaresoft\TreeBundle\Event\TreeNodeMovedEvent;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class TreeEventSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents(): array
        {
            return [
                TreeNodeMovedEvent::NAME => 'onTreeNodeMoved',
            ];
        }
    
        public function onTreeNodeMoved(TreeNodeMovedEvent $event): void
        {
            // Custom logic (e
    
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels