Skip to content
Browse files

Merge pull request #359 from rozwell/sortable_scope

Improved sortable behavior
  • Loading branch information...
2 parents 1135e38 + 3af6a72 commit 19dc671d5c3c90c169ae90fd11ed8562d43417bd @willdurand willdurand committed Aug 22, 2012
View
93 generator/lib/behavior/sortable/SortableBehaviorObjectBuilderModifier.php
@@ -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();
@@ -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)
@@ -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
*
@@ -174,7 +213,8 @@ public function setRank(\$v)
*/
protected function addScopeAccessors(&$script)
{
- $script .= "
+ $script .= "
+
/**
* Wrap the getter for scope value
*
@@ -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) {
@@ -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);
@@ -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);
@@ -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 .= "
View
104 ...tsuite/generator/behavior/sortable/SortableBehaviorObjectBuilderModifierWithScopeTest.php
@@ -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
*/
@@ -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();
@@ -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()
@@ -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);
@@ -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
*/
@@ -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);
@@ -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();
+ }
+
}
View
9 ...estsuite/generator/behavior/sortable/SortableBehaviorPeerBuilderModifierWithScopeTest.php
@@ -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);
@@ -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');
}
}
View
30 test/tools/helpers/bookstore/behavior/BookstoreSortableTestBase.php
@@ -36,11 +36,11 @@ protected function populateTable11()
protected function populateTable12()
{
/* List used for tests
- scope=1 scope=2
- row1 row5
- row2 row6
- row3
- row4
+ scope=1 scope=2 scope=null
+ row1 row5 row7
+ row2 row6 row8
+ row3 row9
+ row4 row10
*/
Table12Peer::doDeleteAll();
$t1 = new Table12();
@@ -73,6 +73,22 @@ protected function populateTable12()
$t6->setScopeValue(2);
$t6->setTitle('row6');
$t6->save();
+ $t7 = new Table12();
+ $t7->setRank(1);
+ $t7->setTitle('row7');
+ $t7->save();
+ $t8 = new Table12();
+ $t8->setRank(2);
+ $t8->setTitle('row8');
+ $t8->save();
+ $t9 = new Table12();
+ $t9->setRank(3);
+ $t9->setTitle('row9');
+ $t9->save();
+ $t10 = new Table12();
+ $t10->setRank(4);
+ $t10->setTitle('row10');
+ $t10->save();
}
protected function getFixturesArray()
@@ -91,9 +107,7 @@ protected function getFixturesArray()
protected function getFixturesArrayWithScope($scope = null)
{
$c = new Criteria();
- if ($scope !== null) {
- $c->add(Table12Peer::SCOPE_COL, $scope);
- }
+ $c->add(Table12Peer::SCOPE_COL, $scope);
$c->addAscendingOrderByColumn(Table12Peer::RANK_COL);
$ts = Table12Peer::doSelect($c);
$ret = array();

0 comments on commit 19dc671

Please sign in to comment.
Something went wrong with that request. Please try again.