- Laravel
7.0
or newer - PHP
7.2
or newer
Install the package via Composer:
composer require nevadskiy/laravel-position
Add the HasPosition
trait to the models that should have positions:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Nevadskiy\Position\HasPosition;
class Category extends Model
{
use HasPosition;
}
You also need to add a position
column to the model's table using a migration:
Schema::create('categories', function (Blueprint $table) {
$table->integer('position')->unsigned()->index();
});
And that's it!
Models with positions have an integer attribute named position, which indicates their position
in the sequence.
This attribute is automatically calculated upon insertion and is utilized for sorting the models during query operations.
The position
attribute is a kind of array index and is automatically inserted when a new model is created. For example:
$category = Category::create();
echo $category->position; // 0
$category = Category::create();
echo $category->position; // 1
$category = Category::create();
echo $category->position; // 2
By default, the first record in the sequence is assigned a position value of 0
. If you want to specify a custom number to start counting models, you can override the getStartPosition
method in your model:
public function getStartPosition(): int
{
return 1;
}
In this example, the first record will be assigned a position value of 1
.
By default, the newly created model is assigned the position at the end of the sequence.
For example, if you want to create models at the beginning of the sequence, you can override the getNextPosition
method in your model:
public function getNextPosition(): int
{
return $this->getStartPosition();
}
In this example, each new model will be assigned the starting position and will be positioned at the beginning of the sequence. The positions of other models in the sequence will be automatically updated:
$first = Category::create();
echo $first->position; // 0
$second = Category::create();
echo $second->position; // 0
echo $first->position; // 1 (automatically updated)
$third = Category::create();
echo $third->position; // 0
echo $second->position; // 1 (automatically updated)
echo $first->position; // 2 (automatically updated again)
You can also use negative positions. For example, the -1 position indicates that the model will be positioned at the end of the sequence. It is almost identical to Model::count() - 1. This is the default behavior.
When a model is deleted, the positions of other records in the sequence are automatically updated.
To select models in the arranged sequence, you can use the orderByPosition
scope. For example:
Category::orderByPosition()->get();
The orderByPosition
scope is not applied by default because the corresponding SQL statement will be added to all queries, even where it is not required.
It is recommended to manually add the scope in all places where you need it.
However, if you want to enable auto-ordering for all query operations, you can override the alwaysOrderByPosition
method in your model as following:
public function alwaysOrderByPosition(): bool
{
return true;
}
To move a model to an arbitrary position in the sequence, you can simply update its position. For example:
$category->update([
'position' => 3
]);
The positions of other models will be automatically recalculated as well.
You can also use the move
method, which sets a new position value and updates the model immediately. For example:
$category->move(3);
If you want to move the model to the end of the sequence, you can use a negative position value. For example:
$category->move(-1); // Move to the end
The swap
method swaps the position of two models. For example:
$category->swap($anotherCategory);
It is also possible to arrange models by their IDs. The position of each model will be recalculated according to the index of its ID in the given array. You can also provide a second argument as a starting position. For example:
Category::arrangeByKeys([3, 5, 7]);
Position grouping is particularly useful when you want to maintain position sequences separately for different groups of records within the same table.
To enable position grouping, you can specify one or more database columns that act as the grouping criteria for positions using the groupPositionBy
method in your model:
public function groupPositionBy(): array
{
return [
'category_id',
];
}
By default, when you change the position or group of a model, the PositionObserver
automatically updates the positions of other models in the sequence by performing additional database queries. If you need to disable this behavior for any reason, you can do it like so:
use Nevadskiy\Position\PositionObserver;
PositionObserver::lockFor(Category::class);
$category->update(['position' => 1]);
PositionObserver::unlockFor(Category::class);
Please see CHANGELOG for more information what has changed recently.
Please see CONTRIBUTING for more information.
If you discover any security related issues, please e-mail me instead of using the issue tracker.
The MIT License (MIT). Please see LICENSE for more information.
- support
swap
for models from different groups