diff --git a/README.md b/README.md index ba3a521..4bafb2a 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,13 @@ $myModel->moveOrderDown(); $myModel->moveOrderUp(); ``` +Or you can move model up or down by specific amount: + +```php +$myModel->moveDownBy(6); +$myModel->moveUpBy(4); +``` + You can also move a model to the first or last position: ```php diff --git a/src/SortableTrait.php b/src/SortableTrait.php index fc15668..a0887e1 100644 --- a/src/SortableTrait.php +++ b/src/SortableTrait.php @@ -151,6 +151,68 @@ public function moveToStart(): static return $this; } + public function moveDownBy(int $amount): static + { + if ($amount < 1) { + return $this; + } + + if ($amount === 1) { + return $this->moveOrderDown(); + } + + $orderColumnName = $this->determineOrderColumnName(); + + $newOrder = (int) $this->buildSortQuery() + ->where($orderColumnName, '<=', $this->$orderColumnName + $amount) + ->max($orderColumnName); + + if ($this->$orderColumnName === $newOrder) { + return $this; + } + + $oldOrder = $this->$orderColumnName; + $this->$orderColumnName = $newOrder; + $this->save(); + + $this->buildSortQuery()->where($this->getKeyName(), '!=', $this->getKey()) + ->whereBetween($orderColumnName, [$oldOrder, $newOrder]) + ->decrement($orderColumnName); + + return $this; + } + + public function moveUpBy(int $amount): static + { + if ($amount < 1) { + return $this; + } + + if ($amount === 1) { + return $this->moveOrderUp(); + } + + $orderColumnName = $this->determineOrderColumnName(); + + $newOrder = (int) $this->buildSortQuery() + ->where($orderColumnName, '>=', $this->$orderColumnName - $amount) + ->min($orderColumnName); + + if ($this->$orderColumnName === $newOrder) { + return $this; + } + + $oldOrder = $this->$orderColumnName; + $this->$orderColumnName = $newOrder; + $this->save(); + + $this->buildSortQuery()->where($this->getKeyName(), '!=', $this->getKey()) + ->whereBetween($orderColumnName, [$newOrder, $oldOrder]) + ->increment($orderColumnName); + + return $this; + } + public function moveToEnd(): static { $maxOrder = $this->getHighestOrderNumber(); diff --git a/tests/SortableTest.php b/tests/SortableTest.php index 159b71f..62c57a4 100644 --- a/tests/SortableTest.php +++ b/tests/SortableTest.php @@ -371,4 +371,52 @@ public function it_can_tell_if_element_is_last_in_order() $this->assertTrue($model[$model->count() - 1]->isLastInOrder()); $this->assertFalse($model[$model->count() - 2]->isLastInOrder()); } + + /** @test */ + public function it_can_move_order_down_by_amount() + { + $moveBy = 6; + $oldOrder = 4; + + $model = Dummy::find($oldOrder); + $others = Dummy::whereBetween('order_column', [$oldOrder + 1, $oldOrder + $moveBy])->get(); + + $this->assertEquals($model->order_column, $oldOrder); + foreach ($others as $index => $other) { + $this->assertEquals($other->order_column, $oldOrder + $index + 1); + } + + $this->assertNotFalse($model->moveDownBy($moveBy)); + + $others = Dummy::whereKey($others)->get(); + + $this->assertEquals($model->order_column, $oldOrder + $moveBy); + foreach ($others as $index => $other) { + $this->assertEquals($other->order_column, $oldOrder + $index); + } + } + + /** @test */ + public function it_can_move_order_up_by_amount() + { + $moveBy = 6; + $oldOrder = 14; + + $model = Dummy::find($oldOrder); + $others = Dummy::whereBetween('order_column', [$oldOrder - $moveBy, $oldOrder - 1])->ordered('desc')->get(); + + $this->assertEquals($model->order_column, $oldOrder); + foreach ($others as $index => $other) { + $this->assertEquals($other->order_column, $oldOrder - $index - 1); + } + + $this->assertNotFalse($model->moveUpBy($moveBy)); + + $others = Dummy::whereKey($others)->ordered('desc')->get(); + + $this->assertEquals($model->order_column, $oldOrder - $moveBy); + foreach ($others as $index => $other) { + $this->assertEquals($other->order_column, $oldOrder - $index); + } + } }