diff --git a/framework/base/Model.php b/framework/base/Model.php index a75b694..040e229 100644 --- a/framework/base/Model.php +++ b/framework/base/Model.php @@ -102,11 +102,11 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc return self::$_attributes[$className]; } - $class = new ReflectionClass($this); + $class = new \ReflectionClass($this); $names = array(); - foreach ($class->getProperties() as $property) { + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); - if ($property->isPublic() && !$property->isStatic()) { + if (!$property->isStatic()) { $names[] = $name; } } diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index 5df6965..688b14e 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -94,8 +94,8 @@ class Command extends \yii\base\Component } else { $this->query = new Query; - if (is_array($this->query)) { - $this->query->fromArray($this->query); + if (is_array($query)) { + $this->query->fromArray($query); } else { $this->_sql = $query; @@ -124,9 +124,9 @@ class Command extends \yii\base\Component /** * @return string the SQL statement to be executed */ - public function getSql() + public function getSql($rebuild = false) { - if ($this->_sql == '' && is_object($this->query)) { + if ($this->_sql === null || $rebuild) { $this->_sql = $this->query->getSql($this->connection); } return $this->_sql; @@ -168,7 +168,7 @@ class Command extends \yii\base\Component \Yii::log('Error in preparing SQL: ' . $this->getSql(), CLogger::LEVEL_ERROR, 'system.db.Command'); $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null; throw new Exception('Unable to prepare the SQL statement: {error}', - array('{error}' => $e->getMessage())), (int)$e->getCode(), $errorInfo); + array('{error}' => $e->getMessage()), (int)$e->getCode(), $errorInfo); } } } diff --git a/framework/db/dao/Query.php b/framework/db/dao/Query.php index cf96ad1..08fc11d 100644 --- a/framework/db/dao/Query.php +++ b/framework/db/dao/Query.php @@ -94,6 +94,136 @@ class Query extends \yii\base\Component } } + public function mergeWith($query, $useAnd = true) + { + $and = $useAnd ? 'AND' : 'OR'; + if (is_array($query)) { + $query = new self($query); + } + + if ($this->select !== $query->select) { + if($this->select === '*') { + $this->select = $query->select; + } + elseif($query->select!=='*') { + $select1 = is_string($this->select) ? preg_split('/\s*,\s*/', trim($this->select), -1, PREG_SPLIT_NO_EMPTY) : $this->select; + $select2 = is_string($query->select) ? preg_split('/\s*,\s*/', trim($query->select), -1, PREG_SPLIT_NO_EMPTY) : $query->select; + $this->select = array_merge($select1, array_diff($select2, $select1)); + } + } + + if ($this->selectOption !== $query->selectOption) { + if ($this->selectOption === null) { + $this->selectOption = $query->selectOption; + } + elseif ($query->selectOption !== null) { + $this->selectOption .= ' ' . $query->selectOption; + } + } + + if ($query->distinct) { + $this->distinct = $query->distinct; + } + + if ($this->where !== $query->where) { + if (empty($this->where)) { + $this->where = $query->where; + } + elseif (!empty($query->where)) { + $this->where = array('AND', $this->where, $query->where); + } + } + + if ($this->params !== $query->params) { + $this->params = $this->addParams($query->params); + } + + if ($query->limit !== null) { + $this->limit = $query->limit; + } + + if ($query->offset !== null) { + $this->offset = $query->offset; + } + + if ($this->orderBy !== $query->orderBy) { + if (empty($this->orderBy)) { + $this->orderBy = $query->orderBy; + } + elseif (!empty($query->orderBy)) { + if (!is_array($this->orderBy)) { + $this->orderBy = array($this->orderBy); + } + if (is_array($query->orderBy)) { + $this->orderBy = array_merge($this->orderBy, $query->orderBy); + } + else { + $this->orderBy[] = $query->orderBy; + } + } + } + + if ($this->groupBy !== $query->groupBy) { + if (empty($this->groupBy)) { + $this->groupBy = $query->groupBy; + } + elseif (!empty($query->groupBy)) { + if (!is_array($this->groupBy)) { + $this->groupBy = array($this->groupBy); + } + if (is_array($query->groupBy)) { + $this->groupBy = array_merge($this->groupBy, $query->groupBy); + } + else { + $this->groupBy[] = $query->groupBy; + } + } + } + + if ($this->join !== $query->join) { + if (empty($this->join)) { + $this->join = $query->join; + } + elseif (!empty($query->join)) { + if (!is_array($this->join)) { + $this->join = array($this->join); + } + if (is_array($query->join)) { + $this->join = array_merge($this->join, $query->join); + } + else { + $this->join[] = $query->join; + } + } + } + + if ($this->having !== $query->having) { + if (empty($this->having)) { + $this->having = $query->having; + } + elseif (!empty($query->having)) { + $this->having = array('AND', $this->having, $query->having); + } + } + + if ($this->union !== $query->union) { + if (empty($this->union)) { + $this->union = $query->union; + } + elseif (!empty($query->union)) { + if (!is_array($this->union)) { + $this->union = array($this->union); + } + if (is_array($query->union)) { + $this->union = array_merge($this->union, $query->union); + } + else { + $this->union[] = $query->union; + } + } + } + } + /** * Appends a condition to the existing {@link condition}. * The new condition and the existing condition will be concatenated via the specified operator @@ -372,15 +502,44 @@ class Query extends \yii\base\Component return $this; } + public function reset() + { + $this->select = null; + $this->selectOption = null; + $this->from = null; + $this->distinct = null; + $this->where = null; + $this->limit = null; + $this->offset = null; + $this->orderBy = null; + $this->groupBy = null; + $this->join = null; + $this->having = null; + $this->params = array(); + $this->union = null; + } + + public function fromArray($array) + { + $this->reset(); + foreach (array('select', 'selectOption', 'from', 'distinct', 'where', 'limit', 'offset', 'orderBy', 'groupBy', 'join', 'having', 'params', 'union') as $name) { + if (isset($array[$name])) { + $this->$name = $array[$name]; + } + } + } + /** * @return array the array representation of the criteria - * @since 1.0.6 */ public function toArray() { $result = array(); - foreach (array('select', 'condition', 'params', 'limit', 'offset', 'order', 'group', 'join', 'having', 'distinct', 'scopes', 'with', 'alias', 'index', 'together') as $name) - $result[$name] = $this->$name; + foreach (array('select', 'selectOption', 'from', 'distinct', 'where', 'limit', 'offset', 'orderBy', 'groupBy', 'join', 'having', 'params', 'union') as $name) { + if (!empty($this->$name)) { + $result[$name] = $this->$name; + } + } return $result; } } diff --git a/tests/unit/data/config.php b/tests/unit/data/config.php index 865edc6..2473c85 100644 --- a/tests/unit/data/config.php +++ b/tests/unit/data/config.php @@ -5,5 +5,6 @@ return array( 'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', 'username' => 'root', 'password' => '', + 'fixture' => __DIR__ . '/mysql.sql', ), ); \ No newline at end of file diff --git a/tests/unit/data/mysql.sql b/tests/unit/data/mysql.sql index 057b1ad..c2af862 100644 --- a/tests/unit/data/mysql.sql +++ b/tests/unit/data/mysql.sql @@ -4,6 +4,16 @@ * and create an account 'test/test' which owns this test database. */ +DROP TABLE IF EXISTS types CASCADE; +DROP TABLE IF EXISTS items CASCADE; +DROP TABLE IF EXISTS orders CASCADE; +DROP TABLE IF EXISTS post_category CASCADE; +DROP TABLE IF EXISTS categories CASCADE; +DROP TABLE IF EXISTS comments CASCADE; +DROP TABLE IF EXISTS posts CASCADE; +DROP TABLE IF EXISTS profiles CASCADE; +DROP TABLE IF EXISTS users CASCADE; + CREATE TABLE users ( id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, diff --git a/tests/unit/framework/db/dao/CommandTest.php b/tests/unit/framework/db/dao/CommandTest.php new file mode 100644 index 0000000..9754ae3 --- /dev/null +++ b/tests/unit/framework/db/dao/CommandTest.php @@ -0,0 +1,296 @@ +markTestSkipped('pdo and pdo_mysql extensions are required.'); + + $params = $this->getParam('mysql'); + $this->connection = new Connection($params['dsn'], $params['username'], $params['password']); + $this->connection->open(); + $this->connection->pdo->exec(file_get_contents($params['fixture'])); + } + + function tearDown() + { + $this->connection->close(); + } + + function testConstruct() + { + $command = $this->connection->createCommand(); + $this->assertEquals("SELECT *\nFROM ", $command->sql); + + $sql='SELECT * FROM posts'; + $command = $this->connection->createCommand($sql); + $this->assertEquals($sql, $command->sql); + + $query = new Query; + $command = $this->connection->createCommand($query); + $this->assertEquals($query, $command->query); + + $query = array('select'=>'id', 'from'=>'posts'); + $command = $this->connection->createCommand($query); + $this->assertEquals($query, $command->query->toArray()); + } + + function testReset() + { + + } + + function testGetSetSql() + { + + } + + function testPrepare() + { + + } + + function testBindParam() + { + + } + + function testBindValue() + { + + } + + function testExecute() + { + + } + + function testQuery() + { + + } + + function testQueryRow() + { + + } + + function testQueryAll() + { + + } + + function testQueryColumn() + { + + } + + function testQueryScalar() + { + + } + + function testFetchMode() + { + + } + +s /* + function testPrepare() + { + $sql='SELECT title FROM posts'; + $command=$this->connection->createCommand($sql); + $this->assertEquals($command->pdoStatement,null); + $command->prepare(); + $this->assertTrue($command->pdoStatement instanceof PDOStatement); + $this->assertEquals($command->queryScalar(),'post 1'); + + $command->text='Bad SQL'; + $this->setExpectedException('CException'); + $command->prepare(); + } + + function testCancel() + { + $sql='SELECT title FROM posts'; + $command=$this->connection->createCommand($sql); + $command->prepare(); + $this->assertTrue($command->pdoStatement instanceof PDOStatement); + $command->cancel(); + $this->assertEquals($command->pdoStatement,null); + } + + function testExecute() + { + $sql='INSERT INTO comments(content,post_id,author_id) VALUES (\'test comment\', 1, 1)'; + $command=$this->connection->createCommand($sql); + $this->assertEquals($command->execute(),1); + $this->assertEquals($command->execute(),1); + $command=$this->connection->createCommand('SELECT * FROM comments WHERE content=\'test comment\''); + $this->assertEquals($command->execute(),0); + $command=$this->connection->createCommand('SELECT COUNT(*) FROM comments WHERE content=\'test comment\''); + $this->assertEquals($command->queryScalar(),2); + + $command=$this->connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->execute(); + } + + function testQuery() + { + $sql='SELECT * FROM posts'; + $reader=$this->connection->createCommand($sql)->query(); + $this->assertTrue($reader instanceof CDbDataReader); + + $sql='SELECT * FROM posts'; + $command=$this->connection->createCommand($sql); + $command->prepare(); + $reader=$command->query(); + $this->assertTrue($reader instanceof CDbDataReader); + + $command=$this->connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->query(); + } + + function testBindParam() + { + $sql='INSERT INTO posts(title,create_time,author_id) VALUES (:title, :create_time, 1)'; + $command=$this->connection->createCommand($sql); + $title='test title'; + $createTime=time(); + $command->bindParam(':title',$title); + $command->bindParam(':create_time',$createTime); + $command->execute(); + + $sql='SELECT create_time FROM posts WHERE title=:title'; + $command=$this->connection->createCommand($sql); + $command->bindParam(':title',$title); + $this->assertEquals($command->queryScalar(),$createTime); + + $sql='INSERT INTO types (int_col, char_col, float_col, blob_col, numeric_col, bool_col) VALUES (:int_col, :char_col, :float_col, :blob_col, :numeric_col, :bool_col)'; + $command=$this->connection->createCommand($sql); + $intCol=123; + $charCol='abc'; + $floatCol=1.23; + $blobCol="\x10\x11\x12"; + $numericCol='1.23'; + $boolCol=false; + $command->bindParam(':int_col',$intCol); + $command->bindParam(':char_col',$charCol); + $command->bindParam(':float_col',$floatCol); + $command->bindParam(':blob_col',$blobCol); + $command->bindParam(':numeric_col',$numericCol); + $command->bindParam(':bool_col',$boolCol); + $this->assertEquals(1,$command->execute()); + + $sql='SELECT * FROM types'; + $row=$this->connection->createCommand($sql)->queryRow(); + $this->assertEquals($row['int_col'],$intCol); + $this->assertEquals($row['char_col'],$charCol); + $this->assertEquals($row['float_col'],$floatCol); + $this->assertEquals($row['blob_col'],$blobCol); + $this->assertEquals($row['numeric_col'],$numericCol); + } + + function testBindValue() + { + $sql='INSERT INTO comments(content,post_id,author_id) VALUES (:content, 1, 1)'; + $command=$this->connection->createCommand($sql); + $command->bindValue(':content','test comment'); + $command->execute(); + + $sql='SELECT post_id FROM comments WHERE content=:content'; + $command=$this->connection->createCommand($sql); + $command->bindValue(':content','test comment'); + $this->assertEquals($command->queryScalar(),1); + } + + function testQueryAll() + { + $rows=$this->connection->createCommand('SELECT * FROM posts')->queryAll(); + $this->assertEquals(count($rows),5); + $row=$rows[2]; + $this->assertEquals($row['id'],3); + $this->assertEquals($row['title'],'post 3'); + + $rows=$this->connection->createCommand('SELECT * FROM posts WHERE id=10')->queryAll(); + $this->assertEquals($rows,array()); + } + + function testQueryRow() + { + $sql='SELECT * FROM posts'; + $row=$this->connection->createCommand($sql)->queryRow(); + $this->assertEquals($row['id'],1); + $this->assertEquals($row['title'],'post 1'); + + $sql='SELECT * FROM posts'; + $command=$this->connection->createCommand($sql); + $command->prepare(); + $row=$command->queryRow(); + $this->assertEquals($row['id'],1); + $this->assertEquals($row['title'],'post 1'); + + $sql='SELECT * FROM posts WHERE id=10'; + $command=$this->connection->createCommand($sql); + $this->assertFalse($command->queryRow()); + + $command=$this->connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryRow(); + } + + function testQueryColumn() + { + $sql='SELECT * FROM posts'; + $column=$this->connection->createCommand($sql)->queryColumn(); + $this->assertEquals($column,range(1,5)); + + $command=$this->connection->createCommand('SELECT id FROM posts WHERE id=10'); + $this->assertEquals($command->queryColumn(),array()); + + $command=$this->connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryColumn(); + } + + function testQueryScalar() + { + $sql='SELECT * FROM posts'; + $this->assertEquals($this->connection->createCommand($sql)->queryScalar(),1); + + $sql='SELECT id FROM posts'; + $command=$this->connection->createCommand($sql); + $command->prepare(); + $this->assertEquals($command->queryScalar(),1); + + $command=$this->connection->createCommand('SELECT id FROM posts WHERE id=10'); + $this->assertFalse($command->queryScalar()); + + $command=$this->connection->createCommand('bad SQL'); + $this->setExpectedException('CException'); + $command->queryScalar(); + } + + function testFetchMode(){ + $sql='SELECT * FROM posts'; + $command=$this->connection->createCommand($sql); + $result = $command->queryRow(); + $this->assertTrue(is_array($result)); + + $sql='SELECT * FROM posts'; + $command=$this->connection->createCommand($sql); + $command->setFetchMode(PDO::FETCH_OBJ); + $result = $command->queryRow(); + $this->assertTrue(is_object($result)); + } + */ +} \ No newline at end of file diff --git a/tests/unit/framework/db/dao/ConnectionTest.php b/tests/unit/framework/db/dao/ConnectionTest.php index 3318065..d433303 100644 --- a/tests/unit/framework/db/dao/ConnectionTest.php +++ b/tests/unit/framework/db/dao/ConnectionTest.php @@ -85,57 +85,9 @@ class ConnectionTest extends TestCase } - function createConnection() { $params = $this->getParam('mysql'); return new Connection($params['dsn'], $params['username'], $params['password']); } - - /* - function testCreateCommand() - { - $sql='SELECT * FROM posts'; - $this->connection->active=true; - $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); - $command=$this->connection->createCommand($sql); - $this->assertTrue($command instanceof CDbCommand); - } - - function testLastInsertID() - { - $this->connection->active=true; - $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); - $sql='INSERT INTO posts(title,create_time,author_id) VALUES(\'test post\',11000,1)'; - $this->connection->createCommand($sql)->execute(); - $this->assertEquals($this->connection->lastInsertID,6); - } - - function testQuoteValue() - { - $this->connection->active=true; - $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); - $str="this is 'my' name"; - $expectedStr="'this is ''my'' name'"; - $this->assertEquals($expectedStr,$this->connection->quoteValue($str)); - } - - function testColumnNameCase() - { - $this->connection->active=true; - $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); - $this->assertEquals(PDO::CASE_NATURAL,$this->connection->ColumnCase); - $this->connection->columnCase=PDO::CASE_LOWER; - $this->assertEquals(PDO::CASE_LOWER,$this->connection->ColumnCase); - } - - function testNullConversion() - { - $this->connection->active=true; - $this->connection->pdoInstance->exec(file_get_contents(dirname(__FILE__).'/data/sqlite.sql')); - $this->assertEquals(PDO::NULL_NATURAL,$this->connection->NullConversion); - $this->connection->nullConversion=PDO::NULL_EMPTY_STRING; - $this->assertEquals(PDO::NULL_EMPTY_STRING,$this->connection->NullConversion); - } - */ }