Skip to content

Commit

Permalink
Merge pull request #359 from rozwell/sortable_scope
Browse files Browse the repository at this point in the history
Improved sortable behavior
  • Loading branch information
willdurand committed Aug 22, 2012
2 parents 1135e38 + 3af6a72 commit 19dc671
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ public function preInsert($builder)
";
}

public function preUpdate($builder)
{
if ($this->behavior->useScope()) {
$this->setBuilder($builder);

return "// if scope has changed and rank was not modified (if yes, assuming superior action)
// insert object to the end of new scope and cleanup old one
if (\$this->isColumnModified({$this->peerClassname}::SCOPE_COL) && !\$this->isColumnModified({$this->peerClassname}::RANK_COL)) {
{$this->peerClassname}::shiftRank(-1, \$this->{$this->getColumnGetter()}() + 1, null, \$this->oldScope, \$con);
\$this->insertAtBottom(\$con);
}
";
}
}

public function preDelete($builder)
{
$useScope = $this->behavior->useScope();
Expand All @@ -97,13 +112,24 @@ public function preDelete($builder)

public function objectAttributes($builder)
{
return "
$script = "
/**
* Queries to be executed in the save transaction
* @var array
*/
protected \$sortableQueries = array();
";
if ($this->behavior->useScope()) {
$script .= "
/**
* The old scope value.
* @var int
*/
protected \$oldScope;
";
}

return $script;
}

public function objectMethods($builder)
Expand Down Expand Up @@ -136,14 +162,27 @@ public function objectMethods($builder)
return $script;
}

public function objectFilter(&$script, $builder)
{
if ($this->behavior->useScope()) {
$methodName = $this->getColumnSetter('scope_column');
$search = "if (\$this->{$this->getColumnAttribute('scope_column')} !== \$v) {";
$replace = $search . "
// sortable behavior
\$this->oldScope = \$this->{$this->getColumnGetter('scope_column')}();
";
$script = str_replace($search, $replace, $script);
}
}

/**
* Get the wraps for getter/setter, if the rank column has not the default name
*
* @return string
*/
protected function addRankAccessors(&$script)
{
$script .= "
$script .= "
/**
* Wrap the getter for rank value
*
Expand Down Expand Up @@ -174,7 +213,8 @@ public function setRank(\$v)
*/
protected function addScopeAccessors(&$script)
{
$script .= "
$script .= "
/**
* Wrap the getter for scope value
*
Expand Down Expand Up @@ -309,12 +349,6 @@ protected function addInsertAtRank(&$script)
*/
public function insertAtRank(\$rank, PropelPDO \$con = null)
{";
if ($useScope) {
$script .= "
if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
throw new PropelException('The scope must be defined before inserting an object in a suite');
}";
}
$script .= "
\$maxRank = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
if (\$rank < 1 || \$rank > \$maxRank + 1) {
Expand Down Expand Up @@ -351,12 +385,6 @@ protected function addInsertAtBottom(&$script)
*/
public function insertAtBottom(PropelPDO \$con = null)
{";
if ($useScope) {
$script .= "
if (null === \$this->{$this->getColumnGetter('scope_column')}()) {
throw new PropelException('The scope must be defined before inserting an object in a suite');
}";
}
$script .= "
\$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
Expand Down Expand Up @@ -454,7 +482,17 @@ public function swapWith(\$object, PropelPDO \$con = null)
\$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
}
\$con->beginTransaction();
try {
try {";
if ($this->behavior->useScope()) {
$script .= "
\$oldScope = \$this->{$this->getColumnGetter('scope_column')}();
\$newScope = \$object->{$this->getColumnGetter('scope_column')}();
if (\$oldScope != \$newScope) {
\$this->{$this->getColumnSetter('scope_column')}(\$newScope);
\$object->{$this->getColumnSetter('scope_column')}(\$oldScope);
}";
}
$script .= "
\$oldRank = \$this->{$this->getColumnGetter()}();
\$newRank = \$object->{$this->getColumnGetter()}();
\$this->{$this->getColumnSetter()}(\$newRank);
Expand Down Expand Up @@ -598,23 +636,34 @@ protected function addRemoveFromList(&$script)
$useScope = $this->behavior->useScope();
$script .= "
/**
* Removes the current object from the list.
* Removes the current object from the list".($useScope ? ' (moves it to the null scope)' : '').".
* The modifications are not persisted until the object is saved.
*
* @param PropelPDO \$con optional connection
*
* @return {$this->objectClassname} the current object
*/
public function removeFromList()
{
public function removeFromList(PropelPDO \$con = null)
{";
if ($useScope) {
$script .= "
// check if object is already removed
if (\$this->{$this->getColumnGetter('scope_column')}() === null) {
throw new PropelException('Object is already removed (has null scope)');
}
// move the object to the end of null scope
\$this->{$this->getColumnSetter('scope_column')}(null);
// \$this->insertAtBottom(\$con);";
} else {
$script .= "
// Keep the list modification query for the save() transaction
\$this->sortableQueries []= array(
'callable' => array(self::PEER, 'shiftRank'),
'arguments' => array(-1, \$this->{$this->getColumnGetter()}() + 1, null" . ($useScope ? ", \$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
);
// remove the object from the list
\$this->{$this->getColumnSetter('rank_column')}(null);";
if ($useScope) {
$script .= "
\$this->{$this->getColumnSetter('scope_column')}(null);";
}
$script .= "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,30 @@ public function testInsertAtRank()
$t->setScopeValue(1);
$t->insertAtRank(2);
$this->assertEquals(2, $t->getRank(), 'insertAtRank() sets the position');
$this->assertTrue($t->isNew(), 'insertAtTop() doesn\'t save the object');
$this->assertTrue($t->isNew(), 'insertAtRank() doesn\'t save the object');
$t->save();
$expected = array(1 => 'row1', 2 => 'new', 3 => 'row2', 4 => 'row3', 5 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'insertAtRank() shifts the entire suite');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtRank() leaves other suites unchanged');
}

public function testInsertAtRankNoScope()
{
$t = new Table12();
$t->setTitle('new');
$t->insertAtRank(2);
$this->assertEquals(2, $t->getRank(), 'insertAtRank() sets the position');
$this->assertTrue($t->isNew(), 'insertAtRank() doesn\'t save the object');
$t->save();
$expected = array(1 => 'row7', 2 => 'new', 3 => 'row8', 4 => 'row9', 5 => 'row10');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'insertAtRank() shifts the entire suite');
$expected = array(1 => 'row1', 2 => 'row2', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'insertAtRank() leaves other suites unchanged');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtRank() leaves other suites unchanged');
}

/**
* @expectedException PropelException
*/
Expand All @@ -148,15 +164,6 @@ public function testInsertAtOverMaxRank()
$t->insertAtRank(6);
}

/**
* @expectedException PropelException
*/
public function testInsertAtNoScope()
{
$t = new Table12();
$t->insertAtRank(3);
}

public function testInsertAtBottom()
{
$t = new Table12();
Expand All @@ -172,13 +179,20 @@ public function testInsertAtBottom()
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtBottom() leaves other suites unchanged');
}

/**
* @expectedException PropelException
*/
public function testInsertAtBottomNoScope()
{
$t = new Table12();
$t->setTitle('new');
$t->insertAtBottom();
$this->assertEquals(5, $t->getRank(), 'insertAtBottom() sets the position to the last');
$this->assertTrue($t->isNew(), 'insertAtTop() doesn\'t save the object');
$t->save();
$expected = array(1 => 'row7', 2 => 'row8', 3 => 'row9', 4 => 'row10', 5 => 'new');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'insertAtBottom() does not shift the entire suite');
$expected = array(1 => 'row1', 2 => 'row2', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'insertAtRank() leaves other suites unchanged');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtRank() leaves other suites unchanged');
}

public function testInsertAtTop()
Expand All @@ -196,6 +210,22 @@ public function testInsertAtTop()
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtTop() leaves other suites unchanged');
}

public function testInsertAtTopNoScope()
{
$t = new Table12();
$t->setTitle('new');
$t->insertAtTop();
$this->assertEquals(1, $t->getRank(), 'insertAtTop() sets the position to 1');
$this->assertTrue($t->isNew(), 'insertAtTop() doesn\'t save the object');
$t->save();
$expected = array(1 => 'new', 2 => 'row7', 3 => 'row8', 4 => 'row9', 5 => 'row10');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'insertAtTop() shifts the entire suite');
$expected = array(1 => 'row1', 2 => 'row2', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'insertAtRank() leaves other suites unchanged');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtRank() leaves other suites unchanged');
}

public function testMoveToRank()
{
$t2 = Table12Peer::retrieveByRank(2, 1);
Expand All @@ -215,6 +245,27 @@ public function testMoveToRank()
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'moveToRank() can move down');
}

public function testMoveToRankNoScope()
{
$t2 = Table12Peer::retrieveByRank(2);
$t2->moveToRank(3);
$expected = array(1 => 'row7', 2 => 'row9', 3 => 'row8', 4 => 'row10');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'moveToRank() can move up');
$expected = array(1 => 'row1', 2 => 'row2', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'insertAtRank() leaves other suites unchanged');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'insertAtRank() leaves other suites unchanged');
$t2->moveToRank(1);
$expected = array(1 => 'row8', 2 => 'row7', 3 => 'row9', 4 => 'row10');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'moveToRank() can move to the first rank');
$t2->moveToRank(4);
$expected = array(1 => 'row7', 2 => 'row9', 3 => 'row10', 4 => 'row8');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'moveToRank() can move to the last rank');
$t2->moveToRank(2);
$expected = array(1 => 'row7', 2 => 'row8', 3 => 'row9', 4 => 'row10');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'moveToRank() can move down');
}

/**
* @expectedException PropelException
*/
Expand Down Expand Up @@ -253,6 +304,19 @@ public function testSwapWith()
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'swapWith() leaves other suites unchanged');
}

public function testSwapWithBetweenScopes()
{
$t2 = Table12Peer::retrieveByRank(2, 1);
$t4 = Table12Peer::retrieveByRank(4);
$t2->swapWith($t4);
$expected = array(1 => 'row7', 2 => 'row8', 3 => 'row9', 4 => 'row2');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'swapWith() swaps ranks of the two objects between scopes and leaves the other ranks unchanged');
$expected = array(1 => 'row1', 2 => 'row10', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'swapWith() swaps ranks of the two objects between scopes and leaves the other ranks unchanged');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'swapWith() leaves rest of suites unchanged');
}

public function testMoveUp()
{
$t3 = Table12Peer::retrieveByRank(3, 1);
Expand Down Expand Up @@ -320,16 +384,26 @@ public function testRemoveFromList()
$t2 = Table12Peer::retrieveByRank(2, 1);
$res = $t2->removeFromList();
$this->assertTrue($res instanceof Table12, 'removeFromList() returns the current object');
$this->assertNull($res->getRank(), 'removeFromList() resets the object\'s rank');
Table12Peer::clearInstancePool();
$expected = array(1 => 'row1', 2 => 'row2', 3 => 'row3', 4 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'removeFromList() does not change the list until the object is saved');
$t2->save();
Table12Peer::clearInstancePool();
$expected = array(1 => 'row1', 2 => 'row3', 3 => 'row4');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'removeFromList() changes the list once the object is saved');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(1), 'removeFromList() changes the list and moves object to null scope once the object is saved');
$expected = array(1 => 'row7', 2 => 'row8', 3 => 'row9', 4 => 'row10', 5 => 'row2');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(), 'removeFromList() moves object to the end of null scope');
$expected = array(1 => 'row5', 2 => 'row6');
$this->assertEquals($expected, $this->getFixturesArrayWithScope(2), 'removeFromList() leaves other suites unchanged');
}

/**
* @expectedException PropelException
*/
public function testRemoveFromListNoScope()
{
$t2 = Table12Peer::retrieveByRank(2);
$t2->removeFromList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function testGetMaxRank()
Table12Peer::doDeleteAll();
$this->assertNull(Table12Peer::getMaxRank(1), 'getMaxRank() returns null for empty tables');
}

public function testRetrieveByRank()
{
$t = Table12Peer::retrieveByRank(5, 1);
Expand Down Expand Up @@ -94,21 +95,25 @@ public function testDoSelectOrderByRank()

public function testRetrieveList()
{
$this->assertEquals(4, count(Table12Peer::retrieveList(null)), 'retrieveList() returns the list of objects in the scope');
$this->assertEquals(4, count(Table12Peer::retrieveList(1)), 'retrieveList() returns the list of objects in the scope');
$this->assertEquals(2, count(Table12Peer::retrieveList(2)), 'retrieveList() returns the list of objects in the scope');
}

public function testCountList()
{
$this->assertEquals(4, Table12Peer::countList(null), 'countList() returns the list of objects in the scope');
$this->assertEquals(4, Table12Peer::countList(1), 'countList() returns the list of objects in the scope');
$this->assertEquals(2, Table12Peer::countList(2), 'countList() returns the list of objects in the scope');
}

public function testDeleteList()
{
$this->assertEquals(4, Table12Peer::deleteList(1), 'deleteList() returns the list of objects in the scope');
$this->assertEquals(4, Table12Peer::deleteList(null), 'deleteList() returns the list of deleted objects in the scope');
$this->assertEquals(6, Table12Peer::doCount(new Criteria()), 'deleteList() deletes the objects in the scope');
$this->assertEquals(4, Table12Peer::deleteList(1), 'deleteList() returns the list of deleted objects in the scope');
$this->assertEquals(2, Table12Peer::doCount(new Criteria()), 'deleteList() deletes the objects in the scope');
$this->assertEquals(2, Table12Peer::deleteList(2), 'deleteList() returns the list of objects in the scope');
$this->assertEquals(2, Table12Peer::deleteList(2), 'deleteList() returns the list of deleted objects in the scope');
$this->assertEquals(0, Table12Peer::doCount(new Criteria()), 'deleteList() deletes the objects in the scope');
}
}
Loading

0 comments on commit 19dc671

Please sign in to comment.