Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix addRelated and create his test unit.

  • Loading branch information...
commit 5c650e3fbe44614062b36bc86480056abadb8735 1 parent 86b3bc9
Laurent authored lchenay committed
View
249 library/Centurion/Db/Table/Select.php
@@ -197,26 +197,146 @@ public function count()
}
$result = $rowCount->query(Zend_Db::FETCH_ASSOC)->fetch();
- return count($result) > 0 ? $result[$rowCountColumn] : 0;
+ return count($result) > 0 ? (int)$result[$rowCountColumn] : 0;
}
/**
+ * Proxy function to test isAlreadyJoined
+ * @param unknown_type $tableName
+ * @param unknown_type $joinCond
+ * @return boolean
+ */
+ public function isAlreadyJoined($tableName, $joinCond = null) {
+ return $this->_isAlreadyJoined($tableName, $joinCond);
+ }
+
+ /**
* Check if the table has already been joined to avoid multiple join of the same table
*
* @param string $tableName
* @param string $joinCond
*/
- protected function _isAlreadyJoined($tableName, $joinCond)
- {
+ protected function _isAlreadyJoined($tableName, $joinCond = null) {
$fromParts = $this->getPart(self::FROM);
-
- foreach ($this->getPart(self::FROM) as $key => $val) {
- if ($val['joinCondition'] === $joinCond)
- return true;
+
+ try {
+ if (in_array($tableName, array_keys($fromParts))) {
+ if (null == $joinCond) {
+ return true;
+ }
+
+ //TODO: this should be foreach
+ if ($this->isConditionEquals($fromParts[$tableName]['joinCondition'], $joinCond)) {
+ return true;
+ }
+
+ }
+ } catch (Exception $e) {
+ return false;
}
-
+
return false;
}
+
+ public function isConditionEquals($cond1, $cond2)
+ {
+ if (false !== strpos($cond1, '(') || false !== strpos($cond2, '(')) {
+ throw new Exception('Not yet supported');
+ }
+
+ $cond1 = strtolower($cond1);
+ $cond2 = strtolower($cond2);
+
+ $tabAnd1 = explode(' and ', $cond1);
+ $tabAnd2 = explode(' and ', $cond2);
+
+ if (count($tabAnd1) != count($tabAnd2)) {
+ return false;
+ }
+
+ $tabAnd1 = array_map(array($this, 'normalizeCondition'), $tabAnd1);
+ $tabAnd2 = array_map(array($this, 'normalizeCondition'), $tabAnd2);
+
+ foreach ($tabAnd1 as $val) {
+ $found = false;
+ foreach ($tabAnd2 as $val2) {
+ if (strcmp($val, $val2) == 0) {
+ $found = true;
+ break;
+ }
+ }
+
+ if (!$found) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function forcePrefix($cond) {
+
+ $tabCondition = explode('.', $cond);
+
+ if (!isset($tabCondition[1])) {
+ $tabCondition[1] = $tabCondition[0];
+ $tabCondition[0] = $this->getAdapter()->quoteTableAs($this->getTable()->info('name'));
+ }
+
+ $tabCondition = array_map('trim', $tabCondition);
+
+ if (false === strpos($tabCondition[0], '`')) {
+ $tabCondition[0] = $this->getAdapter()->quoteIdentifier($tabCondition[0]);
+ }
+
+ if (false === strpos($tabCondition[1], '`')) {
+ $tabCondition[1] = $this->getAdapter()->quoteIdentifier($tabCondition[1]);
+ }
+
+
+ return implode('.', $tabCondition);
+ }
+
+ public function normalizeCondition($cond)
+ {
+ if (trim($cond) == '') {
+ return $cond;
+ }
+
+ $tabCond = preg_split('` *((?:[<>]=?)|=) *`', $cond, 2, PREG_SPLIT_DELIM_CAPTURE);
+
+ if (count($tabCond) != 3) {
+ throw new Exception('Problem. Condition not supported');
+ }
+
+ $tabCond[0] = $this->forcePrefix(trim($tabCond[0]));
+ $tabCond[2] = $this->forcePrefix($tabCond[2]);
+
+ if (strcmp($tabCond[0], $tabCond[2]) > 0) {
+ switch($tabCond[1]) {
+ case '=':
+ break;
+ case '<':
+ $tabCond[1] = '>';
+ break;
+ case '<=':
+ $tabCond[1] = '>=';
+ break;
+ case '>':
+ $tabCond[1] = '<';
+ break;
+ case '>=':
+ $tabCond[1] = '<=';
+ break;
+ }
+
+ $temp = $tabCond[0];
+ $tabCond[0] = $tabCond[2];
+ $tabCond[2] = $temp;
+ }
+
+ return implode(' ', $tabCond);
+ }
public function hasColumn($colName)
{
@@ -320,20 +440,44 @@ public function addManyToManyTable($rule, $localTable = null, $full = true,
} while ($this->_isAlreadyJoined($tableName, $joinCond));
if (!$this->_isAlreadyJoined($interTableName, $joinCond)) {
+ /**
+ * We will join to the table. We check that Zend will not generate a autoatic alias because in this case
+ * we must change the join condition
+ */
+ if ($interTableName !== $this->_uniqueCorrelation($interTableName)) {
+ $quotedInterTableName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($interTableName));
+ $joinCond = sprintf('%s.%s = %s.%s', $quotedInterTableName,
+ $this->_adapter->quoteIdentifier($ref['columns']['local']),
+ $this->_adapter->quoteIdentifier($localTableName),
+ $this->_adapter->quoteIdentifier($localPrimary));
+ } else {
+ $quotedInterTableName = $this->_adapter->quoteIdentifier($interTableName);
+ }
$this->$method($interTableName, $joinCond, array());
}
- $joinCond = sprintf('%s.%s = %s.%s', $this->_adapter->quoteIdentifier($interTableName),
+ $joinCond = sprintf('%s.%s = %s.%s', $interTableName,
$this->_adapter->quoteIdentifier($ref['columns']['foreign']),
$this->_adapter->quoteIdentifier($refTableName),
$this->_adapter->quoteIdentifier($refPrimary));
if ($full && !$this->_isAlreadyJoined($refTableName, $joinCond)) {
- call_user_func_array(array($this, $method), array($refTableName, $joinCond, array()));
+ /**
+ * We will join to the table. We check that Zend will not generate a autoatic alias because in this case
+ * we must change the join condition
+ */
+ if ($refTableName !== $this->_uniqueCorrelation($refTableName)) {
+ $joinCond = sprintf('%s.%s = %s.%s', $quotedInterTableName,
+ $this->_adapter->quoteIdentifier($ref['columns']['foreign']),
+ $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($refTableName)),
+ $this->_adapter->quoteIdentifier($refPrimary));
+ }
+ $this->$method($refTableName, $joinCond, array());
}
- if ($cols)
+ if ($cols) {
$this->_tableCols($localTableName, $cols, true);
+ }
return $this;
}
@@ -345,12 +489,14 @@ public function addManyToManyTable($rule, $localTable = null, $full = true,
* @param Centurion_Db_Table_Abstract [OPTIONAL] $localTable
* @param array $cols
* @param string $joinType
+ * @return Centurion_Db_Table_Select
*/
public function addDependentTable($rule, $localTable = null, $cols = null, $joinType = self::JOIN_TYPE_INNER)
{
- if (null == $localTable)
+ if (null == $localTable) {
$localTable = $this->getTable();
-
+ }
+
if (!($localTable instanceof Centurion_Db_Table_Abstract)) {
$cols = (array) $localTable;
$localTable = $this->getTable();
@@ -362,32 +508,39 @@ public function addDependentTable($rule, $localTable = null, $cols = null, $join
$refTable = Centurion_Db::getSingletonByClassName($refMap['refTableClass']);
$refTableName = $refTable->info(Zend_Db_Table::NAME);
- if (!in_array($joinType, array_keys(self::$_joinMethod)))
+ if (!in_array($joinType, array_keys(self::$_joinMethod))) {
throw new Centurion_Db_Exception(sprintf("unknown join type : %s", $joinType));
+ }
- $tableName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($refTableName));
+ $tableName = $this->_adapter->quoteIdentifier($refTableName);
- do {
- $joinCond = sprintf('%s.%s = %s.%s', $tableName,
- $this->_adapter->quoteIdentifier($refMap['refColumns']),
- $this->_adapter->quoteIdentifier($localTableName),
- $this->_adapter->quoteIdentifier($refMap['columns']));
- $tableName = $this->_uniqueCorrelation($refTableName);
- } while ($this->_isAlreadyJoined($tableName, $joinCond));
-
- if (!$this->_isAlreadyJoined($refTableName, $joinCond)) {
- $tableName = $this->_uniqueCorrelation($tableName);
- }
+ $joinCond = sprintf('%s.%s = %s.%s', $tableName,
+ $this->_adapter->quoteIdentifier($refMap['refColumns']),
+ $this->_adapter->quoteIdentifier($localTableName),
+ $this->_adapter->quoteIdentifier($refMap['columns']));
if (!$this->_isAlreadyJoined($refTableName, $joinCond)) {
$method = self::$_joinMethod[$joinType];
+ /**
+ * We will join to the table. We check that Zend will not generate a autoatic alias because in this case
+ * we must change the join condition
+ */
+ if ($refTableName !== $this->_uniqueCorrelation($refTableName)) {
+ $tableName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($refTableName));
+ $joinCond = sprintf('%s.%s = %s.%s', $tableName,
+ $this->_adapter->quoteIdentifier($refMap['refColumns']),
+ $this->_adapter->quoteIdentifier($localTableName),
+ $this->_adapter->quoteIdentifier($refMap['columns']));
+ }
+
$this->$method($refTableName, $joinCond, array());
}
- if ($cols)
+ if ($cols) {
$this->_tableCols($localTableName, $cols, true);
+ }
- return $this;
+ return $tableName;
}
/**
@@ -418,7 +571,7 @@ public function addOneToOneTable($rule, $localTable = null, $cols = null, $joinT
$depTable = Centurion_Db::getSingletonByClassName($depTableClassName);
$depTableName = $depTable->info(Centurion_Db_Table_Abstract::NAME);
- $tableName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($depTableName));
+ $tableName = $depTableName;
$foreignRefMap = $depTable->getReferenceMap();
if (null == $foreignRule) {
@@ -428,33 +581,48 @@ public function addOneToOneTable($rule, $localTable = null, $cols = null, $joinT
}
}
- if ($refRule['refTableClass'] != $localTableClass)
+ if ($refRule['refTableClass'] != $localTableClass) {
throw new Centurion_Db_Exception(sprintf('The foreign reference rules couldn\'t be found in %s', $depTableClassName));
+ }
} else {
- if (!in_array($foreignRule, array_keys($foreignRefMap)))
+ if (!in_array($foreignRule, array_keys($foreignRefMap))) {
throw new Centurion_Db_Exception('no such rule (%s) for model %s', $foreignRule, $depTableClassName);
+ }
$refRule = $foreignRefMap[$foreignRule];
}
- if (!in_array($joinType, array_keys(self::$_joinMethod)))
+ if (!in_array($joinType, array_keys(self::$_joinMethod))) {
throw new Centurion_Db_Exception(sprintf('unknown join type : %s', $joinType));
+ }
- $joinCond = sprintf('%s.%s = %s.%s', $tableName,
+ $joinCond = sprintf('%s.%s = %s.%s', $this->_adapter->quoteIdentifier($tableName),
$this->_adapter->quoteIdentifier($refRule['columns']),
$this->_adapter->quoteIdentifier($localTableName),
$this->_adapter->quoteIdentifier($refRule['refColumns']));
-
+
if (!$this->_isAlreadyJoined($depTableName, $joinCond)) {
$method = self::$_joinMethod[$joinType];
+ /**
+ * We will join to the table. We check that Zend will not generate a autoatic alias because in this case
+ * we must change the join condition
+ */
+ if ($tableName !== $this->_uniqueCorrelation($tableName)) {
+ $tableName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($tableName));
+ $joinCond = sprintf('%s.%s = %s.%s', $tableName,
+ $this->_adapter->quoteIdentifier($refRule['columns']),
+ $this->_adapter->quoteIdentifier($localTableName),
+ $this->_adapter->quoteIdentifier($refRule['refColumns']));
+ }
$this->$method($depTableName, $joinCond, array());
}
- if ($cols)
+ if ($cols) {
$this->_tableCols($localTableName, $cols, true);
+ }
- return $this;
+ return $tableName;
}
/**
@@ -513,23 +681,20 @@ public function addRelated($columnString, $separator = self::RULES_SEPARATOR)
} elseif (in_array($rule[1], array_keys($dependentRefMap))) {
if (null !== $nextRule) {
$foreignTable = Centurion_Db::getSingletonByClassName($dependentRefMap[$rule[1]]['refTableClass']);
- $uniqName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($foreignTable->info('name')));
- $this->addDependentTable($rule[1], $localTable, array(), $rule[0]);
+ $uniqName = $this->addDependentTable($rule[1], $localTable, array(), $rule[0]);
} else {
$sqlField = sprintf('%s.%s', $uniqName,
$this->_adapter->quoteIdentifier($dependentRefMap[$rule[1]]['columns']));
}
} elseif (in_array($rule[1], array_keys($dependentTables))) {
$foreignTable = Centurion_Db::getSingletonByClassName($dependentTables[$rule[1]]);
- $uniqName = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($foreignTable->info('name')));
- $this->addOneToOneTable($rule[1], $localTable, array(), $rule[0]);
+ $uniqName = $this->addOneToOneTable($rule[1], $localTable, array(), $rule[0]);
} else {
throw new Centurion_Db_Exception(sprintf('%s did not match any external rule', $rule[1]));
}
if (null === $sqlField && in_array($nextRule, $foreignTable->info(Centurion_Db_Table_Abstract::COLS))) {
- $sqlField = sprintf('%s.%s', $uniqName,
- $this->_adapter->quoteIdentifier($nextRule));
+ $sqlField = sprintf('%s.%s', $uniqName, $this->_adapter->quoteIdentifier($nextRule));
}
$localTable = $foreignTable;
View
179 tests/library/Centurion/Db/Table/SelectTest.php
@@ -1,71 +1,152 @@
<?php
-
require_once dirname(__FILE__) . '/../../../../TestHelper.php';
class Centurion_Db_Table_SelectTest extends PHPUnit_Framework_TestCase
{
- public function testFilters()
+ public function testNormalizeCondition()
{
- $userTable = Centurion_Db::getSingleton('auth/user');
- $groupTable = Centurion_Db::getSingleton('auth/group');
- $groupPermissionTable = Centurion_Db::getSingleton('auth/user_permission');
-
- $groupTable->fetchAll(array('name=?' => 'test'))->delete();
- $userTable->fetchAll(array('username=?' => 'test'))->delete();
-
- $userRow = $userTable->insert(array('username' => 'test',
- 'password' => 'test',
- 'user_parent_id' => null,
- 'can_be_deleted' => 1,
- 'retrieve' => true));
-
+ $select = Centurion_Db::getSingleton('user/profile')->select(true);
- $this->assertFalse($userRow === null);
-
- $groupRow = $groupTable->insert(array('name' => 'test',
- 'retrieve' => true));
- $this->assertFalse($groupRow === null);
-
- $filteredGroupRow = $groupTable->get(array('name' => 'test'));
- $filteredUserRow = $userTable->get(array('username' => 'test'));
-
- $this->assertFalse($filteredGroupRow instanceof Zend_Db_Table_Row);
- $this->assertFalse($filteredUserRow instanceof Zend_Db_Table_Row);
-
- $this->assertEquals($groupRow->toArray(), $filteredGroupRow->toArray());
-
- $this->assertEquals($userRow->toArray(), $filteredUserRow->toArray());
-
- $userRow->groups->add($groupRow);
-
- $this->assertFalse($groupRow->has('users', $userRow) === null);
+ $return = $select->normalizeCondition('`auth_user`.`id` = `user_profile`.`user_id`');
+ $this->assertEquals('`auth_user`.`id` = `user_profile`.`user_id`', $return);
+
+ $return = $select->normalizeCondition('`user_profile`.`user_id` = `auth_user`.`id`');
+
+ $this->assertEquals('`auth_user`.`id` = `user_profile`.`user_id`', $return);
+
+ $return = $select->normalizeCondition('`user_profile`.`user_id` > `auth_user`.`id`');
+ $this->assertEquals('`auth_user`.`id` < `user_profile`.`user_id`', $return);
+
+ $return = $select->normalizeCondition('`auth_user`.`id` = `user_id`');
+ $this->assertEquals('`auth_user`.`id` = `user_profile`.`user_id`', $return);
+
+ $return = $select->normalizeCondition('`auth_user` . id = user_id');
+ $this->assertEquals('`auth_user`.`id` = `user_profile`.`user_id`', $return);
+
+ }
+
+ public function testIsConditionEqualsFunction()
+ {
+ $select = Centurion_Db::getSingleton('user/profile')->select(true);
+
+ $full = '`auth_user`.`id` = `user_profile`.`user_id`';
+
+ $this->assertTrue($select->isConditionEquals($full, $full));
+ $this->assertTrue($select->isConditionEquals($full, '`user_profile`.`user_id` = `auth_user`.`id`'));
+ $this->assertTrue($select->isConditionEquals('`auth_user`.`id` = `user_id`', '`user_profile`.`user_id` = `auth_user`.`id`'));
+ $this->assertTrue($select->isConditionEquals($full, '`user_id` = `auth_user`.`id`'));
+ $this->assertTrue($select->isConditionEquals($full, 'auth_user.id = user_profile.user_id'));
+ $this->assertFalse($select->isConditionEquals($full, 'id` = user_profile.`user_id`'), 'Id should be prefixed by current table. Should fail');
+
+ }
+
+ public function testIsAlreadyJoinFunction()
+ {
+ $select = Centurion_Db::getSingleton('user/profile')->select(true);
+ $select->addRelated('user__id');
+
+ $this->assertTrue($select->isAlreadyJoined('auth_user'));
+
+ $this->assertTrue($select->isAlreadyJoined('auth_user', '`auth_user`.`id` = `user_profile`.`user_id`'));
+ $this->assertTrue($select->isAlreadyJoined('auth_user', 'auth_user.id = user_profile.user_id'));
+ $this->assertTrue($select->isAlreadyJoined('auth_user', 'auth_user.`id` = `user_id`'), 'Fail IsAlreadyJoin when no prefix to column in join cond');
+ $this->assertTrue($select->isAlreadyJoined('auth_user', 'auth_user.id = user_profile.user_id'));
+ }
+
+ public function testGetRelatedJoin()
+ {
+ $select = Centurion_Db::getSingleton('poll/participation')->select(true);
+ $select->addRelated('user__id');
- $userRowset = $userTable->filter(array('groups__name' => 'test'));
+ $string = $select->__toString();
- $this->assertFalse(!count($userRowset));
+ $this->assertContains('INNER JOIN', $string, 'Query should contain INNER JOIN after addRelated() call');
+ }
- $this->assertEquals($userRow->toArray(), $userRowset->current()->toArray());
+ /**
+ * @todo: Test the same thing for many dependant
+ */
+ public function tesAddRelatedForReferenceMap()
+ {
+ $select = Centurion_Db::getSingleton('user/profile')->select(true);
- $userRowset = $userTable->filter(array('groups__name__exact' => 'test'));
+ $select->addRelated('user__id');
+ $string = $select->__toString();
- $this->assertFalse(!count($userRowset));
+ $select->addRelated('user__id');
+
+ $this->assertEquals($string, $select->__toString(), 'Relation already exists, queries should be equal');
- $this->assertEquals($userRow->toArray(), $userRowset->current()->toArray());
+ $select->addRelated('user__email');
+ $this->assertEquals($string, $select->__toString(), 'Relation already exists with id field, queries should be equal');
+
+
+ //TODO: check that the request is good
+ //We want to test that in the second join inner the condition use the same alias as zend will generate.
+ $select->addRelated('user__parent_user__id');
+ $this->assertNotEquals($string, $select->__toString());
+ }
+
+ public function testAddRelatedForManyUniq()
+ {
+ $select = Centurion_Db::getSingleton('user/profile')->select(true);
+
}
- public function testMultiJointure()
+ /**
+ * TODO should add user before try to get it.
+ * @
+ */
+ public function testFilter()
{
- $permissionTable = Centurion_Db::getSingleton('auth/permission');
- $permissionRow = $permissionTable->insert(array('name' => 'test', Centurion_Db_Table_Abstract::RETRIEVE_ROW_ON_INSERT => true));
+ $select = Centurion_Db::getSingleton('poll/participation')->select(true);
+
+ $select->filter(array('user__id' => 1));
+ $select->filter(array('user__username' => 'admin'));
- $userOriginalRow = Centurion_Db::getSingleton('auth/user')->insert(array('username' => 'testOriginal', Centurion_Db_Table_Abstract::RETRIEVE_ROW_ON_INSERT => true));
- $userRow = Centurion_Db::getSingleton('auth/user')->insert(array('username' => 'test', 'user_parent_id' => $userOriginalRow->id, Centurion_Db_Table_Abstract::RETRIEVE_ROW_ON_INSERT => true));
+ $adminRow = $select->fetchAll();
- Centurion_Db::getSingleton('auth/user_permission')->insert(array('permission_id' => $permissionRow->id, 'user_id' => $userRow->id));
+ $this->assertNotNull($adminRow);
+ }
+
+ public function testMany()
+ {
+ $select = Centurion_Db::getSingleton('auth/user')->select(true);
+ $select->addRelated('groups__id');
+
+ $this->assertTrue($select->isAlreadyJoined('auth_belong'));
+ $this->assertTrue($select->isAlreadyJoined('auth_belong', '`auth_belong`.`user_id` = `auth_user`.`id`'));
+
+ $select->addRelated('groups__id');
+ $select->addRelated('groups__users__id');
+ }
+
+ public function testDependant()
+ {
+ $select = Centurion_Db::getSingleton('auth/user')->select(true);
- $select = $permissionTable->select(true)->filter(array('users__parent_user__id' => $userOriginalRow->id));
+ $select->joinInner('user_profile', 'user_profile.id = 1', false);
- $this->assertEquals($select->count(), 1, sprintf("Except 1, found %d\n", $select->count()));
+ $select->filter(array('!profiles__id__isnull' => null));
+ //TODO: test that is good
+ }
+
+ public function testJoinToSameTableWithDifferCondition()
+ {
+ $select = Centurion_Db::getSingleton('auth/user')->select(true);
+
+ $select->addRelated('groups__users__id');
+
+ $select->filter(array('parent_user__username' => 'admin'));
+
+ echo $select;
+ die();
+ $select->limit(2);
+
+ $rowSet = $select->fetchAll();
+
+ $this->assertEquals(1, $rowSet->count());
}
-}
+}
+

2 comments on commit 5c650e3

@Floby
Owner

@lchenay is this merged in Centurion/master ?

@Floby
Owner

and if not, why not ?

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