Weave Code
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

Doctrine Sort Bundle Laravel Package

chamber-orchestra/doctrine-sort-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle Add to composer.json:

    "require": {
        "chamber-orchestra/doctrine-sort-bundle": "^1.0"
    }
    

    Run composer require chamber-orchestra/doctrine-sort-bundle.

  2. Enable in Symfony Register in config/bundles.php:

    return [
        // ...
        ChamberOrchestra\SortBundle\SortBundle::class => ['all' => true],
    ];
    
  3. Configure an Entity Use the [Sortable] attribute on your Doctrine entity:

    use ChamberOrchestra\SortBundle\Attribute\Sortable;
    
    #[ORM\Entity]
    #[Sortable(group: 'menu_items')] // Define a sort group
    class MenuItem
    {
        #[ORM\Column(type: 'integer')]
        private ?int $position = null;
    
        // ...
    }
    
  4. First Use Case Reorder items via a form or API, then persist:

    $menuItem->setPosition(5); // Manually set or use a drag-and-drop UI
    $entityManager->flush(); // Bundle auto-recalculates positions in the group
    

Implementation Patterns

Core Workflows

  1. Grouped Sorting

    • Entities with the same #[Sortable(group: '...')] are auto-sorted by position on flush().
    • Example: A Playlist entity with #[Sortable(group: 'user_playlists')] will recalculate positions for all items in that group.
  2. Manual Position Updates

    • Set position directly (e.g., after drag-and-drop):
      $item->setPosition($newPosition);
      $entityManager->flush(); // Triggers recalculation
      
    • Avoid manual position gaps; let the bundle handle gaps on save.
  3. Second-Level Cache Integration

    • Enable cache for sorted queries (e.g., findByGroup()):
      # config/packages/sort.yaml
      chamber_orchestra_sort:
        cache: true
        cache_lifetime: 3600 # 1 hour
      
    • Cache keys are auto-generated per group (e.g., sort:menu_items).
  4. Querying Sorted Results Use the repository method:

    $sortedItems = $menuItemRepo->findByGroup('menu_items', $direction = 'ASC');
    

Integration Tips

  • Symfony Forms Bind a position field to your form for manual input:

    $builder->add('position', HiddenType::class);
    

    Use JavaScript (e.g., SortableJS) for drag-and-drop UIs.

  • APIs Return sorted data directly:

    return $this->getDoctrine()->getRepository(MenuItem::class)
        ->findByGroup('menu_items', 'ASC');
    
  • Migrations Add position column if missing:

    $this->addSql('ALTER TABLE menu_items ADD position INT');
    
  • Testing Mock the SortEvent subscriber to test recalculation logic:

    $this->entityManager->getEventManager()->addEventSubscriber(
        new SortEventSubscriber($entityManager)
    );
    

Gotchas and Tips

Pitfalls

  1. Missing position Column

    • Error: Column 'position' not found on flush.
    • Fix: Add the column to your entity and database schema.
  2. Cache Stale Data

    • Issue: Sorted results don’t update after manual position changes.
    • Fix: Clear the cache manually or set cache_lifetime to 0 for testing:
      chamber_orchestra_sort:
        cache: true
        cache_lifetime: 0 # Disable cache
      
  3. Group Collisions

    • Problem: Two entities with the same group value but different classes.
    • Fix: Use fully qualified group names (e.g., menu_items_admin) or override the getSortGroup() method in your entity.
  4. Performance with Large Groups

    • Warning: Recalculating positions for 10,000+ items may block the event loop.
    • Workaround: Use a background job (e.g., Symfony Messenger) to defer recalculation.

Debugging

  • Enable SQL Logging Add to config/packages/dev/doctrine.yaml:

    doctrine:
      dbal:
        logging: true
    

    Check for UPDATE queries on the position column during flush().

  • Event Subscriber Debugging Dump the SortEvent payload to verify group/position logic:

    public function onFlush(SortEvent $event): void
    {
        dump($event->getGroup(), $event->getEntities());
    }
    

Extension Points

  1. Custom Recalculation Logic Override the default gap-filling algorithm by extending SortEventSubscriber:

    use ChamberOrchestra\SortBundle\Event\SortEvent;
    
    class CustomSortSubscriber extends SortEventSubscriber
    {
        public function onFlush(SortEvent $event): void
        {
            $this->recalculatePositions($event->getGroup(), $event->getEntities(), 'CUSTOM_LOGIC');
        }
    }
    

    Register it in services.yaml:

    services:
        App\EventSubscriber\CustomSortSubscriber:
            tags: [doctrine.event_subscriber]
    
  2. Dynamic Group Names Use a method to generate group names dynamically:

    #[Sortable(group: 'menu_items')]
    class MenuItem
    {
        public function getSortGroup(): string
        {
            return 'menu_items_' . $this->getUserId();
        }
    }
    
  3. Non-Integer Positions Extend the bundle to support strings or UUIDs for position by modifying the Sortable attribute and subscriber logic.

Configuration Quirks

  • Cache Provider The bundle defaults to Symfony’s cache system. For custom providers (e.g., Redis), implement Psr\Cache\CacheItemPoolInterface and configure:

    chamber_orchestra_sort:
        cache_pool: app.redis_cache
    
  • Attribute Overrides The #[Sortable] attribute cannot be overridden per environment. Use multiple entities or dynamic group names instead.

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.
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager