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

Sortable Behavior Bundle Laravel Package

runroom-packages/sortable-behavior-bundle

Runroom Sortable Behavior Bundle adds easy sortable behavior to Symfony and Doctrine entities. Use a position field and trait to make records reorderable, then manage ordering in your admin or custom UI. Great for categories, menus, and any list that needs drag-and-drop sorting.

View on GitHub
Deep Wiki
Context7

Getting Started

The bundle layers a Sonata Admin UI (drag‑and‑drop + move action) on top of Doctrine Extensions' Sortable behaviour. It does not implement sorting itself — so step one is always getting Gedmo wired up correctly.

  1. Install the dependencies together. In practice you need the bundle plus gedmo/doctrine-extensions and, almost always, stof/doctrine-extensions-bundle:
    composer require runroom-packages/sortable-behavior-bundle gedmo/doctrine-extensions stof/doctrine-extensions-bundle
    
  2. Register the bundle in config/bundles.php:
    Runroom\SortableBehaviorBundle\RunroomSortableBehaviorBundle::class => ['all' => true],
    
  3. Enable the Sortable listener under stof_doctrine_extensions (this is the step people forget, and the symptom is that position is always null):
    stof_doctrine_extensions:
        orm:
            default:
                sortable: true
    
  4. Annotate your entity. Add a nullable position integer column and the Gedmo annotations:
    /**
     * @Gedmo\Sortable
     */
    class Article
    {
        /** @Gedmo\SortablePosition */
        private ?int $position = null;
    }
    
    Add @Gedmo\SortableGroup on any column that scopes the list (category, locale, parent, …).
  5. Wire up the admin. Attach the bundle's sortable admin trait/extension to your AbstractAdmin and add position to configureListFields(). Point the move route at the bundle's controller (or override it) and include the provided JS asset in sonata_admin.yaml.
  6. First use case: open the Sonata list for your entity, drag a row, refresh — the position should persist and the list should be ordered by position on subsequent loads. If that works end‑to‑end, the plumbing is right; everything else is refinement.

Look first at the bundle's Resources/ directory: the controller action, the Twig row template, and the JS file tell you exactly what the front end expects (route name, required data attributes, HTTP method). That is your integration contract.

Implementation Patterns

Plain drag‑and‑drop list. The canonical setup is: position as a list field, the bundle's JS loaded globally for the admin, and a move route declared under the admin. Every row carries its entity id and the JS posts a direction/delta to the controller, which delegates to Gedmo's SortableListener and reorders.

Grouped lists. When items belong to a parent (e.g. articles per category, blocks per page), annotate the parent with @Gedmo\SortableGroup. Two things then matter:

  • The admin's query must be filtered to a single group; otherwise the drag endpoint will compute the wrong neighbour.
  • The move endpoint needs the group column available (usually via the admin's getSubject() and the route parameters), so the listener can resolve positions within the group.

Programmatic reordering. Fixtures, imports, and console commands should not poke at position directly. Use the Gedmo listener or repository helpers so the listener can shift siblings:

$sortableListener->setSortablePosition($entity, 3);

or set position and flush inside a transaction so a failure does not leave gaps/duplicates.

Front‑end ordering. Once positions are maintained, expose ordering in your own queries with ORDER BY position ASC (or a group filter + position). Because positions can have gaps after deletes, never assume position === 0..n-1; only use it for ordering.

Extending the admin UI. The bundle is meant to be subclassed, not patched. Typical customisations: add up/down buttons for accessibility alongside drag‑and‑drop, add a "move to top / bottom" action, or replace the JS with a SortableJS-based variant while reusing the controller action.

Multiple sortable admins in one app. Each admin gets its own route name and template block. If you use a custom admin controller, keep it extendsing the bundle's controller rather than duplicating the move logic — the listener call plus position recalculation is the part you don't want to rewrite.

Integrating with Sonata filters/pagination. Sorting is defined on the full table (or full group), not on the filtered result set. If your admins offer filters that change the visible ordering, either hide drag‑and‑drop when a filter is active, or explicitly document that reordering is global. With pagination, ensure the move endpoint receives the page offset/limit (or performs a global move) so dragging across page boundaries doesn't produce surprising jumps.

Gotchas and Tips

  • Silent no‑op when the listener is off. position staying null after persist is almost always a missing sortable: true in stof_doctrine_extensions. Check that first before touching the bundle.
  • Nullable integer required. position must be nullable (or have a default), because Gedmo assigns it on prePersist. A NOT NULL column without a default will fail on insert.
  • Position is set on persist, not construct. Newly instantiated entities have no position; anything that sorts an unsaved collection will appear to ignore the field.
  • Unique constraints on (group, position) break reorders. The listener shifts rows one at a time and can transiently violate a unique index unless the move runs inside a transaction with deferred constraints (or without the index). This is the most common production incident with sortable behaviour.
  • Route + method + CSRF are part of the contract. The JS posts to the move route with a specific HTTP method and CSRF token. If you rename the admin route, override the template, or tighten security.yaml, the drag silently stops working. Watch the browser network tab before blaming the controller.
  • Role/ACL checks on the move action. Because the controller is a separate action, it does not automatically inherit your admin isGranted logic — add the check explicitly, otherwise any authenticated user can reorder anyone's data.
  • Filters vs. ordering. Reordering in a filtered view reorders the underlying global sequence; users expect otherwise. Either document it or disable dragging while filters apply.
  • Deletes leave gaps. Gedmo does not compact positions. Code comparing position + 1 to the next row will be wrong; always sort by position, id.
  • N+1 through the group field. If the group is a relation (category, parent page), make sure the admin list query joins/eager‑loads it, or the sortable rows will hammer the database.
  • Testing. Run your functional tests against a configured Gedmo listener (a test kernel with stof enabled), and set explicit position values in fixtures so tests don't depend on insertion order.
  • Maintenance risk. The package has a very small user base (11 stars, no dependents), so treat it as a thin UI layer: keep your reordering logic reachable from your own services, and be prepared to inline or replace it if Sonata/Gedmo upgrades outpace it. Overriding the Twig template and controller is the intended extension point — copy them into your app rather than forking the package.
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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo