diff --git a/framework/db/ar/ActiveFinder.php b/framework/db/ar/ActiveFinder.php index ba8d30d..586476c 100644 --- a/framework/db/ar/ActiveFinder.php +++ b/framework/db/ar/ActiveFinder.php @@ -17,7 +17,8 @@ use yii\db\Exception; /** * ActiveFinder.php is ... * todo: add SQL monitor - * + * todo: better handling on join() support in QueryBuilder: use regexp to detect table name and quote it + * todo: do not support anonymous parameter binding * todo: add ActiveFinderBuilder * todo: quote join/on part of the relational query * todo: modify QueryBuilder about join() methods @@ -29,6 +30,7 @@ use yii\db\Exception; * todo: lazy loading * todo: scope * todo: test via option + * todo: count, sum, exists * * @property integer $count * @@ -80,6 +82,10 @@ class ActiveFinder extends \yii\base\Object implements \IteratorAggregate, \Arra $this->query = new Query; } + /** + * Executes query and returns all results as an array. + * @return array the query results. If the query results in nothing, an empty array will be returned. + */ public function all() { if ($this->records === null) { @@ -89,15 +95,14 @@ class ActiveFinder extends \yii\base\Object implements \IteratorAggregate, \Arra } /** - * @param boolean $limitOne - * @return null|ActiveRecord + * Executes query and returns a single row of result. + * @return null|array|ActiveRecord the single row of query result. Depending on the setting of [[asArray]], + * the query result may be either an array or an ActiveRecord object. Null will be returned + * if the query results in nothing. */ - public function one($limitOne = true) + public function one() { if ($this->records === null) { - if ($limitOne) { - $this->limit(1); - } $this->records = $this->findRecords(); } return isset($this->records[0]) ? $this->records[0] : null; @@ -442,96 +447,73 @@ class ActiveFinder extends \yii\base\Object implements \IteratorAggregate, \Arra } /** - * Appends an INNER JOIN part to the query. + * Appends a JOIN part to the query. + * The first parameter specifies what type of join it is. + * @param string $type the type of join, such as INNER JOIN, LEFT JOIN. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. - * @return ActiveFinder the query object itself + * @return Query the query object itself */ - public function join($table, $condition, $params = array()) + public function join($type, $table, $on = '', $params = array()) { - if (is_array($params)) { - $this->query->join($table, $condition, $params); - } else { - call_user_func_array(array($this->query, __FUNCTION__), func_get_args()); - } + $this->query->join($type, $table, $on, $params); return $this; } /** - * Appends a LEFT OUTER JOIN part to the query. + * Appends an INNER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. - * @param array $params the parameters (name=>value) to be bound to the query + * @param array $params the parameters (name=>value) to be bound to the query. + * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return ActiveFinder the query object itself */ - public function leftJoin($table, $condition, $params = array()) + public function innerJoin($table, $on, $params = array()) { - if (is_array($params)) { - $this->query->leftJoin($table, $condition, $params); - } else { - call_user_func_array(array($this->query, __FUNCTION__), func_get_args()); - } + $this->query->join($table, $on, $params); return $this; } /** - * Appends a RIGHT OUTER JOIN part to the query. + * Appends a LEFT OUTER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query * @return ActiveFinder the query object itself */ - public function rightJoin($table, $condition, $params = array()) - { - if (is_array($params)) { - $this->query->rightJoin($table, $condition, $params); - } else { - call_user_func_array(array($this->query, __FUNCTION__), func_get_args()); - } - return $this; - } - - /** - * Appends a CROSS JOIN part to the query. - * Note that not all DBMS support CROSS JOIN. - * @param string $table the table to be joined. - * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). - * The method will automatically quote the table name unless it contains some parenthesis - * (which means the table is given as a sub-query or DB expression). - * @return ActiveFinder the query object itself - */ - public function crossJoin($table) + public function leftJoin($table, $on, $params = array()) { - $this->query->crossJoin($table); + $this->query->leftJoin($table, $on, $params); return $this; } /** - * Appends a NATURAL JOIN part to the query. - * Note that not all DBMS support NATURAL JOIN. + * Appends a RIGHT OUTER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). + * @param string|array $on the join condition that should appear in the ON part. + * Please refer to [[where()]] on how to specify this parameter. + * @param array $params the parameters (name=>value) to be bound to the query * @return ActiveFinder the query object itself */ - public function naturalJoin($table) + public function rightJoin($table, $on, $params = array()) { - $this->query->naturalJoin($table); + $this->query->rightJoin($table, $on, $params); return $this; } @@ -746,7 +728,7 @@ class ActiveFinder extends \yii\base\Object implements \IteratorAggregate, \Arra $rows = $command->queryAll(); - if (!empty($this->with)) { + if (isset($joinTree)) { foreach ($rows as $row) { $joinTree->populateData($row); } diff --git a/framework/db/ar/ActiveRecord.php b/framework/db/ar/ActiveRecord.php index 29f0a88..fbde605 100644 --- a/framework/db/ar/ActiveRecord.php +++ b/framework/db/ar/ActiveRecord.php @@ -50,6 +50,17 @@ abstract class ActiveRecord extends Model } /** + * Returns the database connection used by this AR class. + * By default, the "db" application component is used as the database connection. + * You may override this method if you want to use a different database connection. + * @return Connection the database connection used by this AR class. + */ + public static function getDbConnection() + { + return \Yii::$application->getDb(); + } + + /** * Creates an [[ActiveFinder]] instance for query purpose. * * Because [[ActiveFinder]] implements a set of query building methods, @@ -105,10 +116,6 @@ abstract class ActiveRecord extends Model */ public static function findBySql($sql, $params = array()) { - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $finder = static::createActiveFinder(); $finder->sql = $sql; return $finder->params($params); @@ -155,17 +162,6 @@ abstract class ActiveRecord extends Model } /** - * Returns the database connection used by this AR class. - * By default, the "db" application component is used as the database connection. - * You may override this method if you want to use a different database connection. - * @return Connection the database connection used by this AR class. - */ - public static function getDbConnection() - { - return \Yii::$application->getDb(); - } - - /** * Declares the name of the database table associated with this AR class. * By default this method returns the class name as the table name by calling [[Text::camel2id()]]. * For example, 'Customer' becomes 'customer', and 'OrderDetail' becomes 'order_detail'. diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php index c6d6db6..16bf1e2 100644 --- a/framework/db/dao/Query.php +++ b/framework/db/dao/Query.php @@ -169,17 +169,10 @@ class Query extends \yii\base\Object * @param string|array $condition the conditions that will be put in the WHERE part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself */ public function update($table, $columns, $condition = '', $params = array()) { - if (!is_array($params)) { - $params = func_get_args(); - array_shift($params); - array_shift($params); - unset($params[0]); - } $this->addParams($params); $this->operation = array(__FUNCTION__, $table, $columns, $condition, array()); return $this; @@ -191,16 +184,10 @@ class Query extends \yii\base\Object * @param string|array $condition the conditions that will be put in the WHERE part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself */ public function delete($table, $condition = '', $params = array()) { - if (!is_array($params)) { - $params = func_get_args(); - array_shift($params); - unset($params[0]); - } $this->operation = array(__FUNCTION__, $table, $condition); return $this->addParams($params); } @@ -484,8 +471,6 @@ class Query extends \yii\base\Object * * @param string|array $condition the conditions that should be put in the WHERE part. * @param array $params the parameters (name=>value) to be bound to the query. - * For anonymous parameters, they can alternatively be specified as separate parameters to this method. - * For example, `where('type=? AND status=?', 100, 1)`. * @return Query the query object itself * @see andWhere() * @see orWhere() @@ -493,10 +478,6 @@ class Query extends \yii\base\Object public function where($condition, $params = array()) { $this->where = $condition; - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } @@ -507,7 +488,6 @@ class Query extends \yii\base\Object * @param string|array $condition the new WHERE condition. Please refer to [[where()]] * on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see where() * @see orWhere() @@ -519,10 +499,6 @@ class Query extends \yii\base\Object } else { $this->where = array('and', $this->where, $condition); } - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } @@ -533,7 +509,6 @@ class Query extends \yii\base\Object * @param string|array $condition the new WHERE condition. Please refer to [[where()]] * on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see where() * @see andWhere() @@ -545,109 +520,78 @@ class Query extends \yii\base\Object } else { $this->where = array('or', $this->where, $condition); } - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } /** - * Appends an INNER JOIN part to the query. + * Appends a JOIN part to the query. + * The first parameter specifies what type of join it is. + * @param string $type the type of join, such as INNER JOIN, LEFT JOIN. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself */ - public function join($table, $condition, $params = array()) + public function join($type, $table, $on = '', $params = array()) { - $this->join[] = array('JOIN', $table, $condition); - if (!is_array($params)) { - $params = func_get_args(); - array_shift($params); - unset($params[0]); - } + $this->join[] = array($type, $table, $on); return $this->addParams($params); } /** - * Appends a LEFT OUTER JOIN part to the query. + * Appends an INNER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. - * @param array $params the parameters (name=>value) to be bound to the query + * @param array $params the parameters (name=>value) to be bound to the query. * @return Query the query object itself */ - public function leftJoin($table, $condition, $params = array()) + public function innerJoin($table, $on = '', $params = array()) { - $this->join[] = array('LEFT JOIN', $table, $condition); - if (!is_array($params)) { - $params = func_get_args(); - array_shift($params); - unset($params[0]); - } + $this->join[] = array('INNER JOIN', $table, $on); return $this->addParams($params); } /** - * Appends a RIGHT OUTER JOIN part to the query. + * Appends a LEFT OUTER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @param string|array $condition the join condition that should appear in the ON part. + * @param string|array $on the join condition that should appear in the ON part. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query * @return Query the query object itself */ - public function rightJoin($table, $condition, $params = array()) + public function leftJoin($table, $on = '', $params = array()) { - $this->join[] = array('RIGHT JOIN', $table, $condition); - if (!is_array($params)) { - $params = func_get_args(); - array_shift($params); - unset($params[0]); - } + $this->join[] = array('LEFT JOIN', $table, $on); return $this->addParams($params); } /** - * Appends a CROSS JOIN part to the query. - * Note that not all DBMS support CROSS JOIN. - * @param string $table the table to be joined. - * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). - * The method will automatically quote the table name unless it contains some parenthesis - * (which means the table is given as a sub-query or DB expression). - * @return Query the query object itself - */ - public function crossJoin($table) - { - $this->join[] = array('CROSS JOIN', $table); - return $this; - } - - /** - * Appends a NATURAL JOIN part to the query. - * Note that not all DBMS support NATURAL JOIN. + * Appends a RIGHT OUTER JOIN part to the query. * @param string $table the table to be joined. * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * The method will automatically quote the table name unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). + * @param string|array $on the join condition that should appear in the ON part. + * Please refer to [[where()]] on how to specify this parameter. + * @param array $params the parameters (name=>value) to be bound to the query * @return Query the query object itself */ - public function naturalJoin($table) + public function rightJoin($table, $on = '', $params = array()) { - $this->join[] = array('NATURAL JOIN', $table); - return $this; + $this->join[] = array('RIGHT JOIN', $table, $on); + return $this->addParams($params); } /** @@ -695,7 +639,6 @@ class Query extends \yii\base\Object * @param string|array $condition the conditions to be put after HAVING. * Please refer to [[where()]] on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see andHaving() * @see orHaving() @@ -703,10 +646,6 @@ class Query extends \yii\base\Object public function having($condition, $params = array()) { $this->having = $condition; - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } @@ -717,7 +656,6 @@ class Query extends \yii\base\Object * @param string|array $condition the new HAVING condition. Please refer to [[where()]] * on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see having() * @see orHaving() @@ -729,10 +667,6 @@ class Query extends \yii\base\Object } else { $this->having = array('and', $this->having, $condition); } - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } @@ -743,7 +677,6 @@ class Query extends \yii\base\Object * @param string|array $condition the new HAVING condition. Please refer to [[where()]] * on how to specify this parameter. * @param array $params the parameters (name=>value) to be bound to the query. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see having() * @see andHaving() @@ -755,10 +688,6 @@ class Query extends \yii\base\Object } else { $this->having = array('or', $this->having, $condition); } - if (!is_array($params)) { - $params = func_get_args(); - unset($params[0]); - } $this->addParams($params); return $this; } @@ -840,7 +769,6 @@ class Query extends \yii\base\Object * Sets the parameters to be bound to the query. * @param array $params list of query parameter values indexed by parameter placeholders. * For example, `array(':name'=>'Dan', ':age'=>31)`. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see addParams() */ @@ -854,7 +782,6 @@ class Query extends \yii\base\Object * Adds additional parameters to be bound to the query. * @param array $params list of query parameter values indexed by parameter placeholders. * For example, `array(':name'=>'Dan', ':age'=>31)`. - * Please refer to [[where()]] on alternative syntax of specifying anonymous parameters. * @return Query the query object itself * @see params() */ diff --git a/framework/db/dao/QueryBuilder.php b/framework/db/dao/QueryBuilder.php index cb03a4a..2ca4575 100644 --- a/framework/db/dao/QueryBuilder.php +++ b/framework/db/dao/QueryBuilder.php @@ -488,7 +488,7 @@ class QueryBuilder extends \yii\base\Object ); if (!is_array($condition)) { - return $condition; + return (string)$condition; } elseif ($condition === array()) { return ''; } @@ -733,8 +733,11 @@ class QueryBuilder extends \yii\base\Object } } $joins[$i] = strtoupper($join[0]) . ' ' . $table; - if (isset($join[2])) { // join condition - $joins[$i] .= ' ON ' . $this->buildCondition($join[2]); + if (isset($join[2])) { + $condition = $this->buildCondition($join[2]); + if ($condition !== '') { + $joins[$i] .= ' ON ' . $this->buildCondition($join[2]); + } } } else { throw new Exception('A join clause must be specified as an array of at least two elements.'); diff --git a/tests/unit/framework/db/ar/ActiveRecordTest.php b/tests/unit/framework/db/ar/ActiveRecordTest.php index 8e2545c..7b32faf 100644 --- a/tests/unit/framework/db/ar/ActiveRecordTest.php +++ b/tests/unit/framework/db/ar/ActiveRecordTest.php @@ -63,7 +63,8 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $pk = array('order_id' => 2, 'item_id' => 4); $orderItem = OrderItem::find($pk)->one(); $this->assertEquals(1, $orderItem->quantity); - $orderItem->saveCounters(array('quantity' => -1)); + $ret = $orderItem->saveCounters(array('quantity' => -1)); + $this->assertTrue($ret); $this->assertEquals(0, $orderItem->quantity); $orderItem = OrderItem::find($pk)->one(); $this->assertEquals(0, $orderItem->quantity); @@ -71,9 +72,10 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase // updateAll $customer = Customer::find(3)->one(); $this->assertEquals('user3', $customer->name); - Customer::updateAll(array( + $ret = Customer::updateAll(array( 'name' => 'temp', ), array('id' => 3)); + $this->assertEquals(1, $ret); $customer = Customer::find(3)->one(); $this->assertEquals('temp', $customer->name); @@ -81,9 +83,10 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase $pk = array('order_id' => 1, 'item_id' => 2); $orderItem = OrderItem::find($pk)->one(); $this->assertEquals(2, $orderItem->quantity); - OrderItem::updateCounters(array( + $ret = OrderItem::updateCounters(array( 'quantity' => 3, ), $pk); + $this->assertEquals(1, $ret); $orderItem = OrderItem::find($pk)->one(); $this->assertEquals(5, $orderItem->quantity); } @@ -101,7 +104,8 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase // deleteAll $customers = Customer::find()->all(); $this->assertEquals(2, count($customers)); - Customer::deleteAll(); + $ret = Customer::deleteAll(); + $this->assertEquals(2, $ret); $customers = Customer::find()->all(); $this->assertEquals(0, count($customers)); }