Skip to content

Commit d54312c

Browse files
author
Martin Kluska
committed
Added support for a forced position on new object (old position returns null)
1 parent 5858b93 commit d54312c

File tree

6 files changed

+183
-86
lines changed

6 files changed

+183
-86
lines changed

src/Commands/RecalculatePositionCommand.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ public function handle()
6464
$modelClass::sorted()->chunk(200, function (Collection $collection) use ($groups, &$positionsByGroup) {
6565
/** @var PositionTrait|Model $model */
6666
foreach ($collection as $model) {
67-
// Prevent the move action and force the position we set
68-
$model->setDisablePositionUpdateAttribute(true);
69-
7067
// Builds the group key to get position
7168
$groupKey = $this->buildGroupKeyForPosition($model, $groups);
7269

src/PositionObserver.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<?php
2+
23
namespace Pion\Support\Eloquent\Position;
34

45
use Pion\Support\Eloquent\Position\Query\AbstractPositionQuery;
56
use Pion\Support\Eloquent\Position\Query\LastPositionQuery;
67
use Pion\Support\Eloquent\Position\Query\PositionQuery;
8+
use Pion\Support\Eloquent\Position\Query\MoveQuery;
79
use Pion\Support\Eloquent\Position\Traits\PositionTrait;
810
use Illuminate\Database\Eloquent\Model;
911
use Illuminate\Contracts\Events\Dispatcher;
@@ -50,12 +52,30 @@ public function saving($model)
5052
// Check if the position is set
5153
if (is_null($position) || $position == '') {
5254
$this->appendLast($model, $oldPosition);
55+
} else if (is_null($oldPosition)) {
56+
$this->forcedPosition($model, $position);
5357
} else {
5458
$this->move($model, $position, $oldPosition);
5559
}
5660
}
5761
}
5862

63+
/**
64+
* Forces the new position, will be overriden if it's out of maximum bounds.
65+
*
66+
* @param Model|PositionTrait $model
67+
* @param int $position
68+
* @param int|null $oldPosition
69+
*/
70+
protected function forcedPosition($model, $position, $oldPosition = null)
71+
{
72+
// Build the new position
73+
$query = new PositionQuery($model, $position);
74+
75+
// Run the query
76+
$query->runQuery($query, $oldPosition);
77+
}
78+
5979
/**
6080
* Setups the position to be at the end
6181
*
@@ -83,7 +103,7 @@ protected function move($model, $position, $oldPosition)
83103
// Check if the position has change and we need to recalculate
84104
if ($oldPosition != $position) {
85105
// Build the position query
86-
$query = new PositionQuery($model, $position, $oldPosition);
106+
$query = new MoveQuery($model, $position, $oldPosition);
87107

88108
// Run the position query
89109
$this->runQuery($query, $oldPosition);

src/Query/LastPositionQuery.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
class LastPositionQuery extends AbstractPositionQuery
1717
{
18-
1918
/**
2019
* Creates the base query and builds the query
2120
*

src/Query/MoveQuery.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
namespace Pion\Support\Eloquent\Position\Query;
3+
4+
use Pion\Support\Eloquent\Position\Traits\PositionTrait;
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class MoveQuery extends AbstractPositionQuery
9+
{
10+
/**
11+
* @var bool
12+
*/
13+
protected $increment = false;
14+
15+
/**
16+
* @var string
17+
*/
18+
protected $positionColumn = null;
19+
20+
/**
21+
* @var int
22+
*/
23+
protected $position;
24+
25+
/**
26+
* Comparision condition for old position value.
27+
* In default is for decrement.
28+
* @var string
29+
*/
30+
protected $oldComparisionCondition = '>';
31+
/**
32+
* Comparision condition for new position value.
33+
* In default is for decrement.
34+
* @var string
35+
*/
36+
protected $newComparisionCondition = '<=';
37+
38+
39+
/**
40+
* PositionQuery constructor.
41+
*
42+
* @param Model|PositionTrait $model
43+
* @param int $position
44+
* @param int|null $oldPosition
45+
*/
46+
public function __construct($model, $position, $oldPosition)
47+
{
48+
parent::__construct($model, $oldPosition, false);
49+
50+
$this->position = $position;
51+
52+
// Indicate if si the increment/decrement
53+
$this->increment = $position < $oldPosition;
54+
55+
// Get the column for position to build correct query
56+
$this->positionColumn = $model->getPositionColumn();
57+
58+
// Build the comparision condition
59+
$this->buildComparisionCondition();
60+
61+
// Prepare the query
62+
$this->query = $this->buildQuery();
63+
}
64+
65+
//region Query
66+
67+
/**
68+
* Runs the increment/decrement query
69+
*
70+
* @param Builder $query
71+
*
72+
* @return int
73+
*/
74+
public function runQuery($query)
75+
{
76+
if ($this->increment) {
77+
return $query->increment($this->positionColumn);
78+
} else {
79+
return $query->decrement($this->positionColumn);
80+
}
81+
}
82+
83+
/**
84+
* Builds the basic query with where condition (includes the position conditions)
85+
* @return Builder
86+
*/
87+
protected function buildQuery()
88+
{
89+
// Create query
90+
$query = parent::buildQuery();
91+
92+
// Build the where condition for the position. This will ensure to update only required rows
93+
return $query->where($this->positionColumn, $this->oldComparisionCondition, $this->oldPosition)
94+
->where($this->positionColumn, $this->newComparisionCondition, $this->position);
95+
}
96+
97+
/**
98+
* Builds the correct comparision condition
99+
*/
100+
protected function buildComparisionCondition()
101+
{
102+
if ($this->increment) {
103+
$this->oldComparisionCondition = '<';
104+
$this->newComparisionCondition = '>=';
105+
}
106+
}
107+
//endregion
108+
109+
//region Getters
110+
111+
/**
112+
* @return mixed
113+
*/
114+
public function position()
115+
{
116+
return $this->position;
117+
}
118+
//endregion
119+
}

src/Query/PositionQuery.php

Lines changed: 43 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,87 @@
11
<?php
22
namespace Pion\Support\Eloquent\Position\Query;
33

4-
use Pion\Support\Eloquent\Position\Traits\PositionTrait;
5-
use Illuminate\Database\Eloquent\Builder;
64
use Illuminate\Database\Eloquent\Model;
5+
use Pion\Support\Eloquent\Position\Traits\PositionTrait;
76

8-
class PositionQuery extends AbstractPositionQuery
9-
{
7+
class PositionQuery extends AbstractPositionQuery {
108
/**
11-
* @var bool
9+
* @var string
1210
*/
13-
protected $increment = false;
11+
protected $position = null;
1412

1513
/**
1614
* @var string
1715
*/
1816
protected $positionColumn = null;
1917

2018
/**
21-
* @var int
22-
*/
23-
protected $position;
24-
25-
/**
26-
* Comparision condition for old position value.
27-
* In default is for decrement.
28-
* @var string
29-
*/
30-
protected $oldComparisionCondition = '>';
31-
/**
32-
* Comparision condition for new position value.
33-
* In default is for decrement.
34-
* @var string
19+
* When only the new model is updated, we don`t need to run query
20+
* @var bool
3521
*/
36-
protected $newComparisionCondition = '<=';
37-
22+
protected $shouldRunQuery = false;
3823

3924
/**
40-
* PositionQuery constructor.
25+
* Creates the base query and builds the query
4126
*
4227
* @param Model|PositionTrait $model
43-
* @param int $position
44-
* @param int $oldPosition
4528
*/
46-
public function __construct($model, $position, $oldPosition)
29+
public function __construct($model, $position)
4730
{
48-
parent::__construct($model, $oldPosition, false);
49-
31+
// Store the new position
5032
$this->position = $position;
5133

52-
// Indicate if si the increment/decrement
53-
$this->increment = $position < $oldPosition;
54-
55-
// Get the column for position to build correct query
34+
// Get the position column
5635
$this->positionColumn = $model->getPositionColumn();
5736

58-
// Build the comparision condition
59-
$this->buildComparisionCondition();
60-
61-
// Prepare the query
62-
$this->query = $this->buildQuery();
63-
}
64-
65-
//region Query
66-
67-
/**
68-
* Runs the increment/decrement query
69-
*
70-
* @param Builder $query
71-
*
72-
* @return int
73-
*/
74-
public function runQuery($query)
75-
{
76-
if ($this->increment) {
77-
return $query->increment($this->positionColumn);
78-
} else {
79-
return $query->decrement($this->positionColumn);
80-
}
37+
// Build the query
38+
parent::__construct($model, null, true);
8139
}
8240

8341
/**
84-
* Builds the basic query with where condition (includes the position conditions)
42+
* Builds the basic query and appends a where conditions for group is set
8543
* @return Builder
8644
*/
8745
protected function buildQuery()
8846
{
8947
// Create query
9048
$query = parent::buildQuery();
9149

92-
// Build the where condition for the position. This will ensure to update only required rows
93-
return $query->where($this->positionColumn, $this->oldComparisionCondition, $this->oldPosition)
94-
->where($this->positionColumn, $this->newComparisionCondition, $this->position);
95-
}
50+
// Get the last position and move the position
51+
$lastPosition = $query->max($this->positionColumn);
9652

97-
/**
98-
* Builds the correct comparision condition
99-
*/
100-
protected function buildComparisionCondition()
101-
{
102-
if ($this->increment) {
103-
$this->oldComparisionCondition = '<';
104-
$this->newComparisionCondition = '>=';
53+
// If the new position is last position, just update the position or if
54+
// new position is out of bounds
55+
if ($this->position >= $lastPosition) {
56+
$this->model()->setPosition($lastPosition + 1);
57+
} else {
58+
$this->shouldRunQuery = true;
59+
60+
// Set the forced position
61+
$this->model()->setPosition($this->position);
62+
63+
// Move other models
64+
$query->where($this->positionColumn, '>=', $this->position);
10565
}
66+
67+
return $query;
10668
}
107-
//endregion
10869

109-
//region Getters
11070

11171
/**
112-
* @return mixed
72+
* Runs the query for last position and sets the model position if the position has
73+
* changed
74+
*
75+
* @param Builder $query
76+
*
77+
* @return int the last position returned
11378
*/
114-
public function position()
79+
public function runQuery($query)
11580
{
116-
return $this->position;
81+
if ($this->shouldRunQuery) {
82+
return $query->increment($this->positionColumn);
83+
}
84+
85+
return $this->model()->getPosition();
11786
}
118-
//endregion
119-
}
87+
}

src/Traits/BasePositionTrait.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,26 +128,20 @@ protected function resetPositionOptionCache()
128128
* Enables setting disablePositionUpdate option in runtime
129129
*
130130
* @param boolean $value
131-
*
132-
* @return $this
133131
*/
134132
public function setDisablePositionUpdateAttribute($value)
135133
{
136134
$this->optionCache['disablePositionUpdate'] = $value;
137-
return $this;
138135
}
139136

140137
/**
141138
* Enables setting positionColumn option in runtime
142139
*
143140
* @param string $value
144-
*
145-
* @return $this
146141
*/
147142
public function setPositionColumnAttribute($value)
148143
{
149144
$this->optionCache['positionColumn'] = $value;
150-
return $this;
151145
}
152146
//endregion
153147
}

0 commit comments

Comments
 (0)