diff --git a/src/BaseQuery.php b/src/BaseQuery.php index 323b9c4..cb9abcc 100644 --- a/src/BaseQuery.php +++ b/src/BaseQuery.php @@ -11,20 +11,6 @@ class BaseQuery { - /** - * The database the query should be executed on - * - * @var string - */ - protected $database = null; - - /** - * The table the query should be executed on - * - * @var string - */ - protected $table = null; - /** * Query builder callback macros * @@ -37,21 +23,43 @@ class BaseQuery * * @var callable */ - private $resultFetcher = null; + protected $resultFetcher = null; + + /** + * Construct new query object and inherit properties + * + * @param BaseQuery $parent + * @return void + */ + final public function __construct(BaseQuery $parent = null) + { + if (!is_null($parent)) + { + $this->inheritFromParent($parent); + } + } + + /** + * Inherit property values from parent query + * + * @param BaseQuery $parent + * @return void + */ + protected function inheritFromParent(BaseQuery $parent) + { + $this->macros = $parent->macros; + $this->resultFetcher = $parent->resultFetcher; + } /** - * Construct new query object + * Set the result fetcher of the query * * @param callable $resultFetcher - * @param string $table - * @param string $database * @return void */ - public function __construct($resultFetcher = null, $table = null, $database = null) + public function setResultFetcher($resultFetcher = null) { $this->resultFetcher = $resultFetcher; - $this->table = $table; - $this->database = $database; } /** @@ -132,14 +140,24 @@ final public function attributes() } /** - * Creates another query instance with the current parameters - * - * @param string $className - * @return ClanCats\Hydrahon\BaseQuery + * Overwrite the query attributes + * + * Jesuz only use this if you really really know what your are doing + * otherwise you might break stuff add sql injection and all other bad stuff.. + * + * @return array */ - final protected function createSubQuery($className) + final public function overwriteAttributes($attributes) { - return new $className($this->resultFetcher, $this->table, $this->database); + foreach($attributes as $key => $attribute) + { + if (isset($this->{$key})) + { + $this->{$key} = $attribute; + } + } + + return $attributes; } /** diff --git a/src/Builder.php b/src/Builder.php index 201f028..4f43c22 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -100,62 +100,58 @@ public function __construct($grammarKey, $executionCallback) $this->executionCallback = $executionCallback; // prepare the current grammar - list($this->queryClass, $translatorClass) = static::$grammar[$grammarKey]; + list($queryBuilderClass, $translatorClass) = static::$grammar[$grammarKey]; + + // create the query builder specific instances $this->queryTranslator = new $translatorClass; + $this->queryBuilder = new $queryBuilderClass; + + // assign the result fetcher + $this->queryBuilder->setResultFetcher(array($this, 'executeQuery')); // check if the translator is valid if (!$this->queryTranslator instanceof TranslatorInterface) { throw new Exception('A query translator must implement the "TranslatorInterface" interface.'); } + + // check if the query builder is an instance of Base Query + if (!$this->queryBuilder instanceof BaseQuery) + { + throw new Exception('A query builder must be an instance of the "BaseQuery".'); + } } /** - * Creates a new query object with the given table and database and - * sets the query table and optinal the database seperated by a dott + * Forwards calls to the current query builder * * @param string $table * @return ClanCats\Hydrahon\BaseQuery */ - public function table($table) + public function __call($method, $arguments) { - $database = null; - - if (is_string($table) && strpos($table, '.') !== false) - { - $selection = explode('.', $table); - - if (count($selection) !== 2) - { - throw new Exception( 'Invalid argument given. You can only define one seperator.' ); - } - - list($database, $table) = $selection; - } - - // the table might include an alias we need to parse that one out - if (is_string($table) && strpos($table, ' as ') !== false) - { - $tableParts = explode(' as ', $table); - $table = array($tableParts[0] => $tableParts[1]); - unset($tableParts); - } + return call_user_func_array(array($this->queryBuilder, $method), $arguments); + } - // create and return new query instance - return new $this->queryClass(array($this, 'executeQuery'), $table, $database); + /** + * Translate the given query + * + * @param BaseQuery $query + * @return array + */ + public function translateQuery(BaseQuery $query) + { + return $this->queryTranslator->translate($query); } /** * Translate a query and run the current execution callback * - * @param ClanCats\Hydrahon\BaseQuery $query + * @param BaseQuery $query * @return mixed */ public function executeQuery(BaseQuery $query) { - $translatedArguments = $this->queryTranslator->translate($query); - $translatedArguments = array_merge(array($query), $translatedArguments); - - return call_user_func_array($this->executionCallback, $translatedArguments); + return call_user_func_array($this->executionCallback, array_merge(array($query), $this->translateQuery($query))); } } diff --git a/src/Query/Sql.php b/src/Query/Sql.php index 438233a..a25db59 100644 --- a/src/Query/Sql.php +++ b/src/Query/Sql.php @@ -9,80 +9,101 @@ use ClanCats\Hydrahon\BaseQuery; +use ClanCats\Hydrahon\Query\Sql\Select; +use ClanCats\Hydrahon\Query\Sql\Insert; +use ClanCats\Hydrahon\Query\Sql\Update; +use ClanCats\Hydrahon\Query\Sql\Delete; +use ClanCats\Hydrahon\Query\Sql\Drop; +use ClanCats\Hydrahon\Query\Sql\Truncate; +use ClanCats\Hydrahon\Query\Sql\Table; + class Sql extends BaseQuery { + /** + * Create a new table instance + * + * $h->table('users') + * + * @param string|array $fields + * @return Select + */ + public function table($table = null, $alias = null) + { + $query = new Table($this); return $query->table($table, $alias); + } + /** * Create a new select query builder * - * $h->table('users')->select(['name', 'age']) + * $h->select('users', ['name', 'age']) * * @param string|array $fields - * @return ClanCats\Hydrahon\Query\Sql\Select + * @return Select */ - public function select($fields = null) + public function select($table = null, $fields = null) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Select')->fields($fields); + return $this->table($table)->select($fields); } /** * Create a new insert query builder * - * $h->table('users')->insert(['name' => 'Lucas', 'age' => 21]) + * $h->insert('users', ['name' => 'Lucas', 'age' => 21]) * * @param array $values - * @return ClanCats\Hydrahon\Query\Sql\Insert + * @return Insert */ - public function insert(array $values = array()) + public function insert($table = null, array $values = array()) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Insert')->values($values); + return $this->table($table)->insert($values); } /** * Create a new update query builder * - * $h->table('users')->update(['age' => 25])->where('name', 'Johanna') + * $h->update('users', ['age' => 25])->where('name', 'Johanna') * * @param array $values - * @return ClanCats\Hydrahon\Query\Sql\Update + * @return Update */ - public function update(array $values = array()) + public function update($table = null, array $values = array()) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Update')->set($values); + return $this->table($table)->update($values); } /** * Create a new delete sql builder * - * $h->table('users')->delete()->where('age', '<', '18') + * $h->delete('users')->where('age', '<', '18') * - * @return ClanCats\Hydrahon\Query\Sql\Delete + * @return Delete */ - public function delete() + public function delete($table = null) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Delete'); + return $this->table($table)->delete(); } /** * Create a new drop table query * - * $h->table('users')->drop() + * $h->drop('users') * - * @return ClanCats\Hydrahon\Query\Sql\Drop + * @return Drop */ - public function drop() + public function drop($table = null) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Drop'); + return $this->table($table)->drop(); } /** * Create a new truncate table query * - * $h->table('users')->truncate() + * $h->truncate('users') * - * @return ClanCats\Hydrahon\Query\Sql\Truncate + * @return Truncate */ - public function truncate() + public function truncate($table = null) { - return $this->createSubQuery(__NAMESPACE__ . '\\Sql\\Truncate'); + return $this->table($table)->truncate(); } } \ No newline at end of file diff --git a/src/Query/Sql/Base.php b/src/Query/Sql/Base.php new file mode 100644 index 0000000..c8fc58b --- /dev/null +++ b/src/Query/Sql/Base.php @@ -0,0 +1,124 @@ +database)) { + $this->database = $parent->database; + } + if (isset($parent->table)) { + $this->table = $parent->table; + } + } + + /** + * Create a new select query builder + * + * // selecting a table + * $h->table('users') + * + * // selecting table and database + * $h->table('db_mydatabase.posts') + * + * @param string $table + * @return self + */ + public function table($table, $alias = null) + { + $database = null; + + // Check if the table is an object, this means + // we have an subselect inside the table + if (is_object($table) && ($table instanceof \Closure)) + { + // we have to check if an alias isset + // otherwise throw an exception to prevent the + // "Every derived table must have its own alias" error + if (is_null($alias)) + { + throw new Exception('You must define an alias when working with subselects.'); + } + + $table = array($alias => $table); + } + + // Check if the $table is an array and the value is an closure + // that we can pass a new query object as subquery + if (is_array($table) && is_object(reset($table)) && (reset($table) instanceof \Closure)) + { + $alias = key($table); + $table = reset($table); + + // create new query object + $subquery = new Select; + + // run the closure callback on the sub query + call_user_func_array($table, array(&$subquery)); + + // set the table + // IMPORTANT: Only if we have a closure as table + // we set the alias as key. This might cause some confusion + // but only this way we can keep the normal ['table' => 'alias'] syntax + $table = array($alias => $subquery); + } + + // other wise normally try to split the table and database name + elseif (is_string($table) && strpos($table, '.') !== false) + { + $selection = explode('.', $table); + + if (count($selection) !== 2) + { + throw new Exception( 'Invalid argument given. You can only define one seperator.' ); + } + + list($database, $table) = $selection; + } + + // the table might include an alias we need to parse that one out + if (is_string($table) && strpos($table, ' as ') !== false) + { + $tableParts = explode(' as ', $table); + $table = array($tableParts[0] => $tableParts[1]); + } + + // assing the result + $this->database = $database; + $this->table = $table; + + // return self + return $this; + } +} diff --git a/src/Query/Sql/Delete.php b/src/Query/Sql/Delete.php index 6389b4f..742a15f 100644 --- a/src/Query/Sql/Delete.php +++ b/src/Query/Sql/Delete.php @@ -7,7 +7,7 @@ * @copyright 2015 Mario Döring */ -class Delete extends BaseSql +class Delete extends SelectBase { // currently delete does nothing sepecial so go on do other stuff.. } diff --git a/src/Query/Sql/Drop.php b/src/Query/Sql/Drop.php index b85cc37..19a6d62 100644 --- a/src/Query/Sql/Drop.php +++ b/src/Query/Sql/Drop.php @@ -9,7 +9,7 @@ use ClanCats\Hydrahon\BaseQuery; -class Drop extends BaseQuery +class Drop extends Base { // also here we need the class only to identify the query } diff --git a/src/Query/Sql/Insert.php b/src/Query/Sql/Insert.php index 7ff5d32..72d1ae1 100644 --- a/src/Query/Sql/Insert.php +++ b/src/Query/Sql/Insert.php @@ -9,7 +9,7 @@ use ClanCats\Hydrahon\BaseQuery; -class Insert extends BaseQuery +class Insert extends Base { /** * values container diff --git a/src/Query/Sql/Select.php b/src/Query/Sql/Select.php index b267f46..00b0f47 100644 --- a/src/Query/Sql/Select.php +++ b/src/Query/Sql/Select.php @@ -1,4 +1,6 @@ -joins[] = array($type, $table, $subquery); return $this; + } + $this->joins[] = array($type, $table, $localKey, $operator, $referenceKey); return $this; } @@ -309,7 +325,7 @@ public function join($table, $localKey, $operator, $referenceKey, $type = 'left' * * @return self */ - public function leftJoin($table, $localKey, $operator, $referenceKey) + public function leftJoin($table, $localKey, $operator = null, $referenceKey = null) { return $this->join($table, $localKey, $operator, $referenceKey, 'left'); } @@ -324,7 +340,7 @@ public function leftJoin($table, $localKey, $operator, $referenceKey) * * @return self */ - public function rightJoin($table, $localKey, $operator, $referenceKey) + public function rightJoin($table, $localKey, $operator = null, $referenceKey = null) { return $this->join($table, $localKey, $operator, $referenceKey, 'right'); } @@ -339,7 +355,7 @@ public function rightJoin($table, $localKey, $operator, $referenceKey) * * @return self */ - public function innerJoin($table, $localKey, $operator, $referenceKey) + public function innerJoin($table, $localKey, $operator = null, $referenceKey = null) { return $this->join($table, $localKey, $operator, $referenceKey, 'inner'); } @@ -354,7 +370,7 @@ public function innerJoin($table, $localKey, $operator, $referenceKey) * * @return self */ - public function outerJoin($table, $localKey, $operator, $referenceKey) + public function outerJoin($table, $localKey, $operator = null, $referenceKey = null) { return $this->join($table, $localKey, $operator, $referenceKey, 'outer'); } diff --git a/src/Query/Sql/BaseSql.php b/src/Query/Sql/SelectBase.php similarity index 96% rename from src/Query/Sql/BaseSql.php rename to src/Query/Sql/SelectBase.php index ae1db3f..fdc94db 100644 --- a/src/Query/Sql/BaseSql.php +++ b/src/Query/Sql/SelectBase.php @@ -1,16 +1,19 @@ - $val) { $subquery->where($key, $val, null, $type); @@ -101,7 +104,7 @@ public function where($column, $param1 = null, $param2 = null, $type = 'and') if (is_object($column) && ($column instanceof \Closure)) { // create new query object - $subquery = new BaseSql; + $subquery = new SelectBase; // run the closure callback on the sub query call_user_func_array($column, array( &$subquery )); diff --git a/src/Query/Sql/SelectJoin.php b/src/Query/Sql/SelectJoin.php new file mode 100644 index 0000000..09b8e70 --- /dev/null +++ b/src/Query/Sql/SelectJoin.php @@ -0,0 +1,71 @@ +ons[] = array($type, $localKey, $operator, $referenceKey); return $this; + } + + /** + * Add an or on condition to the join object + * + * @param string $localKey + * @param string $operator + * @param string $referenceKey + * + * @return self + */ + public function orOn($localKey, $operator, $referenceKey) + { + $this->on($localKey, $operator, $referenceKey, 'or'); return $this; + } + + /** + * Add an and on condition to the join object + * + * @param string $localKey + * @param string $operator + * @param string $referenceKey + * + * @return self + */ + public function andOn($localKey, $operator, $referenceKey) + { + $this->on($localKey, $operator, $referenceKey, 'and'); return $this; + } +} diff --git a/src/Query/Sql/Table.php b/src/Query/Sql/Table.php new file mode 100644 index 0000000..15f607c --- /dev/null +++ b/src/Query/Sql/Table.php @@ -0,0 +1,88 @@ +table('users')->select(['name', 'age']) + * + * @param string|array $fields + * @return Select + */ + public function select($fields = null) + { + $query = new Select($this); return $query->fields($fields); + } + + /** + * Create a new insert query builder + * + * $h->table('users')->insert(['name' => 'Lucas', 'age' => 21]) + * + * @param array $values + * @return Insert + */ + public function insert(array $values = array()) + { + $query = new Insert($this); return $query->values($values); + } + + /** + * Create a new update query builder + * + * $h->table('users')->update(['age' => 25])->where('name', 'Johanna') + * + * @param array $values + * @return Update + */ + public function update(array $values = array()) + { + $query = new Update($this); return $query->set($values); + } + + /** + * Create a new delete sql builder + * + * $h->table('users')->delete()->where('age', '<', '18') + * + * @return Delete + */ + public function delete() + { + return new Delete($this); + } + + /** + * Create a new drop table query + * + * $h->table('users')->drop() + * + * @return Drop + */ + public function drop() + { + return new Drop($this); + } + + /** + * Create a new truncate table query + * + * $h->table('users')->truncate() + * + * @return Truncate + */ + public function truncate() + { + return new Truncate($this); + } +} \ No newline at end of file diff --git a/src/Query/Sql/Truncate.php b/src/Query/Sql/Truncate.php index ef05d7b..7b6f5ac 100644 --- a/src/Query/Sql/Truncate.php +++ b/src/Query/Sql/Truncate.php @@ -9,7 +9,7 @@ use ClanCats\Hydrahon\BaseQuery; -class Truncate extends BaseQuery +class Truncate extends Base { // also here we need the class only to identify the query } diff --git a/src/Query/Sql/Update.php b/src/Query/Sql/Update.php index 97c3136..de85d48 100644 --- a/src/Query/Sql/Update.php +++ b/src/Query/Sql/Update.php @@ -7,7 +7,7 @@ * @copyright 2015 Mario Döring */ -class Update extends BaseSql +class Update extends SelectBase { /** * values container diff --git a/src/Translator/Mysql.php b/src/Translator/Mysql.php index c0bb87a..ff324c8 100644 --- a/src/Translator/Mysql.php +++ b/src/Translator/Mysql.php @@ -291,10 +291,30 @@ protected function escapeTable($allowAlias = true) $buffer .= $this->escape($database) . '.'; } + // when the table is an array we have a table with alias if (is_array($table)) { - reset($table); + reset($table); + // the table might be a subselect so check that + // first and compile the select if it is one + if ($table[key($table)] instanceof Select) + { + $translator = new static; + + // translate the subselect + list($subQuery, $subQueryParameters) = $translator->translate($table[key($table)]); + + // merge the parameters + foreach($subQueryParameters as $parameter) + { + $this->addParameter($parameter); + } + + return '(' . $subQuery . ') as ' . $this->escape(key($table)); + } + + // otherwise continue with normal table if ($allowAlias) { $table = key($table) . ' as ' . $table[key($table)]; @@ -323,7 +343,7 @@ protected function parameterize($params) } /* - * -- FROM HER TRANSLATE FUNCTIONS FOLLOW + * -- FROM HERE TRANSLATE FUNCTIONS FOLLOW */ /** @@ -381,7 +401,7 @@ protected function translateUpdate() } // build offset and limit - if ($this->attr('limit') || $this->attr('offset')) + if ($this->attr('limit')) { $build .= $this->translateLimit(); } @@ -405,7 +425,7 @@ protected function translateDelete() } // build offset and limit - if ($this->attr('limit') || $this->attr('offset')) + if ($this->attr('limit')) { $build .= $this->translateLimit(); } @@ -541,8 +561,44 @@ protected function translateJoins() foreach ($this->attr('joins') as $join) { - list($type, $table, $localKey, $operator, $referenceKey) = $join; - $build .= ' ' . $type . ' join ' . $this->escape($table) . ' on ' . $this->escape($localKey) . ' ' . $operator . ' ' . $this->escape($referenceKey); + // get the type and table + $type = $join[0]; $table = $join[1]; + + // start the join + $build .= ' ' . $type . ' join ' . $this->escape($table) . ' on '; + + // to make nested join conditions possible you can pass an closure + // wich will create a new query where you can add your nested ons and wheres + if (!isset($join[3]) && isset($join[2]) && $join[2] instanceof BaseQuery) + { + $subAttributes = $join[2]->attributes(); + + $joinConditions = ''; + + // remove the first type from the ons + reset($subAttributes['ons']); + $subAttributes['ons'][key($subAttributes['ons'])][0] = ''; + + foreach($subAttributes['ons'] as $on) + { + list($type, $localKey, $operator, $referenceKey) = $on; + $joinConditions .= ' ' . $type . ' ' . $this->escape($localKey) . ' ' . $operator . ' ' . $this->escape($referenceKey); + } + + $build .= trim($joinConditions); + + // compile the where if set + if (!empty($subAttributes['wheres'])) + { + $build .= ' and ' . substr($this->translateWhere($subAttributes['wheres']), 7); + } + } + else + { + // othewise default join + list($type, $table, $localKey, $operator, $referenceKey) = $join; + $build .= $this->escape($localKey) . ' ' . $operator . ' ' . $this->escape($referenceKey); + } } return $build; diff --git a/tests/Builder.php b/tests/Builder.php index 8c340ed..f8d3960 100644 --- a/tests/Builder.php +++ b/tests/Builder.php @@ -71,7 +71,6 @@ public function testConsturctInvalidTableArgument() $hydrahon = new Builder('mysql', function() {}); $hydrahon->table('foo.bar.fail'); } - /** * Builder::constuct test * @@ -79,8 +78,21 @@ public function testConsturctInvalidTableArgument() */ public function testConsturctInvalidTransltorClass() { - Builder::extend('invalidtranslator', '\\This\\Should\\Work', '\\ClanCats\\Hydrahon\\Exception'); - // but now it should faile + Builder::extend('invalidtranslator', 'ClanCats\\Hydrahon\\Query\\Sql', 'ClanCats\\Hydrahon\\Exception'); + + // but now it should fail + $hydrahon = new Builder('invalidtranslator', function() {}); + } + + /** + * Builder::constuct test + * + * @expectedException ClanCats\Hydrahon\Exception + */ + public function testConsturctInvalidBuilderClass() + { + Builder::extend('invalidBuilder', 'ClanCats\\Hydrahon\\Exception','ClanCats\\Hydrahon\\Translator\\Mysql'); + // but now it should fail $hydrahon = new Builder('invalidtranslator', function() {}); } } \ No newline at end of file diff --git a/tests/Query/QueryCase.php b/tests/Query/QueryCase.php index dbb8335..d4fc794 100644 --- a/tests/Query/QueryCase.php +++ b/tests/Query/QueryCase.php @@ -22,16 +22,20 @@ abstract class Query_QueryCase extends \PHPUnit_Framework_TestCase */ protected function createQuery($result = null) { - return new $this->queryClass(function( $query ) use( $result ) { + $query = new $this->queryClass; + $query->setResultFetcher(function() use($result) + { return $result; - } , 'phpunit', 'db_phpunit' ); + }); + + return $query; } /** * Returns all attributes or a specific one * - * @param ClanCats\Hydrahon\Query\Sql\BaseQuery $query - * @param string $key + * @param BaseQuery $query + * @param string $key * @return mixed */ protected function attributes(BaseQuery $query, $key = null) @@ -50,6 +54,17 @@ protected function attributes(BaseQuery $query, $key = null) } } } + + if ($queryKey === 'joins' && is_array($value)) + { + foreach($value as &$join) + { + if (isset($join[2]) && $join[2] instanceof BaseQuery) + { + $join[2] = $this->attributes($join[2]); + } + } + } } if (!is_null($key)) @@ -74,11 +89,6 @@ protected function attributes(BaseQuery $query, $key = null) */ protected function assertAttributes(BaseQuery $query, array $attributes = array()) { - $attributes = array_merge(array( - 'database' => 'db_phpunit', - 'table' => 'phpunit' - ), $attributes); - $this->assertEquals($attributes, $this->attributes($query)); } } \ No newline at end of file diff --git a/tests/Query/Sql/BaseSql.php b/tests/Query/Sql/BaseSql.php index 0f9ddcf..8d7ab93 100644 --- a/tests/Query/Sql/BaseSql.php +++ b/tests/Query/Sql/BaseSql.php @@ -15,7 +15,7 @@ class Query_Sql_BaseSql_Test extends Query_QueryCase { - protected $queryClass = 'ClanCats\\Hydrahon\\Query\\Sql\\BaseSql'; + protected $queryClass = 'ClanCats\\Hydrahon\\Query\\Sql\\SelectBase'; /** * BaseSql::construct @@ -237,4 +237,19 @@ public function testPage() // custom page size $this->assertAttributes($this->createQuery()->page(2, 5), array('limit' => 5, 'offset' => 10)); } + + /** + * BaseSql::page + */ + public function testOverwriteAttributes() + { + $query = $this->createQuery(); + $query->where('id', 42); + + $this->assertAttributes($query, array('wheres' => array(array('where', 'id', '=', 42)))); + + // overwrite the wheres + $query->overwriteAttributes(array('wheres' => array())); + $this->assertAttributes($query, array()); + } } \ No newline at end of file diff --git a/tests/Query/Sql/Select.php b/tests/Query/Sql/Select.php index 5f3f9a2..2645a3a 100644 --- a/tests/Query/Sql/Select.php +++ b/tests/Query/Sql/Select.php @@ -153,6 +153,58 @@ public function testJoin() $this->assertAttributes($this->createQuery()->outerJoin('avatars', 'users.id', '=', 'avatars.user_id'), array('joins' => array(array('outer', 'avatars', 'users.id', '=', 'avatars.user_id')))); } + /** + * Select::join + */ + public function testJoinCallbacks() + { + // simple clojure + $this->assertAttributes($this->createQuery() + ->join('avatars', function($join) { + $join->on('user.id', '=', 'avatars.user_id'); + }), array( + 'joins' => array( + array('left', 'avatars', array( 'ons' => array( + array('and', 'user.id', '=', 'avatars.user_id'), + ))), + ) + )); + + // with or + $this->assertAttributes($this->createQuery() + ->join('avatars', function($join) { + $join->on('user.id', '=', 'avatars.user_id'); + $join->orOn('user.id', '=', 'avatars.other_user_id'); + }), array( + 'joins' => array( + array('left', 'avatars', array( 'ons' => array( + array('and', 'user.id', '=', 'avatars.user_id'), + array('or', 'user.id', '=', 'avatars.other_user_id'), + ))), + ) + )); + + // with or and wheres + $this->assertAttributes($this->createQuery() + ->join('avatars', function($join) { + $join->on('user.id', '=', 'avatars.user_id'); + $join->orOn('user.id', '=', 'avatars.other_user_id'); + $join->where('avatars.active', 1); + }), array( + 'joins' => array( + array('left', 'avatars', array( + 'ons' => array( + array('and', 'user.id', '=', 'avatars.user_id'), + array('or', 'user.id', '=', 'avatars.other_user_id'), + ), + 'wheres' => array( + array('where', 'avatars.active', '=', 1), + ), + )), + ) + )); + } + /** * Select::run */ diff --git a/tests/Translator/Mysql.php b/tests/Translator/Mysql.php index c6da7f8..a2deaf3 100644 --- a/tests/Translator/Mysql.php +++ b/tests/Translator/Mysql.php @@ -409,6 +409,68 @@ public function testSelectJoins() }); } + /** + * mysql grammar tests + */ + public function testSelectJoinMulticonditions() + { + // simple + $this->assertQueryTranslation('select * from `users` left join `avatars` on `avatars`.`user_id` = `users`.`id`', array(), function($q) + { + return $q->table('users')->select() + ->join('avatars', function($q) + { + $q->on('avatars.user_id', '=', 'users.id'); + }); + }); + + // multiple + $this->assertQueryTranslation('select * from `users` left join `avatars` on `avatars`.`user_id` = `users`.`id` or `avatars`.`other_user_id` = `users`.`id`', array(), function($q) + { + return $q->table('users')->select() + ->join('avatars', function($q) + { + $q->on('avatars.user_id', '=', 'users.id'); + $q->orOn('avatars.other_user_id', '=', 'users.id'); + }); + }); + + // and + $this->assertQueryTranslation('select * from `users` left join `avatars` on `avatars`.`user_id` = `users`.`id` and `avatars`.`other_user_id` = `users`.`id`', array(), function($q) + { + return $q->table('users')->select() + ->join('avatars', function($q) + { + $q->on('avatars.user_id', '=', 'users.id'); + $q->on('avatars.other_user_id', '=', 'users.id'); + }); + }); + + // with wheres + $this->assertQueryTranslation('select * from `users` left join `avatars` on `avatars`.`user_id` = `users`.`id` and `avatars`.`other_user_id` = `users`.`id` and `avatars`.`active` = ?', array(1), function($q) + { + return $q->table('users')->select() + ->join('avatars', function($q) + { + $q->on('avatars.user_id', '=', 'users.id'); + $q->on('avatars.other_user_id', '=', 'users.id'); + $q->where('avatars.active', 1); + }); + }); + + // with or wheres + $this->assertQueryTranslation('select * from `users` left join `avatars` on `avatars`.`user_id` = `users`.`id` and `avatars`.`active` = ? or `avatar`.`public` = ?', array(1, 1), function($q) + { + return $q->table('users')->select() + ->join('avatars', function($q) + { + $q->on('avatars.user_id', '=', 'users.id'); + $q->where('avatars.active', 1); + $q->orWhere('avatar.public', 1); + }); + }); + } + /** * mysql grammar tests */