Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Sortable Interface with Mass Update and Deletion Ordering Methods #182

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 85 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,46 @@ Optionally you can publish the config file with:
php artisan vendor:publish --tag=eloquent-sortable-config
```

This is the content of the file that will be published in `config/eloquent-sortable.php`
This is the content of the file that will be published in `config/eloquent-sortable.php`:

```php
return [
/*
* The name of the column that will be used to sort models.
*/
'order_column_name' => 'order_column',

/*
* Define if the models should sort when creating. When true, the package
* will automatically assign the highest order number to a new model
*/
'sort_when_creating' => true,

/*
* Define if the timestamps should be ignored when sorting.
* When true, updated_at will not be updated when using setNewOrder
*/
'ignore_timestamps' => false,
/*
* The name of the column that will be used to sort models.
*/
'order_column_name' => 'order_column',

/*
* Define if the models should sort when creating.
* When true, the package will automatically assign the highest order number to a new model.
*/
'sort_when_creating' => true,

/*
* Define if the models should sort when updating.
* When true, the package will automatically update the order of models when one is updated.
*/
'sort_when_updating' => true,

/*
* Define if the models should sort when deleting.
* When true, the package will automatically update the order of models when one is deleted.
*/
'sort_when_deleting' => true,

/*
* Define if the timestamps should be ignored when sorting.
* When true, `updated_at` will not be updated when using `setNewOrder`, `setMassNewOrder`,
* or when models are reordered automatically during creation, updating, or deleting.
*/
'ignore_timestamps' => false,
];
```

## Usage

To add sortable behaviour to your model you must:

1. Implement the `Spatie\EloquentSortable\Sortable` interface.
2. Use the trait `Spatie\EloquentSortable\SortableTrait`.
3. Optionally specify which column will be used as the order column. The default is `order_column`.
Expand All @@ -91,6 +105,8 @@ class MyModel extends Model implements Sortable
public $sortable = [
'order_column_name' => 'order_column',
'sort_when_creating' => true,
'sort_when_updating' => true,
'sort_when_deleting' => true,
];

// ...
Expand All @@ -113,31 +129,43 @@ $myModel->save(); // order_column for this record will be set to 2
$myModel = new MyModel();
$myModel->save(); // order_column for this record will be set to 3


//the trait also provides the ordered query scope
// The trait also provides the ordered query scope
$orderedRecords = MyModel::ordered()->get();
```

You can set a new order for all the records using the `setNewOrder`-method
### New Sorting Methods

#### Mass Update Ordering

You can set a new order for all the records using the `setMassNewOrder`-method:

```php
$newOrder = [5, 3, 1, 4, 2];
MyModel::setMassNewOrder($newOrder);
```

This will reorder the records in the order specified by `$newOrder`.

#### Setting a New Order for All Records

```php
/**
* the record for model id 3 will have order_column value 1
* the record for model id 1 will have order_column value 2
* the record for model id 2 will have order_column value 3
*/
MyModel::setNewOrder([3,1,2]);
MyModel::setNewOrder([3, 1, 2]);
```

Optionally you can pass the starting order number as the second argument.
Optionally, you can pass the starting order number as the second argument.

```php
/**
* the record for model id 3 will have order_column value 11
* the record for model id 1 will have order_column value 12
* the record for model id 2 will have order_column value 13
*/
MyModel::setNewOrder([3,1,2], 10);
MyModel::setNewOrder([3, 1, 2], 10);
```

You can modify the query that will be executed by passing a closure as the fourth argument.
Expand All @@ -148,11 +176,12 @@ You can modify the query that will be executed by passing a closure as the fourt
* the record for model id 1 will have order_column value 12
* the record for model id 2 will have order_column value 13
*/
MyModel::setNewOrder([3,1,2], 10, null, function($query) {
MyModel::setNewOrder([3, 1, 2], 10, null, function ($query) {
$query->withoutGlobalScope(new ActiveScope);
});
```

#### Setting New Order by Custom Column

To sort using a column other than the primary key, use the `setNewOrderByCustomColumn`-method.

Expand Down Expand Up @@ -184,6 +213,8 @@ MyModel::setNewOrderByCustomColumn('uuid', [
], 10);
```

### Additional Methods for Sorting

You can also move a model up or down with these methods:

```php
Expand Down Expand Up @@ -211,6 +242,36 @@ You can swap the order of two models:
MyModel::swapOrder($myModel, $anotherModel);
```

### Handling Model Updates and Deletions

If you want your model to automatically reorder upon updating or deleting a record, ensure the relevant configuration values (`sort_when_updating`, `sort_when_deleting`) are set to `true` in the configuration file. This will allow your models to maintain the correct order without needing to manually update the ordering each time a change is made.

For example, if `sort_when_updating` is set to `true`, any changes to a model's attributes will automatically adjust the order, ensuring consistency.

In addition to automatic reordering, you can also manually trigger sorting for specific scenarios. Here is an example of how you can manually trigger sorting:

```php
$model = $this->model::query()->find(1);
$model->forceFill(['updated_at' => now()]);
$model->sortables = [1, 2, 3]; // The `sortables` array contains the IDs of the records that need to be reordered.
$model->save();
```

In this scenario, the model is being updated with a new order, and the `sortables` property is set before saving. This ensures the correct order is applied manually when necessary.

#### Sorting When Deleting

If `sort_when_deleting` is enabled, the order of the remaining models will be automatically adjusted when a model is deleted. For example:

```php
$model = MyModel::find(1);
$model->delete(); // The remaining records will be reordered automatically.
```

This helps maintain the correct sequence without any manual intervention.

In this scenario, the model is being updated with a new order, and the `sortables` property is set before saving. This ensures the correct order is applied manually when necessary.

### Grouping

If your model/table has a grouping field (usually a foreign key): `id, `**`user_id`**`, title, order_column`
Expand Down
19 changes: 17 additions & 2 deletions config/eloquent-sortable.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

return [
/*
* Which column will be used as the order column.
Expand All @@ -8,13 +10,26 @@

/*
* Define if the models should sort when creating.
* When true, the package will automatically assign the highest order number to a new model
* When true, the package will automatically assign the highest order number to a new model.
*/
'sort_when_creating' => true,

/*
* Define if the models should sort when updating.
* When true, the package will automatically update the order of models when one is updated.
*/
'sort_when_updating' => false,

/*
* Define if the models should sort when deleting.
* When true, the package will automatically update the order of models when one is deleted.
*/
'sort_when_deleting' => false,

/*
* Define if the timestamps should be ignored when sorting.
* When true, updated_at will not be updated when using setNewOrder
* When true, `updated_at` will not be updated when using `setNewOrder`, `setMassNewOrder`,
* or when models are reordered automatically during creation, updating, or deleting.
*/
'ignore_timestamps' => false,
];
2 changes: 2 additions & 0 deletions src/EloquentModelSortedEvent.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Spatie\EloquentSortable;

use Illuminate\Database\Eloquent\Model;
Expand Down
2 changes: 2 additions & 0 deletions src/EloquentSortableServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Spatie\EloquentSortable;

use Spatie\LaravelPackageTools\Package;
Expand Down
31 changes: 26 additions & 5 deletions src/Sortable.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?php

declare(strict_types=1);

namespace Spatie\EloquentSortable;

use ArrayAccess;
use Illuminate\Database\Eloquent\Builder;

interface Sortable
Expand All @@ -14,23 +17,41 @@ public function setHighestOrderNumber(): void;
/**
* Let's be nice and provide an ordered scope.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param Builder $query
*
* @return \Illuminate\Database\Query\Builder
* @return Builder
*/
public function scopeOrdered(Builder $query);
public function scopeOrdered(Builder $query): Builder;

/**
* This function reorders the records: the record with the first id in the array
* will get order 1, the record with the second it will get order 2,...
*
* @param array|\ArrayAccess $ids
* @param array|ArrayAccess $ids
* @param int $startOrder
*/
public static function setNewOrder(array|ArrayAccess $ids, int $startOrder = 1): void;

/**
* Modify the order column value for mass updates.
*
* @param array $ids
* @param int $startOrder
*/
public static function setNewOrder($ids, int $startOrder = 1): void;
public static function setMassNewOrder(array $ids, int $startOrder = 1): void;

/**
* Determine if the order column should be set when saving a new model instance.
*/
public function shouldSortWhenCreating(): bool;

/**
* Determine if the order column should be updated when updating a model instance.
*/
public function shouldSortWhenUpdating(): bool;

/**
* Determine if the order column should be updated when deleting a model instance.
*/
public function shouldSortWhenDeleting(): bool;
}
Loading