Skip to content

Commit

Permalink
Added support for a forced position on new object (old position retur…
Browse files Browse the repository at this point in the history
…ns null)
  • Loading branch information
Martin Kluska committed Jun 26, 2017
1 parent 5858b93 commit d54312c
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 86 deletions.
3 changes: 0 additions & 3 deletions src/Commands/RecalculatePositionCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@ public function handle()
$modelClass::sorted()->chunk(200, function (Collection $collection) use ($groups, &$positionsByGroup) {
/** @var PositionTrait|Model $model */
foreach ($collection as $model) {
// Prevent the move action and force the position we set
$model->setDisablePositionUpdateAttribute(true);

// Builds the group key to get position
$groupKey = $this->buildGroupKeyForPosition($model, $groups);

Expand Down
22 changes: 21 additions & 1 deletion src/PositionObserver.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?php

namespace Pion\Support\Eloquent\Position;

use Pion\Support\Eloquent\Position\Query\AbstractPositionQuery;
use Pion\Support\Eloquent\Position\Query\LastPositionQuery;
use Pion\Support\Eloquent\Position\Query\PositionQuery;
use Pion\Support\Eloquent\Position\Query\MoveQuery;
use Pion\Support\Eloquent\Position\Traits\PositionTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Events\Dispatcher;
Expand Down Expand Up @@ -50,12 +52,30 @@ public function saving($model)
// Check if the position is set
if (is_null($position) || $position == '') {
$this->appendLast($model, $oldPosition);
} else if (is_null($oldPosition)) {
$this->forcedPosition($model, $position);
} else {
$this->move($model, $position, $oldPosition);
}
}
}

/**
* Forces the new position, will be overriden if it's out of maximum bounds.
*
* @param Model|PositionTrait $model
* @param int $position
* @param int|null $oldPosition
*/
protected function forcedPosition($model, $position, $oldPosition = null)
{
// Build the new position
$query = new PositionQuery($model, $position);

// Run the query
$query->runQuery($query, $oldPosition);
}

/**
* Setups the position to be at the end
*
Expand Down Expand Up @@ -83,7 +103,7 @@ protected function move($model, $position, $oldPosition)
// Check if the position has change and we need to recalculate
if ($oldPosition != $position) {
// Build the position query
$query = new PositionQuery($model, $position, $oldPosition);
$query = new MoveQuery($model, $position, $oldPosition);

// Run the position query
$this->runQuery($query, $oldPosition);
Expand Down
1 change: 0 additions & 1 deletion src/Query/LastPositionQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
class LastPositionQuery extends AbstractPositionQuery
{

/**
* Creates the base query and builds the query
*
Expand Down
119 changes: 119 additions & 0 deletions src/Query/MoveQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
namespace Pion\Support\Eloquent\Position\Query;

use Pion\Support\Eloquent\Position\Traits\PositionTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class MoveQuery extends AbstractPositionQuery
{
/**
* @var bool
*/
protected $increment = false;

/**
* @var string
*/
protected $positionColumn = null;

/**
* @var int
*/
protected $position;

/**
* Comparision condition for old position value.
* In default is for decrement.
* @var string
*/
protected $oldComparisionCondition = '>';
/**
* Comparision condition for new position value.
* In default is for decrement.
* @var string
*/
protected $newComparisionCondition = '<=';


/**
* PositionQuery constructor.
*
* @param Model|PositionTrait $model
* @param int $position
* @param int|null $oldPosition
*/
public function __construct($model, $position, $oldPosition)
{
parent::__construct($model, $oldPosition, false);

$this->position = $position;

// Indicate if si the increment/decrement
$this->increment = $position < $oldPosition;

// Get the column for position to build correct query
$this->positionColumn = $model->getPositionColumn();

// Build the comparision condition
$this->buildComparisionCondition();

// Prepare the query
$this->query = $this->buildQuery();
}

//region Query

/**
* Runs the increment/decrement query
*
* @param Builder $query
*
* @return int
*/
public function runQuery($query)
{
if ($this->increment) {
return $query->increment($this->positionColumn);
} else {
return $query->decrement($this->positionColumn);
}
}

/**
* Builds the basic query with where condition (includes the position conditions)
* @return Builder
*/
protected function buildQuery()
{
// Create query
$query = parent::buildQuery();

// Build the where condition for the position. This will ensure to update only required rows
return $query->where($this->positionColumn, $this->oldComparisionCondition, $this->oldPosition)
->where($this->positionColumn, $this->newComparisionCondition, $this->position);
}

/**
* Builds the correct comparision condition
*/
protected function buildComparisionCondition()
{
if ($this->increment) {
$this->oldComparisionCondition = '<';
$this->newComparisionCondition = '>=';
}
}
//endregion

//region Getters

/**
* @return mixed
*/
public function position()
{
return $this->position;
}
//endregion
}
118 changes: 43 additions & 75 deletions src/Query/PositionQuery.php
Original file line number Diff line number Diff line change
@@ -1,119 +1,87 @@
<?php
namespace Pion\Support\Eloquent\Position\Query;

use Pion\Support\Eloquent\Position\Traits\PositionTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Pion\Support\Eloquent\Position\Traits\PositionTrait;

class PositionQuery extends AbstractPositionQuery
{
class PositionQuery extends AbstractPositionQuery {
/**
* @var bool
* @var string
*/
protected $increment = false;
protected $position = null;

/**
* @var string
*/
protected $positionColumn = null;

/**
* @var int
*/
protected $position;

/**
* Comparision condition for old position value.
* In default is for decrement.
* @var string
*/
protected $oldComparisionCondition = '>';
/**
* Comparision condition for new position value.
* In default is for decrement.
* @var string
* When only the new model is updated, we don`t need to run query
* @var bool
*/
protected $newComparisionCondition = '<=';

protected $shouldRunQuery = false;

/**
* PositionQuery constructor.
* Creates the base query and builds the query
*
* @param Model|PositionTrait $model
* @param int $position
* @param int $oldPosition
*/
public function __construct($model, $position, $oldPosition)
public function __construct($model, $position)
{
parent::__construct($model, $oldPosition, false);

// Store the new position
$this->position = $position;

// Indicate if si the increment/decrement
$this->increment = $position < $oldPosition;

// Get the column for position to build correct query
// Get the position column
$this->positionColumn = $model->getPositionColumn();

// Build the comparision condition
$this->buildComparisionCondition();

// Prepare the query
$this->query = $this->buildQuery();
}

//region Query

/**
* Runs the increment/decrement query
*
* @param Builder $query
*
* @return int
*/
public function runQuery($query)
{
if ($this->increment) {
return $query->increment($this->positionColumn);
} else {
return $query->decrement($this->positionColumn);
}
// Build the query
parent::__construct($model, null, true);
}

/**
* Builds the basic query with where condition (includes the position conditions)
* Builds the basic query and appends a where conditions for group is set
* @return Builder
*/
protected function buildQuery()
{
// Create query
$query = parent::buildQuery();

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

/**
* Builds the correct comparision condition
*/
protected function buildComparisionCondition()
{
if ($this->increment) {
$this->oldComparisionCondition = '<';
$this->newComparisionCondition = '>=';
// If the new position is last position, just update the position or if
// new position is out of bounds
if ($this->position >= $lastPosition) {
$this->model()->setPosition($lastPosition + 1);
} else {
$this->shouldRunQuery = true;

// Set the forced position
$this->model()->setPosition($this->position);

// Move other models
$query->where($this->positionColumn, '>=', $this->position);
}

return $query;
}
//endregion

//region Getters

/**
* @return mixed
* Runs the query for last position and sets the model position if the position has
* changed
*
* @param Builder $query
*
* @return int the last position returned
*/
public function position()
public function runQuery($query)
{
return $this->position;
if ($this->shouldRunQuery) {
return $query->increment($this->positionColumn);
}

return $this->model()->getPosition();
}
//endregion
}
}
6 changes: 0 additions & 6 deletions src/Traits/BasePositionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,26 +128,20 @@ protected function resetPositionOptionCache()
* Enables setting disablePositionUpdate option in runtime
*
* @param boolean $value
*
* @return $this
*/
public function setDisablePositionUpdateAttribute($value)
{
$this->optionCache['disablePositionUpdate'] = $value;
return $this;
}

/**
* Enables setting positionColumn option in runtime
*
* @param string $value
*
* @return $this
*/
public function setPositionColumnAttribute($value)
{
$this->optionCache['positionColumn'] = $value;
return $this;
}
//endregion
}

0 comments on commit d54312c

Please sign in to comment.