diff --git a/Doctrine/Query.php b/Doctrine/Query.php index ae858dfa6..7220b947e 100644 --- a/Doctrine/Query.php +++ b/Doctrine/Query.php @@ -541,10 +541,14 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable { * */ public static function bracketExplode($str,$d,$e1 = '(',$e2 = ')') { - $str = explode("$d",$str); + if(is_array($d)) + $a = explode("$d",$str); + else + $a = explode("$d",$str); + $i = 0; $term = array(); - foreach($str as $key=>$val) { + foreach($a as $key=>$val) { if (empty($term[$i])) { $term[$i] = trim($val); $s1 = substr_count($term[$i],"$e1"); diff --git a/Doctrine/Query/Where.php b/Doctrine/Query/Where.php index f61dd8a94..00238844d 100644 --- a/Doctrine/Query/Where.php +++ b/Doctrine/Query/Where.php @@ -33,14 +33,17 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { $pos = strpos($field, "("); if($pos !== false) { - $func = substr($field, 0, $pos); - $value = substr($field, ($pos + 1), -1); - $field = array_pop($a); - $reference = implode(".",$a); - $table = $this->query->load($reference, false); + $func = substr($field, 0, $pos); + $value = substr($field, ($pos + 1), -1); + + $values = Doctrine_Query::sqlExplode($value, ','); + + $field = array_pop($a); + $reference = implode(".",$a); + $table = $this->query->load($reference, false); array_pop($a); $reference2 = implode('.', $a); - $alias = $this->query->getTableAlias($reference2); + $alias = $this->query->getTableAlias($reference2); $stack = $this->query->getRelationStack(); $relation = end($stack); @@ -49,16 +52,20 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { switch($func) { case 'contains': - $operator = ' = '; case 'regexp': - $operator = ' RLIKE '; case 'like': - if(empty($relation)) - throw new Doctrine_Query_Exception('DQL function contains can only be used for fields of related components'); + $operator = $this->getOperator($func); - $where = $alias.'.'.$relation->getLocal(). + if(empty($relation)) + throw new Doctrine_Query_Exception('DQL functions contains/regexp/like can only be used for fields of related components'); + + $where = array(); + foreach($values as $value) { + $where[] = $alias.'.'.$relation->getLocal(). ' IN (SELECT '.$relation->getForeign(). - ' FROM '.$relation->getTable()->getTableName().' WHERE '.$field.' = '.$value.')'; + ' FROM '.$relation->getTable()->getTableName().' WHERE '.$field.$operator.$value.')'; + } + $where = implode(' AND ', $where); break; default: throw new Doctrine_Query_Exception('Unknown DQL function: '.$func); @@ -82,11 +89,25 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { $where = $this->query->getTableAlias($reference).'.'.$field.' '.$operator.' '.$value; } } - } else - throw new Doctrine_Query_Exception('Unknown component path. The correct format should be component1.component2 ... componentN.field'); + } return $where; } + public function getOperator($func) { + switch($func) { + case 'contains': + $operator = ' = '; + break; + case 'regexp': + $operator = $this->query->getConnection()->getRegexpOperator(); + break; + case 'like': + $operator = ' LIKE '; + break; + } + return $operator; + } + public function __toString() { return ( ! empty($this->parts))?implode(" AND ", $this->parts):''; } diff --git a/tests/QueryTestCase.php b/tests/QueryTestCase.php index e2c4ccff0..a9394c537 100644 --- a/tests/QueryTestCase.php +++ b/tests/QueryTestCase.php @@ -39,6 +39,16 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { } $this->assertTrue($f); } + public function testBadFunctionLogic() { + $q = new Doctrine_Query(); + $f = false; + try { + $q->from('User')->where('User.name.contains(?)'); + } catch(Doctrine_Query_Exception $e) { + $f = true; + } + $this->assertTrue($f); + } public function testDqlContainsFunction() { $q = new Doctrine_Query(); $this->connection->clear(); @@ -47,21 +57,54 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { $this->assertEqual(count($q->getTableStack()), 2); $this->assertEqual(count($q->getRelationStack()), 1); - //print Doctrine_Lib::formatSql($q->getQuery()); - $coll = $q->execute(array('123 123')); $this->assertEqual($q->getQuery(), 'SELECT entity.id AS entity__id, entity.name AS entity__name, entity.loginname AS entity__loginname, entity.password AS entity__password, entity.type AS entity__type, entity.created AS entity__created, entity.updated AS entity__updated, entity.email_id AS entity__email_id FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT entity_id FROM phonenumber WHERE phonenumber = ?) AND (entity.type = 0)'); - + $this->assertEqual($coll->count(), 3); $this->assertEqual($coll[0]->name, 'zYne'); $this->assertEqual($coll[0]->Phonenumber->count(), 1); $this->assertEqual($coll[1]->Phonenumber->count(), 3); $this->assertEqual($coll[2]->Phonenumber->count(), 1); - - } + public function testDqlFunctionWithMultipleParams() { + $q = new Doctrine_Query(); + $this->connection->clear(); + $q->from('User')->where('User.Phonenumber.phonenumber.like(?,?)'); + $this->assertEqual(count($q->getTableStack()), 2); + $this->assertEqual(count($q->getRelationStack()), 1); + + $coll = $q->execute(array('%123%', '%5%')); + + $this->assertEqual($q->getQuery(), 'SELECT entity.id AS entity__id, entity.name AS entity__name, entity.loginname AS entity__loginname, entity.password AS entity__password, entity.type AS entity__type, entity.created AS entity__created, entity.updated AS entity__updated, entity.email_id AS entity__email_id FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT entity_id FROM phonenumber WHERE phonenumber LIKE ?) AND entity.id IN (SELECT entity_id FROM phonenumber WHERE phonenumber LIKE ?) AND (entity.type = 0)'); + + $this->assertEqual($coll->count(), 3); + $this->assertEqual($coll[0]->name, 'Arnold Schwarzenegger'); + $this->assertEqual($coll[0]->Phonenumber->count(), 3); + $this->assertEqual($coll[1]->Phonenumber->count(), 3); + $this->assertEqual($coll[2]->Phonenumber->count(), 3); + } + public function testDqlLikeFunction() { + $q = new Doctrine_Query(); + $this->connection->clear(); + + $q->from('User')->where('User.Phonenumber.phonenumber.like(?)'); + $this->assertEqual(count($q->getTableStack()), 2); + $this->assertEqual(count($q->getRelationStack()), 1); + + $coll = $q->execute(array('123%')); + + $this->assertEqual($q->getQuery(), 'SELECT entity.id AS entity__id, entity.name AS entity__name, entity.loginname AS entity__loginname, entity.password AS entity__password, entity.type AS entity__type, entity.created AS entity__created, entity.updated AS entity__updated, entity.email_id AS entity__email_id FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT entity_id FROM phonenumber WHERE phonenumber LIKE ?) AND (entity.type = 0)'); + + $this->assertEqual($coll->count(), 5); + $this->assertEqual($coll[0]->name, 'zYne'); + $this->assertEqual($coll[0]->Phonenumber->count(), 1); + $this->assertEqual($coll[1]->Phonenumber->count(), 3); + $this->assertEqual($coll[2]->Phonenumber->count(), 1); + $this->assertEqual($coll[3]->Phonenumber->count(), 3); + $this->assertEqual($coll[4]->Phonenumber->count(), 3); + } public function testEnumConversion() { $e[0] = new EnumTest(); $e[0]->status = 'open'; diff --git a/tests/run.php b/tests/run.php index 6f5383e3f..0ff8d74d8 100644 --- a/tests/run.php +++ b/tests/run.php @@ -31,7 +31,7 @@ require_once("ImportTestCase.php"); error_reporting(E_ALL); $test = new GroupTest("Doctrine Framework Unit Tests"); -/** + $test->addTestCase(new Doctrine_AccessTestCase()); $test->addTestCase(new Doctrine_EventListenerTestCase()); @@ -70,10 +70,10 @@ $test->addTestCase(new Doctrine_SchemaTestCase()); $test->addTestCase(new Doctrine_ImportTestCase()); -$test->addTestCase(new Doctrine_ValidatorTestCase()); +$test->addTestCase(new Doctrine_ValidatorTestCase()); $test->addTestCase(new Doctrine_CollectionTestCase()); -*/ + $test->addTestCase(new Doctrine_QueryTestCase());