Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Categories Reorder with Drag-Drop

Premium
6 min read

Comments & Discussion

FA
Farooq Ahmed ✓ Link copied!

hi dos the 'position' in categories table migration had to be nullable by default.it is showing error when i migrate:fresh and insert seed data

PK
Povilas Korop ✓ Link copied!

I think by default its value is 0, but can't guarantee: what is the actual error test and what seed data are you using?

B
basherdesigns ✓ Link copied!

I made the position default to 9999, so new Categories will be at the end. Or if you default to 0 it will be very first category

RB
Rajesh Budhathoki ✓ Link copied!

While you follow along and encountred some errors as I did, I suggest you to make some amendments. To seed position rows. Amend in CategoryFactory.php:

ublic function definition(): array
    {
        $country = fake()->unique()->country();

        return [
            'name' => $country,
            'slug' => Str::slug($country),
            'position' => fake()->numberBetween(1, 10),
        ];
    }

Import namespace in CategoriesList.php

use Illuminate\Support\Collection;

For better UX and Web accessibility aspect, change buton cursor class to cursor-move in categories-list.blade.php:

<button wire:sortable.handle class="cursor-move">
	// ...                                                 
 </button>
E
Emruardo ✓ Link copied!

I've added the code to Category model. Position has same number as id on seeding or new creation of Category.


    protected static function boot()
    {
        parent::boot();

        static::created(function ($model) {
            $model->position = $model->id;
            $model->save();
        });
    }

RB
Rajesh Budhathoki ✓ Link copied!

Where and how can we dump $list array to see what's inside?

If we paginate 10 the $categories per page, how can we drag and drop 11 category to the first position?

N
Nerijus ✓ Link copied!

You can dump where we update the order. So it would be updateOrder method. To other page you cannot drag and drop.

E
Emruardo ✓ Link copied!

If an item will be positioned in another page than page 1, order number begins from 1. This causes that there are 2 categories with the same position number. The refactoring in CategoriesList helped me to achieve the problem.


class CategoriesList extends Component
{
// ...
	public $currentPage = 1;
	public $perPage = 10;

// ...
public function updateOrder($list)
    {
        foreach ($list as $item) {
            $cat = $this->categories->firstWhere('id', $item['value']);
            $order = $item['order'] + (($this->currentPage - 1) * $this->perPage);
            if ($cat['position'] != $order) {
                Category::where('id', $item['value'])->update(['position' => $order]);
            }
        }
    }
// ...
public function render()
    {
        $cats = Category::orderBy('position')->paginate($this->perPage);
        $links = $cats->links();
        $this->currentPage = $cats->currentPage();
        $this->categories = collect($cats->items());
       // ...
    }
B
basherdesigns ✓ Link copied!

Now how can you move a category that is on the second page to the first page? This seems to not be working.

PK
Povilas Korop ✓ Link copied!

Good question, I don't think it's possible with drag-drop. I guess it's better to build a separate button like "move one page up" or something.

R
RangerCoder99 ✓ Link copied!

Again so many issues following alone had to check the comments for fixes, and this comments were posted a month(s) ago not sure why the tutorial text was not updated. Not trying to be mean just giving honest feedback.

ED
Emre Dikmen ✓ Link copied!

CategoryFactory position will increase in order.

 public function definition(): array
    {
        $name = $this->faker->sentence(5);
        static $position = 1;

        return [
            'name' => $name,
            'slug' => function (array $attributes) {
                return Str::slug($attributes['name']);
            },
            'is_active' => $this->faker->boolean,
            'position' => $position++
        ];
    }

```
ED
Emre Dikmen ✓ Link copied!

Or you can increase the position value this way.

'position' => Category::max('position') + 1

A
augusto-dmh ✓ Link copied!

@Emre Dikem Yep, that's the approach i took too.

A
Alin ✓ Link copied!
    foreach ($list as $item) {
        $cat = $this->categories->firstWhere('id', $item['value']);

        if ($cat['position'] != $item['order']) {
            Category::where('id', $item['value'])->update(['position' => $item['order']]);
        }
    }
			

Why did you not use the variable $cat inside the if? Why did you fetch Category::where...? I assume there are some advantages but i can't figure it out.

M
Modestas ✓ Link copied!

It is used inside the if, but you might be confusing this because it looks like a database query.

The $cat variable comes from a collection, not the databse in this case (it's missing the categories()) and that makes it work with the data that is already loaded as a relationship.

A
Alin ✓ Link copied!

Thank you for your quick reply! Appreciate it, and the whole job you are doing here.