outl1ne / nova-sortable

This Laravel Nova package allows you to reorder models in a Nova resource's index view using drag & drop.
MIT License
283 stars 120 forks source link

Using the "Move to Top" or "Move to End" buttons does not work as expected and creates incorrect sort order values #179

Open connecteev opened 1 year ago

connecteev commented 1 year ago

Problem:

Using the "Move to Top" or "Move to End" buttons does not work as expected and creates incorrect sort order values after the sorting is done. Note: Using Drag-and-Drop to sort / reorder rows in a Nova resource does not cause this problem (though auto-refresh is broken and the page has to be manually refreshed).

Bug Description:

Model relationships:

Goal:

To allow CourseSections to be sortable WITHIN a Course, using drag-and-drop on the Course resource Detail page in Nova. All the CourseSections under /admin/resources/courses/1 should be sortable. In the example below, there are 4 sections in course 1, and 4 sections in course 2. The sort values for the sections should be between 1-4 at all times.

Steps:

  1. I start with the following:
    • course id # 1 with 4 sections, in this order
    • section id # 1 (order:1)
    • section id # 2 (order:2)
    • section id # 3 (order:3)
    • section id # 4 (order:4)
    • course id # 2 with 4 sections, in this order
    • section id # 5 (order:1)
    • section id # 6 (order:2)
    • section id # 7 (order:3)
    • section id # 8 (order:4)

Database screenshot - BEFORE (course_sections table): image

  1. Then, use the "Move to Top" or "Move to End" buttons a few times
  2. The sort order gets messed up.

Video Demo of the problem:

https://github.com/outl1ne/nova-sortable/assets/64816/5389ad4d-d93b-4ec4-819a-8ea107e49b9a

Database screenshot - AFTER (course_sections table): Note that we only updated /admin/resources/courses/1 . Although we did not go to /admin/resources/courses/2 and update the sort for course # 2, the order values are now messed up: image

The sort values for the sections should be between 1-4 at all times. Instead, we see values like 5, 6, and multiple CourseSection (rows in course_sections table) have the same value of 4.

Here is my code for the models and Nova resources:

+++ b/app/Models/CourseContent/CourseSection.php
@@ -8,10 +8,19 @@
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\HasMany;
+use Spatie\EloquentSortable\Sortable;
+use Spatie\EloquentSortable\SortableTrait;

-class CourseSection extends Model
+class CourseSection extends Model implements Sortable
 {
-    use HasFactory;
+    use HasFactory, SortableTrait;
+
+    public array $sortable = [
+        'order_column_name' => 'section_order',
+        'sort_when_creating' => true,
+        'sort_on_has_many' => true,
+        'nova_order_by' => 'ASC',
+    ];

     public function course(): BelongsTo
     {
diff --git a/app/Nova/CourseSection.php b/app/Nova/CourseSection.php
index cc2c8fa..c5cace3 100644
--- a/app/Nova/CourseSection.php
+++ b/app/Nova/CourseSection.php
@@ -11,9 +11,21 @@
 use Laravel\Nova\Fields\Slug;
 use Laravel\Nova\Fields\Text;
 use Laravel\Nova\Http\Requests\NovaRequest;
+use Outl1ne\NovaSortable\Traits\HasSortableRows;

 class CourseSection extends Resource
 {
+    use HasSortableRows;
+
+    // You can disable sorting on a per-request or per-resource basis by overriding the canSort() on the Resource method
+    public static function canSort(NovaRequest $request, $resource)
+    {
+        return true;
+    }
+
     /**
      * The model the resource corresponds to.
      *
@@ -66,10 +78,11 @@ public function fields(NovaRequest $request)
                 ->hideFromIndex()
             ,

-            Number::make('Order')
+            Number::make('Order', 'section_order')
                 ->min(1)
                 ->max(20)
                 ->step(1)
+                ->sortable()
                 ->showWhenPeeking()
                 ->rules('required')
diff --git a/composer.json b/composer.json
index e49599e..0345389 100644
--- a/composer.json
+++ b/composer.json
@@ -18,6 +18,7 @@
         "laravel/tinker": "^2.8",
         "manogi/nova-tiptap": "^3.2",
         "meilisearch/meilisearch-php": "^1.1",
+        "outl1ne/nova-sortable": "^3.4",
         "spatie/laravel-markdown": "^2.3",
         "spatie/shiki-php": "^1.3",
         "stripe/stripe-php": "^10.16",
hvervest commented 1 month ago

Maybe not what you expect to happen, but not really a bug. This package uses the https://github.com/spatie/eloquent-sortable package and when Move To Start is clicked this method is called: https://github.com/spatie/eloquent-sortable/blob/main/src/SortableTrait.php#L152

On line 167 all other order columns are incremented after updating the order column of the clicked model. The sort order still is correct, but the sort order values may not be what you expect them to be.

If you want to set a new order for all the records after moving one to the top, use this: https://github.com/spatie/eloquent-sortable/blob/main/src/SortableTrait.php#L44

Example code:

use Spatie\EloquentSortable\SortableTrait;

class Example extends Model implements Sortable
{
    use SortableTrait {
        moveToStart as traitMoveToStart;
    }

  public function moveToStart(): static
  {
      $this->traitMoveToStart();

      $this->setNewOrder($this->buildSortQuery()->ordered()->pluck($this->primaryKey)->toArray());

      return $this;
  }
}