Enhance HasSortableRelations for inline drag-and-drop sorting#235
Conversation
Supports the Winter CMS inline list/relation reordering feature (wintercms/winter#1472): - Fix incorrect `Winter\Sorm` namespace import typo - Make `getSortableRelations()` public and add `isSortableRelation()` - Add a `$sessionKey` parameter to `setRelationOrder()` so reordering can write to `deferred_bindings.pivot_data` when a relation is deferred - Default empty `$itemOrders` to a sequential 1..N range (instead of using the record ids as order values) - Inject a pivot-table-qualified `order` clause and the pivot sort column for belongsToMany, morphToMany and morphedByMany relations - Auto-append uses `max(sort_order) + 1` via the pivot query so a defined order clause cannot skew the result and non-contiguous orders are handled - Preserve an explicit sort order (including 0) on attach, so committing deferred bindings that carry a pivot sort order no longer overwrites it Adds DB-backed tests and a SortableArticle fixture covering attach ordering, reordering, deferred writes, zero/explicit orders and the pivot/order injection for all three pivot relation types. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Companion Winter CMS PR: wintercms/winter#1491 (implements the list/relation reordering UI on top of these trait changes). Tracking issue: wintercms/winter#1472. |
Implements inline reordering of records directly in a List widget and in RelationController relation lists, closing a longstanding request (#1472). Requires the companion Storm changes (wintercms/storm#235). Lists widget: - New `sortable` / `sortOrderColumn` config. When enabled, a drag-handle column is shown, column-header sorting is disabled (every column is forced non-sortable so `getSortColumn()` returns false and the model / relation's own order is preserved), and pagination is disabled so every record is shown in order. - `onReorder()` AJAX handler validates that submitted record ids are within the current query scope, then fires a `list.reorder` event. - `getRecordSortOrder()` reads the sort value from a record via the configured column path (e.g. `sort_order` or `pivot[sort_order]`). ListController: - `sortable: true` in the list config validates the model uses the Sortable trait and binds `list.reorder` to `setSortableOrder()`. RelationController: - `view[sortable]: true` validates the parent uses HasSortableRelations and declares the relation, then binds `list.reorder` to `setRelationOrder()` (passing the session key in deferred mode). - In deferred mode `withDeferred()` builds the query in orphan mode with no pivot join, so the pivot order clause is stripped and the records are ordered in PHP from `deferred_bindings.pivot_data` (overlaid on any committed pivot rows) via a `list.extendRecords` binding. The Lists widget stays relation-agnostic. Assets: minimal, additive SortableJS initializer (the existing jQuery list widget is untouched) plus styles; SortableJS is vendored at `js/lib/sortable.min.js` and added to package.json. Tests: ListsSortableTest covers the sortable widget config, drag-handle column, `getRecordSortOrder` (direct and pivot paths), and `onReorder` event firing + query-scope validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements inline reordering of records directly in a List widget and in RelationController relation lists, closing a longstanding request (wintercms/winter#1472). Requires the companion Storm changes (wintercms/storm#235). Lists widget: - New `sortable` / `sortOrderColumn` config. When enabled, a drag-handle column is shown, column-header sorting is disabled (every column is forced non-sortable so `getSortColumn()` returns false and the model / relation's own order is preserved), and pagination is disabled so every record is shown in order. - `onReorder()` AJAX handler validates that submitted record ids are within the current query scope, then fires a `list.reorder` event. - `getRecordSortOrder()` reads the sort value from a record via the configured column path (e.g. `sort_order` or `pivot[sort_order]`). ListController: - `sortable: true` in the list config validates the model uses the Sortable trait and binds `list.reorder` to `setSortableOrder()`. RelationController: - `view[sortable]: true` validates the parent uses HasSortableRelations and declares the relation, then binds `list.reorder` to `setRelationOrder()` (passing the session key in deferred mode). - In deferred mode `withDeferred()` builds the query in orphan mode with no pivot join, so the pivot order clause is stripped and the records are ordered in PHP from `deferred_bindings.pivot_data` (overlaid on any committed pivot rows) via a `list.extendRecords` binding. The Lists widget stays relation-agnostic. Assets: minimal, additive SortableJS initializer (the existing jQuery list widget is untouched) plus styles; SortableJS is vendored at `js/lib/sortable.min.js` and added to package.json. Tests: ListsSortableTest covers the sortable widget config, drag-handle column, `getRecordSortOrder` (direct and pivot paths), and `onReorder` event firing + query-scope validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 52 minutes and 49 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
Walkthrough
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Related: wintercms/winter#1491, wintercms/storm#235, wintercms/winter#1472
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Closes: #1472 Related: wintercms/storm#235, wintercms/docs#260
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Closes: wintercms/winter#1472 Related: wintercms/storm#235, wintercms/docs#260
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests