From 3c5a5d9603d4e81e6873f10469fa64f1d914b436 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Fri, 11 Nov 2011 18:01:17 +0400 Subject: [PATCH 01/25] rebuild Model package for NoSQL drivers --- .gitignore | 1 + model/DbDriver.php | 177 +----------------- model/Model.php | 197 +++++---------------- model/MongoDriver.php | 77 ++++++++ model/MySQLiDriver.php | 2 +- model/NoSqlDbDriver.php | 13 ++ model/SqlDbDriver.php | 185 +++++++++++++++++++ model/SqlModel.php | 173 ++++++++++++++++++ model_class_diadram.png | Bin 0 -> 43038 bytes tests/model/MongoDriverTest.php | 65 +++++++ tests/model/MySQLiDriverTest.php | 1 + .../{DbDriverTest.php => SqlDbDriverTest.php} | 9 +- tests/model/{ModelTest.php => SqlModelTest.php} | 23 +-- uml_model.zargo | Bin 0 -> 12190 bytes 14 files changed, 589 insertions(+), 334 deletions(-) create mode 100644 model/MongoDriver.php create mode 100644 model/NoSqlDbDriver.php create mode 100644 model/SqlDbDriver.php create mode 100644 model/SqlModel.php create mode 100644 model_class_diadram.png create mode 100644 tests/model/MongoDriverTest.php rename tests/model/{DbDriverTest.php => SqlDbDriverTest.php} (93%) rename tests/model/{ModelTest.php => SqlModelTest.php} (92%) create mode 100644 uml_model.zargo diff --git a/.gitignore b/.gitignore index f6a98c3..24bad3a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.project /.cache /tests/report +*~ diff --git a/model/DbDriver.php b/model/DbDriver.php index 444d2af..b5f2efd 100644 --- a/model/DbDriver.php +++ b/model/DbDriver.php @@ -12,8 +12,6 @@ abstract class DbDriver { - protected $identifier_quote = '`'; - /** * Database connection * @@ -44,173 +42,20 @@ abstract class DbDriver } } } - + public function getConnection() { $this->connect(); return $this->connection; } - - public function beginTransaction() - { - $this->connect(); - $this->driverBeginTransaction(); - return $this; - } - - public function commit() - { - $this->connect(); - $this->driverCommitTransaction(); - return $this; - } - - public function rollback() - { - $this->connect(); - $this->driverRollbackTransaction(); - return $this; - } - - /** - * @param string $sql - * @param mixed $params - * @return DbStatement - */ - public function query($sql, $params = array()) - { - $this->connect(); - if (!is_array($params)) { - $params = array($params); - } - $stmt = $this->prepare($sql); - $stmt->execute($params); - return $stmt; - } - - /** - * @param string $table - * @param mixed $bind - * @param mixed $on_duplicate - * @return int Affected rows count - */ - public function insert($table, $bind, $on_duplicate = array()) - { - $columns = array(); - foreach ($bind as $col => $val) { - $columns[] = $this->quoteIdentifier($col); - } - $values = array_values($bind); - - $sql = 'INSERT INTO ' . $this->quoteIdentifier($table) - . ' (' . implode(', ', $columns) . ') VALUES (' . $this->quote($values) . ')'; - return $this->query($sql)->affectedRows(); - } - - /** - * @param string $table - * @param array $bind - * @param mixed $where - * @return int - */ - public function update($table, $bind, $where = '') - { - $set = array(); - foreach ($bind as $col => $val) { - $set[] = $this->quoteIdentifier($col) . '=' . $this->quote($val); - } - $where = $this->whereExpr($where); - $sql = 'UPDATE ' . $this->quoteIdentifier($table) . ' SET ' . implode(', ', $set) - . (($where) ? (' WHERE ' . $where) : ''); - return $this->query($sql)->affectedRows(); - } - - /** - * @param string $table - * @param mixed $where - * @return int - */ - public function delete($table, $where = '') - { - $where = $this->whereExpr($where); - $sql = 'DELETE FROM ' . $this->quoteIdentifier($table) . (($where) ? (' WHERE ' . $where) : ''); - return $this->query($sql)->affectedRows(); - } - - /** - * @param mixed $value - * @return string - */ - public function quote($value) - { - if ($value instanceof DbExpr) { - return (string) $value; - } - if (is_array($value)) { - foreach ($value as &$val) { - $val = $this->quote($val); - } - return implode(', ', $value); - } - return $this->driverQuote($value); - } - - /** - * @param string $ident - * @return string - */ - public function quoteIdentifier($ident) - { - $ident = explode('.', $ident); - if (!is_array($ident)) { - $ident = array($ident); - } - foreach ($ident as &$segment) { - $segment = $this->identifier_quote . $segment . $this->identifier_quote; - } - return implode('.', $ident); - } - - public function quoteInto($text, $value) - { - $pos = mb_strpos($text, '?'); - if ($pos === false) { - return $text; - } - return mb_substr($text, 0, $pos) . $this->quote($value) . mb_substr($text, $pos + 1); - } - - /** - * @param mixed $where - * @return string - */ - protected function whereExpr($where) - { - if (empty($where)) { - return $where; - } - - if (!is_array($where)) { - $where = array($where); - } - foreach ($where as $cond => &$term) { - if (is_int($cond)) { - if ($term instanceof DbExpr) { - $term = (string) $term; - } - } else { - $term = $this->quoteInto($cond, $term); - } - } - return implode(' AND ', $where); - } - + /* Abstract methods */ + + abstract public function insert($table, $data); + + abstract public function update($table, $data, $condition); - /** - * @return DbStatement - */ - abstract public function prepare($sql); + abstract public function delete($table, $condition); abstract public function getInsertId($table = null, $key = null); @@ -219,12 +64,4 @@ abstract class DbDriver abstract public function disconnect(); abstract protected function connect(); - - abstract protected function driverQuote($value); - - abstract protected function driverBeginTransaction(); - - abstract protected function driverCommitTransaction(); - - abstract protected function driverRollbackTransaction(); } \ No newline at end of file diff --git a/model/Model.php b/model/Model.php index 53278c5..10e97f8 100644 --- a/model/Model.php +++ b/model/Model.php @@ -62,41 +62,13 @@ abstract class Model } /** - * @param string $ident - * @return string Quoted identifier. - */ - public function identify($ident) - { - return $this->db->quoteIdentifier($ident); - } - - /** - * @param mixed $value - * @return string Quoted value. - */ - public function quote($value) - { - return $this->db->quote($value); - } - - /** - * @param int $id - * @return object - */ - public function get($id) - { - $sql = 'SELECT * FROM :table WHERE :pk=?'; - return $this->fetch($sql, $id); - } - - /** * @param array $data * @param array $on_duplicate * @return int Id of inserted row */ - public function insert($data, $on_duplicate = array()) + public function insert($data) { - $affected = $this->db->insert($this->table(), $data, $on_duplicate); + $affected = $this->db->insert($this->table(), $data); return ($this->getInsertId()) ? $this->getInsertId() : $affected; } @@ -107,21 +79,8 @@ abstract class Model */ public function update($data, $where) { - if (is_int($where) || $where === (string) (int) $where) { - $where = $this->identify($this->key) . '=' . (int) $where; - } return $this->db->update($this->table(), $data, $where); } - - /** - * @param int $id Int id - * @return int Number of affected rows - */ - public function delete($id) - { - $where = $this->identify($this->key) . '=' . (int) $id; - return $this->db->delete($this->table(), $where); - } /** * @return string @@ -133,112 +92,7 @@ abstract class Model } return $this->table; } - - /** - * Creates order sql string - * - * @param array $params - * @param array $sortable - * @return string - */ - protected function order($params, $sortable = array('id')) - { - $sql = ''; - if (isset($params['sort'])) { - $order = (isset($params['order']) && $params['order'] == 'desc') ? 'DESC' : 'ASC'; - if (in_array($params['sort'], $sortable)) { - $sql = ' ORDER BY ' . $this->identify($params['sort']) . ' ' . $order; - } - } - return $sql; - } - - /** - * Searches using like - * - * @param array $params - * @param array $searchable - * @param string $table_prefix - * @return string - */ - protected function search($params, $searchable = array('id'), $table_prefix = '') - { - $sql = ''; - if (isset($params['q']) && isset($params['qt']) && in_array($params['qt'], $searchable)) { - if ($table_prefix) { - $sql = $table_prefix . '.'; - } - $sql .= $this->identify($params['qt']) . ' LIKE ' . $this->quote('%' . $params['q'] . '%'); - } - return $sql; - } - /** - * This method appends to params table and primary key. - * So they can be accessed thru `:table` and `:pk` placeholders. - * - * @return DbStatement - */ - protected function query($sql, $params = array()) - { - if (!is_array($params)) { - $params = array($params); - } - $params = array( - 'table' => new DbExpr($this->identify($this->table())), - 'pk' => new DbExpr($this->identify($this->key)), - ) + $params; - return $this->db->query($sql, $params); - } - - /** - * @param string $sql - * @param array $params - * @param string $field - * @param CacheKey $cache_key - */ - protected function fetchField($sql, $params = array(), $field, $cache_key = null) - { - if (!$cache_key || !$result = $cache_key->get()) { - $result = $this->query($sql, $params)->fetchField($field); - if ($cache_key) { - $cache_key->set($result); - } - } - return $result; - } - - /** - * @param string $sql - * @param array $params - * @param CacheKey $cache_key - */ - protected function fetch($sql, $params = array(), $cache_key = null) - { - if (!$cache_key || !$result = $cache_key->get()) { - $result = $this->query($sql, $params)->fetch(); - if ($cache_key) { - $cache_key->set($result); - } - } - return $result; - } - - /** - * @param string $sql - * @param array $params - * @param CacheKey $cache_key - */ - protected function fetchAll($sql, $params = array(), $cache_key = null) - { - if (!$cache_key || !$result = $cache_key->get()) { - $result = $this->query($sql, $params)->fetchAll(); - if ($cache_key) { - $cache_key->set($result); - } - } - return $result; - } /* Cache workaround */ @@ -280,4 +134,51 @@ abstract class Model } $this->caches_clean = array(); } + + /** + * Abstract methods + */ + + /** + * @param int $id + * @return object + */ + abstract public function get($id); + + /** + * @param int $id Int id + * @return int Number of affected rows + */ + abstract public function delete($id); + + /** + * Creates order sql string + * + * @param array $params + * @param array $sortable + * @return string + */ + abstract protected function order($params, $sortable = array('id')); + + /** + * @param string $sql + * @param array $params + * @param string $field + * @param CacheKey $cache_key + */ + abstract protected function fetchField($data, $params = array(), $field, $cache_key = null); + + /** + * @param string $sql + * @param array $params + * @param CacheKey $cache_key + */ + abstract protected function fetch($data, $params = array(), $cache_key = null); + + /** + * @param string $sql + * @param array $params + * @param CacheKey $cache_key + */ + abstract protected function fetchAll($data, $params = array(), $cache_key = null); } \ No newline at end of file diff --git a/model/MongoDriver.php b/model/MongoDriver.php new file mode 100644 index 0000000..c1d48e0 --- /dev/null +++ b/model/MongoDriver.php @@ -0,0 +1,77 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-10 + */ + +/** + * @property Mongo $connection + */ +class MongoDriver extends NoSqlDbDriver +{ + + public function find($data, $params = array(), $cache_key = null) + { + + } + + public function insert($table, $bind, $on_duplicate = array()) + { + return parent::insert($table, $bind, $on_duplicate); + } + + public function update($table, $bind, $where = '') + { + return parent::update($table, $bind, $where); + } + + public function delete($table, $where = '') + { + return parent::delete($table, $where); + } + + + public function getInsertId($table = null, $key = null) + { + // TODO: Implement getInsertId() method. + } + + public function isConnected() + { + if (!is_null($this->connection)) { + return $this->connection->connected; + } else { + return false; + } + } + + public function disconnect() + { + if ($this->isConnected()) { + $this->connection->close(); + } + $this->connection = null; + } + + protected function connect() + { + if ($this->connection) { + return; + } + + $host = $this->config['hostname']; + $port = isset($this->config['port']) ? ':' . (string) $this->config['port'] : ''; + + $this->config = array( + 'username' => $this->config['username'], + 'password' => $this->config['password'], + 'db' => $this->config['database'] + ); + + $this->connection = new Mongo('mongodb://' . $host . $port, $this->config); + } +} + diff --git a/model/MySQLiDriver.php b/model/MySQLiDriver.php index 4afa22c..2cae3f1 100644 --- a/model/MySQLiDriver.php +++ b/model/MySQLiDriver.php @@ -12,7 +12,7 @@ /** * @property MySQLi $connection */ -class MySQLiDriver extends DbDriver +class MySQLiDriver extends SqlDbDriver { public function insert($table, $bind, $on_duplicate = array()) diff --git a/model/NoSqlDbDriver.php b/model/NoSqlDbDriver.php new file mode 100644 index 0000000..66a1f8f --- /dev/null +++ b/model/NoSqlDbDriver.php @@ -0,0 +1,13 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-11 + */ + +abstract class NoSqlDbDriver extends DbDriver +{ + abstract function find($data, $params = array(), $cache_key = null); +} \ No newline at end of file diff --git a/model/SqlDbDriver.php b/model/SqlDbDriver.php new file mode 100644 index 0000000..8d8b9af --- /dev/null +++ b/model/SqlDbDriver.php @@ -0,0 +1,185 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-11 + */ + +abstract class SqlDbDriver extends DbDriver +{ + + protected $identifier_quote = '`'; + + + + public function beginTransaction() + { + $this->connect(); + $this->driverBeginTransaction(); + return $this; + } + + public function commit() + { + $this->connect(); + $this->driverCommitTransaction(); + return $this; + } + + public function rollback() + { + $this->connect(); + $this->driverRollbackTransaction(); + return $this; + } + + /** + * @param string $sql + * @param mixed $params + * @return DbStatement + */ + public function query($sql, $params = array()) + { + $this->connect(); + if (!is_array($params)) { + $params = array($params); + } + $stmt = $this->prepare($sql); + $stmt->execute($params); + return $stmt; + } + + /** + * @param string $table + * @param mixed $bind + * @param mixed $on_duplicate + * @return int Affected rows count + */ + public function insert($table, $data) + { + $columns = array(); + foreach ($data as $col => $val) { + $columns[] = $this->quoteIdentifier($col); + } + $values = array_values($data); + + $sql = 'INSERT INTO ' . $this->quoteIdentifier($table) + . ' (' . implode(', ', $columns) . ') VALUES (' . $this->quote($values) . ')'; + return $this->query($sql)->affectedRows(); + } + + /** + * @param string $table + * @param array $bind + * @param mixed $where + * @return int + */ + public function update($table, $data, $condition = '') + { + $set = array(); + foreach ($data as $col => $val) { + $set[] = $this->quoteIdentifier($col) . '=' . $this->quote($val); + } + $where = $this->whereExpr($condition); + $sql = 'UPDATE ' . $this->quoteIdentifier($table) . ' SET ' . implode(', ', $set) + . (($where) ? (' WHERE ' . $where) : ''); + return $this->query($sql)->affectedRows(); + } + + /** + * @param string $table + * @param mixed $where + * @return int + */ + public function delete($table, $condition = '') + { + $where = $this->whereExpr($condition); + $sql = 'DELETE FROM ' . $this->quoteIdentifier($table) . (($where) ? (' WHERE ' . $where) : ''); + return $this->query($sql)->affectedRows(); + } + + /** + * @param mixed $value + * @return string + */ + public function quote($value) + { + if ($value instanceof DbExpr) { + return (string) $value; + } + if (is_array($value)) { + foreach ($value as &$val) { + $val = $this->quote($val); + } + return implode(', ', $value); + } + return $this->driverQuote($value); + } + + /** + * @param string $ident + * @return string + */ + public function quoteIdentifier($ident) + { + $ident = explode('.', $ident); + if (!is_array($ident)) { + $ident = array($ident); + } + foreach ($ident as &$segment) { + $segment = $this->identifier_quote . $segment . $this->identifier_quote; + } + return implode('.', $ident); + } + + public function quoteInto($text, $value) + { + $pos = mb_strpos($text, '?'); + if ($pos === false) { + return $text; + } + return mb_substr($text, 0, $pos) . $this->quote($value) . mb_substr($text, $pos + 1); + } + + /** + * @param mixed $where + * @return string + */ + protected function whereExpr($where) + { + if (empty($where)) { + return $where; + } + + if (!is_array($where)) { + $where = array($where); + } + foreach ($where as $cond => &$term) { + if (is_int($cond)) { + if ($term instanceof DbExpr) { + $term = (string) $term; + } + } else { + $term = $this->quoteInto($cond, $term); + } + } + return implode(' AND ', $where); + } + + /** + * @return DbStatement + */ + abstract public function prepare($sql); + + abstract protected function driverQuote($value); + + abstract protected function driverBeginTransaction(); + + abstract protected function driverCommitTransaction(); + + abstract protected function driverRollbackTransaction(); + +} + \ No newline at end of file diff --git a/model/SqlModel.php b/model/SqlModel.php new file mode 100644 index 0000000..7724232 --- /dev/null +++ b/model/SqlModel.php @@ -0,0 +1,173 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage Model + * @since 2011-11-11 + */ + +abstract class SqlModel extends Model +{ + + /** + * @param string $ident + * @return string Quoted identifier. + */ + public function identify($ident) + { + return $this->db->quoteIdentifier($ident); + } + + /** + * @param mixed $value + * @return string Quoted value. + */ + public function quote($value) + { + return $this->db->quote($value); + } + + /** + * @param int $id + * @return object + */ + public function get($id) + { + $sql = 'SELECT * FROM :table WHERE :pk=?'; + return $this->fetch($sql, $id); + } + + /** + * @param array $data + * @param mixed $where + * @return int Number of affected rows + */ + public function update($data, $where) + { + if (is_int($where) || $where === (string) (int) $where) { + $where = $this->identify($this->key) . '=' . (int) $where; + } + return parent::update($data, $where); + } + + /** + * @param int $id Int id + * @return int Number of affected rows + */ + public function delete($id) + { + $where = $this->identify($this->key) . '=' . (int) $id; + return $this->db->delete($this->table(), $where); + } + + /** + * Creates order sql string + * + * @param array $params + * @param array $sortable + * @return string + */ + protected function order($params, $sortable = array('id')) + { + $sql = ''; + if (isset($params['sort'])) { + $order = (isset($params['order']) && $params['order'] == 'desc') ? 'DESC' : 'ASC'; + if (in_array($params['sort'], $sortable)) { + $sql = ' ORDER BY ' . $this->identify($params['sort']) . ' ' . $order; + } + } + return $sql; + } + + /** + * Searches using like + * + * @param array $params + * @param array $searchable + * @param string $table_prefix + * @return string + */ + protected function search($params, $searchable = array('id'), $table_prefix = '') + { + $sql = ''; + if (isset($params['q']) && isset($params['qt']) && in_array($params['qt'], $searchable)) { + if ($table_prefix) { + $sql = $table_prefix . '.'; + } + $sql .= $this->identify($params['qt']) . ' LIKE ' . $this->quote('%' . $params['q'] . '%'); + } + return $sql; + } + + /** + * This method appends to params table and primary key. + * So they can be accessed thru `:table` and `:pk` placeholders. + * + * @return DbStatement + */ + protected function query($sql, $params = array()) + { + if (!is_array($params)) { + $params = array($params); + } + $params = array( + 'table' => new DbExpr($this->identify($this->table())), + 'pk' => new DbExpr($this->identify($this->key)), + ) + $params; + return $this->db->query($sql, $params); + } + + /** + * @param string $sql + * @param array $params + * @param string $field + * @param CacheKey $cache_key + */ + protected function fetchField($data, $params = array(), $field, $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->query($data, $params)->fetchField($field); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } + + /** + * @param string $sql + * @param array $params + * @param CacheKey $cache_key + */ + protected function fetch($data, $params = array(), $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->query($data, $params)->fetch(); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } + + /** + * @param string $sql + * @param array $params + * @param CacheKey $cache_key + */ + protected function fetchAll($data, $params = array(), $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->query($data, $params)->fetchAll(); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } + +} \ No newline at end of file diff --git a/model_class_diadram.png b/model_class_diadram.png new file mode 100644 index 0000000000000000000000000000000000000000..1c74e42c7f5fd65f23669a5439c24fa0859374f4 GIT binary patch literal 43038 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

0vT;DA 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 qUk=i6&i@z>|8vy--xJc+5n8E!oiSH0jd!8YZg + * @link http://netmonsters.ru + * @package Majestic + * @subpackage UnitTests + * @since 2011-11-10 + * + * Unit tests for MongoDriver class + */ + +require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php'; +require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; + +class MongoDriverTest extends PHPUnit_Framework_TestCase +{ + + private $conf = array(); + + public function setUp() + { + $this->conf = array( + 'hostname' => 'localhost', + 'database' => 'test', + 'username' => 'test', + 'password' => '1234', + 'port' => 27017 + ); + } + /** + * @expectedException Exception + * @expectedExceptionMessage Configuration must have a "hostname". + */ + public function testGetConnectionNoHostname() + { + unset($this->conf['hostname']); + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + } + + /** + * @expectedException MongoConnectionException + * @expectedExceptionMessage Couldn't authenticate with database + */ + public function testGetConnectionWrongPassword() + { + $this->conf['password'] = 'nopass'; + $mongo = new MongoDriver($this->conf); + $this->assertInstanceOf('Mongo', $mongo->getConnection()); + } + + public function testGetConnection() + { + $mongo = new MongoDriver($this->conf); + + $this->assertFalse($mongo->isConnected()); + $this->assertInstanceOf('Mongo', $mongo->getConnection()); + $this->assertTrue($mongo->isConnected()); + $mongo->disconnect(); + $this->assertFalse($mongo->isConnected()); + } + +} \ No newline at end of file diff --git a/tests/model/MySQLiDriverTest.php b/tests/model/MySQLiDriverTest.php index 156cdad..a9a5747 100644 --- a/tests/model/MySQLiDriverTest.php +++ b/tests/model/MySQLiDriverTest.php @@ -14,6 +14,7 @@ require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbStatement.php'; require_once dirname(__FILE__) . '/../../model/MySQLiStatement.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/SqlDbDriver.php'; require_once dirname(__FILE__) . '/../../model/MySQLiDriver.php'; class MySQLiDriverTest extends PHPUnit_Extensions_Database_TestCase diff --git a/tests/model/DbDriverTest.php b/tests/model/SqlDbDriverTest.php similarity index 93% rename from tests/model/DbDriverTest.php rename to tests/model/SqlDbDriverTest.php index 891b0a0..8fc8e89 100644 --- a/tests/model/DbDriverTest.php +++ b/tests/model/SqlDbDriverTest.php @@ -13,8 +13,9 @@ require_once dirname(__FILE__) . '/../../model/DbExpr.php'; require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/SqlDbDriver.php'; -class DbDriverTest extends PHPUnit_Framework_TestCase +class SqlDbDriverTest extends PHPUnit_Framework_TestCase { private $driver; @@ -22,13 +23,13 @@ class DbDriverTest extends PHPUnit_Framework_TestCase public function setUp() { $conf = array('hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234'); - $this->driver = $this->getMockForAbstractClass('DbDriver', array($conf)); + $this->driver = $this->getMockForAbstractClass('SqlDbDriver', array($conf)); } public function testConstruct() { $conf = array('hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234'); - $this->driver = $this->getMockForAbstractClass('DbDriver', array($conf)); + $this->driver = $this->getMockForAbstractClass('SqlDbDriver', array($conf)); } /** @@ -38,7 +39,7 @@ class DbDriverTest extends PHPUnit_Framework_TestCase public function testConstructWrongConfig() { $conf = array('hostname' => 'localhost', 'database' => 'db'); - $this->getMockForAbstractClass('DbDriver', array($conf)); + $this->getMockForAbstractClass('SqlDbDriver', array($conf)); } public function testGetConnection() diff --git a/tests/model/ModelTest.php b/tests/model/SqlModelTest.php similarity index 92% rename from tests/model/ModelTest.php rename to tests/model/SqlModelTest.php index e089eca..18de4ce 100644 --- a/tests/model/ModelTest.php +++ b/tests/model/SqlModelTest.php @@ -17,8 +17,9 @@ require_once dirname(__FILE__) . '/../../model/DbExpr.php'; require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; require_once dirname(__FILE__) . '/../../model/Model.php'; +require_once dirname(__FILE__) . '/../../model/SqlModel.php'; -class ModelTest extends PHPUnit_Framework_TestCase +class SqlModelTest extends PHPUnit_Framework_TestCase { private $model; @@ -32,7 +33,7 @@ class ModelTest extends PHPUnit_Framework_TestCase Config::set('Db', $conf); if (!class_exists('MockModel')) { - $this->model = $this->getMockForAbstractClass('Model', array(), 'MockModel'); + $this->model = $this->getMockForAbstractClass('SqlModel', array(), 'MockModel'); } else { $this->model = new MockModel(); } @@ -41,7 +42,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testModel() { - $this->assertInstanceOf('Model', $this->model); + $this->assertInstanceOf('SqlModel', $this->model); } public function testGetInsertId() @@ -83,7 +84,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testOrder() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('order'); $method->setAccessible(true); $this->assertEquals(' ORDER BY id DESC', $method->invoke($this->model, array('sort' => 'id', 'order' => 'desc'))); @@ -98,7 +99,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testSearch() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('search'); $method->setAccessible(true); $this->assertEmpty($method->invoke($this->model, array())); @@ -108,7 +109,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testFetch() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('fetch'); $method->setAccessible(true); @@ -118,7 +119,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testFetchField() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('fetchField'); $method->setAccessible(true); @@ -128,7 +129,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testFetchAll() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('fetchAll'); $method->setAccessible(true); @@ -167,7 +168,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testAddCleanCache() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('addCleanCache'); $method->setAccessible(true); @@ -180,7 +181,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testCleanCaches() { - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('addCleanCache'); $method->setAccessible(true); @@ -190,7 +191,7 @@ class ModelTest extends PHPUnit_Framework_TestCase $method->invoke($this->model, $key); $this->assertAttributeEquals(array($key, $key, $key), 'caches_clean', $this->model); - $model = new ReflectionClass('Model'); + $model = new ReflectionClass('SqlModel'); $method = $model->getMethod('cleanCaches'); $method->setAccessible(true); diff --git a/uml_model.zargo b/uml_model.zargo new file mode 100644 index 0000000000000000000000000000000000000000..a775ed3720a006a2872af95b5dcaec08c2237aeb GIT binary patch literal 12190 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ⅇ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 literal 0 HcmV?d00001 From 488655b7884c46db661cf678c7912cd841384740 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 11:12:20 +0400 Subject: [PATCH 02/25] Mongo driver with separate CRUD commsnds, without profiler --- model/MongoDriver.php | 54 +++++++++++++++++---- model/NoSqlDbDriver.php | 3 +- tests/model/MongoDriverTest.php | 103 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 11 deletions(-) diff --git a/model/MongoDriver.php b/model/MongoDriver.php index c1d48e0..8f75db0 100644 --- a/model/MongoDriver.php +++ b/model/MongoDriver.php @@ -13,30 +13,65 @@ class MongoDriver extends NoSqlDbDriver { - public function find($data, $params = array(), $cache_key = null) + + protected $last_inset_id = 0; + + protected $db_name = 'admin'; + + protected function getCollection($name) { + return $this->connection->selectCollection($this->db, $name); + } + public function find($collection, $condition = array(), $fields = array(), $cache_key = null) + { + return $this->getCollection($collection)->find($condition, $fields); } - - public function insert($table, $bind, $on_duplicate = array()) + + public function get($collection, $condition, $fields = array()) { - return parent::insert($table, $bind, $on_duplicate); + return $this->getCollection($collection)->findOne($condition, $fields); } - public function update($table, $bind, $where = '') + public function insert($collection, $data, $safe = true) { - return parent::update($table, $bind, $where); + $result = $this->getCollection($collection)->insert($data, array('safe' => $safe)); + if (isset($result['ok']) && $result['ok'] == 1) { + $this->last_inset_id = (string) $data['_id']; + return true; + } else { + return false; + } } - public function delete($table, $where = '') + public function update($collection, $data, $condition = array(), $upsert = false, $safe = true) { - return parent::delete($table, $where); + $result = $this->getCollection($collection)->update($condition, $data, array('upsert' => $upsert, 'safe' => $safe)); + + if (isset($result['ok']) && $result['ok'] == 1) { + if(isset($result['updatedExisting']) && isset($result['upserted'])) { + $this->last_inset_id = (string) $result['upserted']; + } + return $result['n']; + } else { + return false; + } + } + + public function delete($collection, $condition = array(), $safe = true) + { + $result = $this->getCollection($collection)->remove($condition, array('safe' => $safe)); + if (isset($result['ok']) && $result['ok'] == 1) { + return $result['n']; + } else { + return false; + } } public function getInsertId($table = null, $key = null) { - // TODO: Implement getInsertId() method. + return $this->last_inset_id; } public function isConnected() @@ -72,6 +107,7 @@ class MongoDriver extends NoSqlDbDriver ); $this->connection = new Mongo('mongodb://' . $host . $port, $this->config); + $this->db = $this->connection->selectDB($this->config['db']); } } diff --git a/model/NoSqlDbDriver.php b/model/NoSqlDbDriver.php index 66a1f8f..b82e3d3 100644 --- a/model/NoSqlDbDriver.php +++ b/model/NoSqlDbDriver.php @@ -9,5 +9,6 @@ abstract class NoSqlDbDriver extends DbDriver { - abstract function find($data, $params = array(), $cache_key = null); + + abstract function find($collection, $condition = array(), $fields = array(), $cache_key = null); } \ No newline at end of file diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index bcd0159..39aa4bc 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -28,7 +28,45 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase 'password' => '1234', 'port' => 27017 ); + + $data = array( + array( + 'name' => 'bread', + 'price' => 3.2, + 'quantity' => 10 + ), + array( + 'name' => 'eggs', + 'price' => 2.1, + 'quantity' => 20 + ), + array( + 'name' => 'fish', + 'price' => 13.2, + 'quantity' => 2 + ), + array( + 'name' => 'milk', + 'price' => 3.8, + 'quantity' => 1 + ), + array( + 'name' => 'eggs', + 'price' => 2.3, + 'quantity' => 5 + ) + ); + $connection = new Mongo('mongodb://' . $this->conf['hostname'] . ':' . $this->conf['port']); + $db = $connection->selectDB($this->conf['database']); + $db->authenticate($this->conf['username'], $this->conf['password']); + $collection = 'items'; + $db->dropCollection($collection); + $collection = $db->selectCollection($collection); + foreach($data as $document) { + $collection->insert($document); + } } + /** * @expectedException Exception * @expectedExceptionMessage Configuration must have a "hostname". @@ -48,7 +86,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase { $this->conf['password'] = 'nopass'; $mongo = new MongoDriver($this->conf); - $this->assertInstanceOf('Mongo', $mongo->getConnection()); + $this->assertInstanceOf('MongoDB', $mongo->getConnection()); } public function testGetConnection() @@ -58,8 +96,71 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertFalse($mongo->isConnected()); $this->assertInstanceOf('Mongo', $mongo->getConnection()); $this->assertTrue($mongo->isConnected()); + $mongo->getConnection(); $mongo->disconnect(); $this->assertFalse($mongo->isConnected()); } + public function testFind() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertEquals(2, $mongo->find('items', array('name' => 'eggs'))->count()); + $this->assertEquals(3, $mongo->find('items', array('price' => array('$lt' => 3.5)))->count()); + $this->assertEquals(5, $mongo->find('items', array())->count()); + } + + public function testGet() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + $bread = $mongo->get('items', array('name' => 'bread')); + $this->assertEquals(3.2, $bread['price']); + } + + public function testRemove() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertEquals(0, $mongo->delete('items', array('name' => 'esggs'))); + $this->assertEquals(2, $mongo->delete('items', array('name' => 'eggs'))); + $this->assertEquals(1, $mongo->delete('items', array('name' => 'bread'))); + $this->assertEquals(0, $mongo->find('items', array('name' => 'bread'))->count()); + } + + public function testInsert() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertTrue($mongo->insert('items', array('name' => 'bread'))); + $this->assertNotEmpty($mongo->getInsertId()); + $this->assertEquals(2, $mongo->find('items', array('name' => 'bread'))->count()); + } + + public function testUpdate() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); + $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'fish')); + $fish = $mongo->get('items', array('name' => 'fish')); + $this->assertEquals(200, $fish['price']); + $this->assertEquals('today', $fish['date']); + $this->assertEquals(0, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'))); + } + + public function testUpsert() + { + $mongo = new MongoDriver($this->conf); + $mongo->getConnection(); + + $this->assertTrue($mongo->insert('items', array('name' => 'bread'))); + $id = $mongo->getInsertId(); + $this->assertNotEmpty($id); + $this->assertEquals(1, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'), true)); + $this->assertNotEquals($id, $mongo->getInsertId()); + } } \ No newline at end of file From 1f0bfb131bc0f0d898646966e9c5ed9729f94d24 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 12:41:05 +0400 Subject: [PATCH 03/25] MongoCommand hierarchy created --- model/MongoDbCommand.php | 120 ++++++++++++++++++++++++ tests/model/MongoDbCommandTest.php | 183 +++++++++++++++++++++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 model/MongoDbCommand.php create mode 100644 tests/model/MongoDbCommandTest.php diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php new file mode 100644 index 0000000..663f73e --- /dev/null +++ b/model/MongoDbCommand.php @@ -0,0 +1,120 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-15 + */ + +class MongoCommandBuilder +{ + + const FIND = 'Find'; + + const INSERT = 'Insert'; + + const UPDATE = 'Update'; + + const REMOVE = 'Remove'; + + static public function factory($type) + { + $class = ucfirst($type) . 'MongoCommand'; + return new $class(); + } +} + +abstract class MongoDbCommand +{ + public function execute() + { + if ($this->checkParams()) { + return $this->concreteExecute(); + } else { + throw new Exception(get_called_class() . ' error. Bind all required params first.'); + } + } + + public function bindParam($name, $value) + { + $this->$name = $value; + return $this; + } + + abstract protected function concreteExecute(); + + abstract protected function checkParams(); +} + +class FindMongoCommand extends MongoDbCommand +{ + protected function concreteExecute() + { + return $this->collection->find($this->condition, $this->fields); + } + + protected function checkParams() + { + if (isset($this->collection) && isset($this->condition) && isset($this->fields)) { + return true; + } else { + return false; + } + } +} + +class InsertMongoCommand extends MongoDbCommand +{ + protected function concreteExecute() + { + return $this->collection->insert($this->data, array('safe' => $this->safe)); + } + + protected function checkParams() + { + if (isset($this->collection) && isset($this->data) && isset($this->safe)) { + return true; + } else { + return false; + } + } +} + +class UpdateMongoCommand extends MongoDbCommand +{ + protected function concreteExecute() + { + return $this->collection->update($this->condition, $this->data, array('upsert' => $this->upsert, 'safe' => $this->safe)); + } + + protected function checkParams() + { + if (isset($this->collection) && isset($this->condition) && isset($this->data) && + isset($this->upsert) && isset($this->safe) + ) { + return true; + } else { + return false; + } + } +} + +class RemoveMongoCommand extends MongoDbCommand +{ + protected function concreteExecute() + { + return $this->collection->remove($this->condition, array('safe' => $this->safe)); + } + + protected function checkParams() + { + if (isset($this->collection) && isset($this->condition) && isset($this->safe)) { + return true; + } else { + return false; + } + } +} + + diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php new file mode 100644 index 0000000..1cc2973 --- /dev/null +++ b/tests/model/MongoDbCommandTest.php @@ -0,0 +1,183 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage UnitTests + * @since 2011-11-10 + * + * Unit tests for MongoDriver class + */ + +require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php'; +require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; +require_once dirname(__FILE__) . '/../../model/MongoDbCommand.php'; + +class MongoDbCommandTest extends PHPUnit_Framework_TestCase +{ + + private $conf = array(); + + private $driver; + + public function setUp() + { + $this->conf = array( + 'hostname' => 'localhost', + 'database' => 'test', + 'username' => 'test', + 'password' => '1234', + 'port' => 27017 + ); + $this->driver = new MongoDriver($this->conf); + $connection = $this->driver->getConnection(); + + $db = $connection->selectDB($this->conf['database']); + $db->authenticate($this->conf['username'], $this->conf['password']); + $collection = 'items'; + $db->dropCollection($collection); + } + + public function testCommandFactory() + { + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $this->assertInstanceOf('MongoDbCommand', $cmd); + $this->assertInstanceOf('FindMongoCommand', $cmd); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); + $this->assertInstanceOf('MongoDbCommand', $cmd); + $this->assertInstanceOf('InsertMongoCommand', $cmd); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE); + $this->assertInstanceOf('MongoDbCommand', $cmd); + $this->assertInstanceOf('UpdateMongoCommand', $cmd); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE); + $this->assertInstanceOf('MongoDbCommand', $cmd); + $this->assertInstanceOf('RemoveMongoCommand', $cmd); + } + + public function testFindCommand() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'bread'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(0, $result->count()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage FindMongoCommand error. Bind all required params first. + */ + public function testFindCommandNotAllParamsBinded() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'bread')); + $cmd->execute(); + } + + public function testInsertCommand() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); + $cmd->bindParam('collection', $collection) + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertArrayHasKey('n', $cmd->execute()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(1, $result->count()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage InsertMongoCommand error. Bind all required params first. + */ + public function testInsertCommandNotAllParamsBinded() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); + $cmd->bindParam('collection', $collection)->bindParam('data', array('name' => 'bread')); + $cmd->execute(); + } + + public function testUpdateCommand() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); + $cmd->bindParam('collection', $collection) + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertArrayHasKey('n', $cmd->execute()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE); + $cmd->bindParam('collection', $collection) + ->bindParam('condition', array('name' => 'insert')) + ->bindParam('data', array('$set' => array('name' => 'update'))) + ->bindParam('upsert', false) + ->bindParam('safe', true); + $this->assertArrayHasKey('n', $cmd->execute()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(0, $result->count()); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'update'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(1, $result->count()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage UpdateMongoCommand error. Bind all required params first. + */ + public function testUpdateCommandNotAllParamsBinded() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE); + $cmd->bindParam('collection', $collection)->bindParam('data', array('name' => 'bread')); + $cmd->execute(); + } + + public function testRemoveCommand() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); + $cmd->bindParam('collection', $collection) + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertArrayHasKey('n', $cmd->execute()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(1, $result->count()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE); + $cmd->bindParam('collection', $collection) + ->bindParam('condition', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertArrayHasKey('n', $cmd->execute()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); + $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $result = $cmd->execute(); + $this->assertEquals(0, $result->count()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage RemoveMongoCommand error. Bind all required params first. + */ + public function testRemoveCommandNotAllParamsBinded() + { + $collection = $this->driver->getCollection('items'); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE); + $cmd->bindParam('collection', $collection); + $cmd->execute(); + } + +} From 229a39d6826df3af0d93d4c90a914b8fcd8875f9 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 13:14:31 +0400 Subject: [PATCH 04/25] modified MongoCommand classes --- model/MongoDbCommand.php | 40 +++++++++++++++++++++++--- tests/model/MongoDbCommandTest.php | 57 +++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 663f73e..1becb44 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -18,15 +18,19 @@ class MongoCommandBuilder const REMOVE = 'Remove'; - static public function factory($type) + static public function factory($type, $collection = null) { $class = ucfirst($type) . 'MongoCommand'; - return new $class(); + $command = new $class(); + $command->setCollection($collection); + return $command; } } abstract class MongoDbCommand { + protected $collection; + public function execute() { if ($this->checkParams()) { @@ -38,17 +42,29 @@ abstract class MongoDbCommand public function bindParam($name, $value) { - $this->$name = $value; + if (property_exists($this, $name)) { + $this->$name = $value; + } + return $this; + } + + public function setCollection($collection) + { + $this->collection = $collection; return $this; } - abstract protected function concreteExecute(); + abstract protected function concreteExecute(); abstract protected function checkParams(); } class FindMongoCommand extends MongoDbCommand { + protected $condition; + + protected $fields; + protected function concreteExecute() { return $this->collection->find($this->condition, $this->fields); @@ -66,6 +82,10 @@ class FindMongoCommand extends MongoDbCommand class InsertMongoCommand extends MongoDbCommand { + protected $data; + + protected $safe; + protected function concreteExecute() { return $this->collection->insert($this->data, array('safe' => $this->safe)); @@ -83,6 +103,14 @@ class InsertMongoCommand extends MongoDbCommand class UpdateMongoCommand extends MongoDbCommand { + protected $condition; + + protected $data; + + protected $upsert; + + protected $safe; + protected function concreteExecute() { return $this->collection->update($this->condition, $this->data, array('upsert' => $this->upsert, 'safe' => $this->safe)); @@ -102,6 +130,10 @@ class UpdateMongoCommand extends MongoDbCommand class RemoveMongoCommand extends MongoDbCommand { + protected $condition; + + protected $safe; + protected function concreteExecute() { return $this->collection->remove($this->condition, array('safe' => $this->safe)); diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index 1cc2973..a05a9bb 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -59,8 +59,8 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testFindCommand() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'bread'))->bindParam('fields', array()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'bread'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); } @@ -72,22 +72,22 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testFindCommandNotAllParamsBinded() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'bread')); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'bread')); $cmd->execute(); } public function testInsertCommand() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); - $cmd->bindParam('collection', $collection) + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(1, $result->count()); } @@ -99,33 +99,33 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testInsertCommandNotAllParamsBinded() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); - $cmd->bindParam('collection', $collection)->bindParam('data', array('name' => 'bread')); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd->bindParam('data', array('name' => 'bread')); $cmd->execute(); } public function testUpdateCommand() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); - $cmd->bindParam('collection', $collection) + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE); - $cmd->bindParam('collection', $collection) + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $collection); + $cmd ->bindParam('condition', array('name' => 'insert')) ->bindParam('data', array('$set' => array('name' => 'update'))) ->bindParam('upsert', false) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'update'))->bindParam('fields', array()); + $cmd->bindParam('condition', array('name' => 'update'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(1, $result->count()); } @@ -137,33 +137,33 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testUpdateCommandNotAllParamsBinded() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE); - $cmd->bindParam('collection', $collection)->bindParam('data', array('name' => 'bread')); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $collection); + $cmd->bindParam('data', array('name' => 'bread')); $cmd->execute(); } public function testRemoveCommand() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT); - $cmd->bindParam('collection', $collection) + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(1, $result->count()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE); - $cmd->bindParam('collection', $collection) + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $collection); + $cmd ->bindParam('condition', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); - $cmd->bindParam('collection', $collection)->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); } @@ -175,8 +175,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testRemoveCommandNotAllParamsBinded() { $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE); - $cmd->bindParam('collection', $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $collection); $cmd->execute(); } From 5bff74f20bd55d7ab4f7418dd5d61880ffb0066b Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 16:55:12 +0400 Subject: [PATCH 05/25] added MongoStatement class, modified DbDriver hierarchy --- model/DbDriver.php | 19 ++++++++ model/DbStatement.php | 79 ++++-------------------------- model/MongoDbCommand.php | 21 +++++--- model/MongoDriver.php | 85 ++++++++++++++++++++------------ model/MongoStatement.php | 121 ++++++++++++++++++++++++++++++++++++++++++++++ model/MySQLiStatement.php | 74 +++++++++++++++++++++++++--- model/NoSqlDbDriver.php | 2 +- model/SqlDbDriver.php | 17 ------- 8 files changed, 288 insertions(+), 130 deletions(-) create mode 100644 model/MongoStatement.php diff --git a/model/DbDriver.php b/model/DbDriver.php index b5f2efd..6660027 100644 --- a/model/DbDriver.php +++ b/model/DbDriver.php @@ -49,6 +49,23 @@ abstract class DbDriver return $this->connection; } + /** + * @param mixed $request + * @param mixed $params + * @return DbStatement + */ + public function query($request, $params = array()) + { + $this->connect(); + if (!is_array($params)) { + $params = array($params); + } + $stmt = $this->prepare($request); + $stmt->execute($params); + return $stmt; + } + + /* Abstract methods */ abstract public function insert($table, $data); @@ -56,6 +73,8 @@ abstract class DbDriver abstract public function update($table, $data, $condition); abstract public function delete($table, $condition); + + abstract public function prepare($request); abstract public function getInsertId($table = null, $key = null); diff --git a/model/DbStatement.php b/model/DbStatement.php index 301c574..323b61d 100644 --- a/model/DbStatement.php +++ b/model/DbStatement.php @@ -20,38 +20,16 @@ abstract class DbStatement /** * @var string */ - protected $sql; - - protected $map = null; + protected $request; protected $params = array(); protected $result; - public function __construct($driver, $sql) + public function __construct($driver, $request) { - $this->driver = $driver; - $this->sql = $sql; - } - - public function bindParam($param, &$value) - { - if ($this->map === null) { - $this->mapPlaceholders(); - } - if (count($this->map) > 0) { - if (!is_string($param) && !is_int($param)) { - throw new Exception('Placeholder must be an array or string'); - } - if (is_object($value) && ! ($value instanceof DbExpr)) { - throw new Exception('Objects excepts DbExpr not allowed.'); - } - if (isset($this->map[$param])) { - $this->params[$param] = &$value; - return true; - } - } - return false; + $this->driver = $driver; + $this->request = $request; } /** @@ -67,48 +45,7 @@ abstract class DbStatement } return $this->driverExecute($this->assemble()); } - - protected function mapPlaceholders() - { - $matches = array(); - if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->sql, $matches, PREG_OFFSET_CAPTURE)) { - $noname = 0; - foreach ($matches[0] as $id=>$match) { - $match[2] = $matches[1][$id][0]; - $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++; - $this->map[$name]['placeholder'] = $match[0]; - $this->map[$name]['offset'][] = $match[1]; - } - } - } - - protected function assemble() - { - if (empty($this->map)) { - return $this->sql; - } - - $query = $this->sql; - $placeholders = array(); - foreach($this->map as $name => $place) { - $value = $this->driver->quote($this->params[$name]); - foreach ($place['offset'] as $offset) { - $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value); - } - } - - ksort($placeholders); - - $increment = 0; - foreach($placeholders as $current_offset => $placeholder) { - $offset = $current_offset + $increment; - $length = mb_strlen($placeholder['placeholder']); - $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length); - $increment = (($increment - $length) + mb_strlen($placeholder['value'])); - } - return $query; - } - + public function __destruct() { $this->close(); @@ -164,6 +101,10 @@ abstract class DbStatement } /* Abstract methods */ + + abstract public function bindParam($param, &$value); + + abstract protected function assemble(); abstract public function fetch($style = Db::FETCH_OBJ); @@ -181,5 +122,5 @@ abstract class DbStatement /** * @return bool */ - abstract protected function driverExecute($sql); + abstract protected function driverExecute($request); } diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 1becb44..0af1575 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -65,9 +65,15 @@ class FindMongoCommand extends MongoDbCommand protected $fields; + protected $multiple = true; + protected function concreteExecute() { - return $this->collection->find($this->condition, $this->fields); + if($this->multiple) { + return $this->collection->find($this->condition, $this->fields); + } else { + return $this->collection->findOne($this->condition, $this->fields); + } } protected function checkParams() @@ -84,7 +90,7 @@ class InsertMongoCommand extends MongoDbCommand { protected $data; - protected $safe; + protected $safe = true; protected function concreteExecute() { @@ -107,13 +113,16 @@ class UpdateMongoCommand extends MongoDbCommand protected $data; - protected $upsert; + protected $multiple = true; + + protected $upsert = false; - protected $safe; + protected $safe = true; protected function concreteExecute() { - return $this->collection->update($this->condition, $this->data, array('upsert' => $this->upsert, 'safe' => $this->safe)); + return $this->collection->update($this->condition, $this->data, + array('multiple' => $this->multiple, 'upsert' => $this->upsert, 'safe' => $this->safe)); } protected function checkParams() @@ -132,7 +141,7 @@ class RemoveMongoCommand extends MongoDbCommand { protected $condition; - protected $safe; + protected $safe = true; protected function concreteExecute() { diff --git a/model/MongoDriver.php b/model/MongoDriver.php index 8f75db0..4d1219b 100644 --- a/model/MongoDriver.php +++ b/model/MongoDriver.php @@ -12,66 +12,90 @@ */ class MongoDriver extends NoSqlDbDriver { - - - protected $last_inset_id = 0; + protected $last_insert_id = 0; protected $db_name = 'admin'; + protected $db = null; + protected function getCollection($name) { + if (!$this->connection) { + $this->connect(); + } return $this->connection->selectCollection($this->db, $name); } - public function find($collection, $condition = array(), $fields = array(), $cache_key = null) + public function find($collection, $condition = array(), $fields = array()) { - return $this->getCollection($collection)->find($condition, $fields); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'fields' => $fields + ); + + return $this->query($command, $params); } public function get($collection, $condition, $fields = array()) { - return $this->getCollection($collection)->findOne($condition, $fields); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'fields' => $fields, + 'multiple' => false + ); + return $this->query($command, $params); } public function insert($collection, $data, $safe = true) { - $result = $this->getCollection($collection)->insert($data, array('safe' => $safe)); - if (isset($result['ok']) && $result['ok'] == 1) { - $this->last_inset_id = (string) $data['_id']; - return true; - } else { - return false; - } + $command = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->getCollection($collection)); + $params = array( + 'data' => $data, + 'safe' => $safe + ); + + return $this->query($command, $params)->affectedRows(); } - public function update($collection, $data, $condition = array(), $upsert = false, $safe = true) + public function update($collection, $data, $condition = array(), $multiple = true, $upsert = false, $safe = true) { - $result = $this->getCollection($collection)->update($condition, $data, array('upsert' => $upsert, 'safe' => $safe)); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->getCollection($collection)); + $params = array( + 'data' => $data, + 'condition' => $condition, + 'multiple' => $multiple, + 'upsert' => $upsert, + 'safe' => $safe + ); - if (isset($result['ok']) && $result['ok'] == 1) { - if(isset($result['updatedExisting']) && isset($result['upserted'])) { - $this->last_inset_id = (string) $result['upserted']; - } - return $result['n']; - } else { - return false; - } + return $this->query($command, $params)->affectedRows(); } public function delete($collection, $condition = array(), $safe = true) { - $result = $this->getCollection($collection)->remove($condition, array('safe' => $safe)); - if (isset($result['ok']) && $result['ok'] == 1) { - return $result['n']; - } else { - return false; - } + $command = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'safe' => $safe + ); + + return $this->query($command, $params)->affectedRows(); } + /** + * @param mixed $request + * @return DbStatement + */ + public function prepare($request) + { + return new MongoStatement($this, $request); + } public function getInsertId($table = null, $key = null) { - return $this->last_inset_id; + return $this->last_insert_id; } public function isConnected() @@ -110,4 +134,3 @@ class MongoDriver extends NoSqlDbDriver $this->db = $this->connection->selectDB($this->config['db']); } } - diff --git a/model/MongoStatement.php b/model/MongoStatement.php new file mode 100644 index 0000000..da353ee --- /dev/null +++ b/model/MongoStatement.php @@ -0,0 +1,121 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-15 + */ + +/** + * @property MongoDriver $driver + * @property MongoCursor $result + */ +class MongoStatement extends DbStatement +{ + + public function fetch($style = Db::FETCH_OBJ) + { + if (!$this->result) { + return false; + } + + $row = false; + switch ($style) { + case Db::FETCH_OBJ: + $row = $this->fetchObject(); + break; + case Db::FETCH_ASSOC: + if ($this->result instanceof MongoCursor) { + $row = $this->result->getNext(); + } else { + $row = $this->result; + } + break; + default: + throw new Exception('Invalid fetch mode "' . $style . '" specified'); + } + return $row; + } + + public function fetchObject($class = 'stdClass') + { + if ($this->result instanceof MongoCursor) { + $row = $this->result->getNext(); + } else { + $row = $this->result; + } + if (is_array($row) && isset($row['_id'])) { + $row = new ArrayObject($row, ArrayObject::ARRAY_AS_PROPS); + } else { + $row = false; + } + return $row; + } + + public function close() + { + // TODO: Implement close() method. + } + + /** + * @return int + */ + public function affectedRows() + { + if (is_array($this->result)) { + if (isset($this->result['ok']) && $this->result['ok'] == 1 && isset($this->result['n'])) { + return $this->result['n']; + } else { + return false; + } + } + return false; + } + + public function numRows() + { + if ($this->result instanceof MongoCursor) { + return $this->result->count(); + } else { + return false; + } + } + + /** + * @param MongoDbComand $request + * @return bool + */ + protected function driverExecute($request) + { + $mongo = $this->driver->getConnection(); + if ($mongo instanceof Mongo) { + if (DEBUG) { + $profiler = Profiler::getInstance()->profilerCommand('Mongo', $request); + $result = $request->execute(); + $profiler->end(); + } else { + $result = $request->execute(); + } + if ($result === false) { + throw new Exception('MongoDB request error.'); + } + if ($result instanceof MongoCursor || is_array($result)) { + $this->result = $result; + } + return true; + } else { + throw new Exception('No connection to MongoDB server.'); + } + } + + public function bindParam($param, &$value) + { + $this->request->bindParam($param, $value); + } + + protected function assemble() + { + return $this->request; + } +} \ No newline at end of file diff --git a/model/MySQLiStatement.php b/model/MySQLiStatement.php index 6e0ec6b..e0fb4cf 100644 --- a/model/MySQLiStatement.php +++ b/model/MySQLiStatement.php @@ -15,7 +15,69 @@ */ class MySQLiStatement extends DbStatement { - + + protected $map = null; + + public function bindParam($param, &$value) + { + if ($this->map === null) { + $this->mapPlaceholders(); + } + if (count($this->map) > 0) { + if (!is_string($param) && !is_int($param)) { + throw new Exception('Placeholder must be an array or string'); + } + if (is_object($value) && ! ($value instanceof DbExpr)) { + throw new Exception('Objects excepts DbExpr not allowed.'); + } + if (isset($this->map[$param])) { + $this->params[$param] = &$value; + return true; + } + } + return false; + } + protected function mapPlaceholders() + { + $matches = array(); + if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->request, $matches, PREG_OFFSET_CAPTURE)) { + $noname = 0; + foreach ($matches[0] as $id=>$match) { + $match[2] = $matches[1][$id][0]; + $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++; + $this->map[$name]['placeholder'] = $match[0]; + $this->map[$name]['offset'][] = $match[1]; + } + } + } + + protected function assemble() + { + if (empty($this->map)) { + return $this->request; + } + + $query = $this->request; + $placeholders = array(); + foreach($this->map as $name => $place) { + $value = $this->driver->quote($this->params[$name]); + foreach ($place['offset'] as $offset) { + $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value); + } + } + + ksort($placeholders); + + $increment = 0; + foreach($placeholders as $current_offset => $placeholder) { + $offset = $current_offset + $increment; + $length = mb_strlen($placeholder['placeholder']); + $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length); + $increment = (($increment - $length) + mb_strlen($placeholder['value'])); + } + return $query; + } + /** * Fetches single row * @@ -77,21 +139,21 @@ class MySQLiStatement extends DbStatement return false; } - protected function driverExecute($sql) + protected function driverExecute($request) { /** * @var MySQLi */ $mysqli = $this->driver->getConnection(); if (DEBUG) { - $profiler = Profiler::getInstance()->profilerCommand('MySQL', $sql); - $result = $mysqli->query($sql); + $profiler = Profiler::getInstance()->profilerCommand('MySQL', $request); + $result = $mysqli->query($request); $profiler->end(); } else { - $result = $mysqli->query($sql); + $result = $mysqli->query($request); } if ($result === false) { - $message = $mysqli->error . "\nQuery: \"" . $sql . '"'; + $message = $mysqli->error . "\nQuery: \"" . $request . '"'; throw new Exception($message, $mysqli->errno); } if ($result instanceof MySQLi_Result) { diff --git a/model/NoSqlDbDriver.php b/model/NoSqlDbDriver.php index b82e3d3..19a7020 100644 --- a/model/NoSqlDbDriver.php +++ b/model/NoSqlDbDriver.php @@ -10,5 +10,5 @@ abstract class NoSqlDbDriver extends DbDriver { - abstract function find($collection, $condition = array(), $fields = array(), $cache_key = null); + abstract function find($collection, $condition = array(), $fields = array()); } \ No newline at end of file diff --git a/model/SqlDbDriver.php b/model/SqlDbDriver.php index 8d8b9af..de78409 100644 --- a/model/SqlDbDriver.php +++ b/model/SqlDbDriver.php @@ -36,22 +36,6 @@ abstract class SqlDbDriver extends DbDriver } /** - * @param string $sql - * @param mixed $params - * @return DbStatement - */ - public function query($sql, $params = array()) - { - $this->connect(); - if (!is_array($params)) { - $params = array($params); - } - $stmt = $this->prepare($sql); - $stmt->execute($params); - return $stmt; - } - - /** * @param string $table * @param mixed $bind * @param mixed $on_duplicate @@ -171,7 +155,6 @@ abstract class SqlDbDriver extends DbDriver /** * @return DbStatement */ - abstract public function prepare($sql); abstract protected function driverQuote($value); From b9353065b81ce06d79e4b4cf86e91fa3e2661dbb Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 16:55:55 +0400 Subject: [PATCH 06/25] modified tests for new DbDriver hierarchy --- tests/model/DbStatementTest.php | 73 +---------------------- tests/model/MongoDbCommandTest.php | 40 ++++++------- tests/model/MongoDriverTest.php | 115 +++++++++++++++++++++++++++--------- tests/model/MySQLiStatementTest.php | 78 ++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 123 deletions(-) diff --git a/tests/model/DbStatementTest.php b/tests/model/DbStatementTest.php index 4624bfa..b19a955 100644 --- a/tests/model/DbStatementTest.php +++ b/tests/model/DbStatementTest.php @@ -43,68 +43,7 @@ class DbStatementTest extends PHPUnit_Framework_TestCase public function testConstruct() { $this->assertAttributeEquals($this->driver, 'driver', $this->stmt); - $this->assertAttributeEquals($this->sql, 'sql', $this->stmt); - } - - public function testBindParam() - { - $val = $this->getMockBuilder('DbExpr') - ->disableOriginalConstructor() - ->getMock(); - $this->assertFalse($this->stmt->bindParam('var', $val)); - $this->assertTrue($this->stmt->bindParam('place', $val)); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage Placeholder must be an array or string - * @TODO: change Exception Message 'Placeholder must be an array or string' - no arrays available - */ - public function testBindParamExceptionParam() - { - $val = 1; - $this->stmt->bindParam(array(), $val); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage Objects excepts DbExpr not allowed. - */ - public function testBindParamExceptionWrongObject() - { - $val = $this->getMock('NotDbExpr'); - $this->stmt->bindParam('paa', $val); - } - - public function testExecute() - { - $this->stmt - ->expects($this->once()) - ->method('driverExecute') - ->with($this->anything()) - ->will($this->returnCallback(array($this, 'dbStatementAssemble'))); - - $this->driver - ->expects($this->any()) - ->method('quote') - ->with($this->anything()) - ->will($this->returnCallback(array($this, 'driverQuote'))); - - $result = $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); - $this->assertEquals('SELECT * place_val FROM place_val AND new_val', $result); - } - - public function testExecuteNoPlaceholders() - { - $this->sql = 'PLAIN SQL'; - $this->stmt = $this->getMockForAbstractClass('DbStatement', array($this->driver, $this->sql)); - $this->stmt - ->expects($this->once()) - ->method('driverExecute') - ->with($this->anything()) - ->will($this->returnCallback(array($this, 'dbStatementAssemble'))); - $result = $this->stmt->execute(array()); - $this->assertEquals('PLAIN SQL', $result); + $this->assertAttributeEquals($this->sql, 'request', $this->stmt); } public function testFetch() @@ -131,16 +70,6 @@ class DbStatementTest extends PHPUnit_Framework_TestCase $this->assertEquals(31, $result['one']); } - public function dbStatementAssemble($val) - { - return $val; - } - - public function driverQuote($val) - { - return $val; - } - public function dbStatementFetch($style) { $result = null; diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index a05a9bb..c540240 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -22,6 +22,8 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase private $driver; + private $collection; + public function setUp() { $this->conf = array( @@ -38,6 +40,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $db->authenticate($this->conf['username'], $this->conf['password']); $collection = 'items'; $db->dropCollection($collection); + $this->collection = $db->selectCollection($collection); } public function testCommandFactory() @@ -58,8 +61,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testFindCommand() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'bread'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); @@ -71,22 +73,20 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase */ public function testFindCommandNotAllParamsBinded() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'bread')); $cmd->execute(); } public function testInsertCommand() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(1, $result->count()); @@ -98,22 +98,19 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase */ public function testInsertCommandNotAllParamsBinded() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); - $cmd->bindParam('data', array('name' => 'bread')); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); $cmd->execute(); } public function testUpdateCommand() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->collection); $cmd ->bindParam('condition', array('name' => 'insert')) ->bindParam('data', array('$set' => array('name' => 'update'))) @@ -121,7 +118,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); @@ -136,33 +133,31 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase */ public function testUpdateCommandNotAllParamsBinded() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->collection); $cmd->bindParam('data', array('name' => 'bread')); $cmd->execute(); } public function testRemoveCommand() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(1, $result->count()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); $cmd ->bindParam('condition', array('name' => 'insert')) ->bindParam('safe', true); $this->assertArrayHasKey('n', $cmd->execute()); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); @@ -174,8 +169,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase */ public function testRemoveCommandNotAllParamsBinded() { - $collection = $this->driver->getCollection('items'); - $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $collection); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); $cmd->execute(); } diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index 39aa4bc..bfc2bef 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -10,8 +10,12 @@ * Unit tests for MongoDriver class */ +require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php'; +require_once dirname(__FILE__) . '/../../model/MongoDbCommand.php'; +require_once dirname(__FILE__) . '/../../model/DbStatement.php'; +require_once dirname(__FILE__) . '/../../model/MongoStatement.php'; require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; class MongoDriverTest extends PHPUnit_Framework_TestCase @@ -101,66 +105,121 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertFalse($mongo->isConnected()); } + + /** + * @runInSeparateProcess + */ public function testFind() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); - $this->assertEquals(2, $mongo->find('items', array('name' => 'eggs'))->count()); - $this->assertEquals(3, $mongo->find('items', array('price' => array('$lt' => 3.5)))->count()); - $this->assertEquals(5, $mongo->find('items', array())->count()); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(2, $mongo->find('items', array('name' => 'eggs'))->numRows()); + $eggs = $mongo->find('items', array('name' => 'eggs')); + $egg = $eggs->fetch(); + $this->assertEquals(20, $egg->quantity); + $egg = $eggs->fetchObject(); + $this->assertEquals('eggs', $egg->name); + $this->assertFalse($eggs->fetchObject()); + + $this->assertEquals(3, $mongo->find('items', array('price' => array('$lt' => 3.5)))->numRows()); + $data = $mongo->find('items', array('price' => array('$lt' => 3.5))); + $count = 0; + while($row = $data->fetch(Db::FETCH_ASSOC)) { + $count++; + $this->assertLessThan(3.5, $row['price']); + } + $this->assertEquals(3, $count); + $this->assertEquals(5, $mongo->find('items', array())->numRows()); } + /** + * @runInSeparateProcess + */ public function testGet() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $bread = $mongo->get('items', array('name' => 'bread')); - $this->assertEquals(3.2, $bread['price']); + $eggs = $mongo->get('items', array('name' => 'eggs'))->fetchObject(); + $this->assertEquals(20, $eggs->quantity); + $eggs = $mongo->get('items', array('name' => 'eggs'))->fetch(); + $this->assertEquals('eggs', $eggs->name); + $this->assertInstanceOf('MongoId', $eggs->_id); } + /** + * @runInSeparateProcess + */ public function testRemove() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); $this->assertEquals(0, $mongo->delete('items', array('name' => 'esggs'))); $this->assertEquals(2, $mongo->delete('items', array('name' => 'eggs'))); $this->assertEquals(1, $mongo->delete('items', array('name' => 'bread'))); - $this->assertEquals(0, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertEquals(0, $mongo->find('items', array('name' => 'bread'))->numRows()); } + /** + * @runInSeparateProcess + */ public function testInsert() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); - $this->assertTrue($mongo->insert('items', array('name' => 'bread'))); - $this->assertNotEmpty($mongo->getInsertId()); - $this->assertEquals(2, $mongo->find('items', array('name' => 'bread'))->count()); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(0, $mongo->insert('items', array('name' => 'bread'))); + //$this->assertNotEmpty($mongo->getInsertId()); + $this->assertEquals(2, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(0, $mongo->insert('items', array('name' => 'meat', 'weight' => 230))); + $this->assertEquals(230, $mongo->get('items', array('name' => 'meat'))->fetch()->weight); } + /** + * @runInSeparateProcess + */ public function testUpdate() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->count()); - $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'fish')); - $fish = $mongo->get('items', array('name' => 'fish')); - $this->assertEquals(200, $fish['price']); - $this->assertEquals('today', $fish['date']); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(1, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'fish'))); + $this->assertEquals(2, $mongo->update('items', array('$set' => array('price' => 1)), array('name' => 'eggs'))); + $fish = $mongo->get('items', array('name' => 'fish'))->fetch(); + $this->assertEquals(200, $fish->price); + $this->assertEquals('today', $fish->date); $this->assertEquals(0, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'))); } + /** + * @runInSeparateProcess + */ public function testUpsert() { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); - $this->assertTrue($mongo->insert('items', array('name' => 'bread'))); - $id = $mongo->getInsertId(); - $this->assertNotEmpty($id); - $this->assertEquals(1, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'), true)); - $this->assertNotEquals($id, $mongo->getInsertId()); + $mongo->insert('items', array('name' => 'bread')); + $this->assertEquals(1, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'), true, true)); + $this->assertEquals('today', $mongo->get('items', array('name' => 'ball'))->fetch()->date); } } \ No newline at end of file diff --git a/tests/model/MySQLiStatementTest.php b/tests/model/MySQLiStatementTest.php index 1e695c6..dbdf27f 100644 --- a/tests/model/MySQLiStatementTest.php +++ b/tests/model/MySQLiStatementTest.php @@ -25,6 +25,8 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase private $stmt; + private $sql; + private $testData = array( array('one' => 11, 'two' => 12), array('one' => 21, 'two' => 22), @@ -50,6 +52,72 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase } } + public function testBindParam() + { + $val = $this->getMockBuilder('DbExpr') + ->disableOriginalConstructor() + ->getMock(); + $this->assertFalse($this->stmt->bindParam('var', $val)); + $this->assertTrue($this->stmt->bindParam('place', $val)); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Placeholder must be an array or string + * @TODO: change Exception Message 'Placeholder must be an array or string' - no arrays available + */ + public function testBindParamExceptionParam() + { + $val = 1; + $this->stmt->bindParam(array(), $val); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Objects excepts DbExpr not allowed. + */ + public function testBindParamExceptionWrongObject() + { + $val = $this->getMock('NotDbExpr'); + $this->stmt->bindParam('paa', $val); + } + + /** + * @runInSeparateProcess + */ + public function testExecute() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $this->driver + ->expects($this->any()) + ->method('quote') + ->with($this->anything()) + ->will($this->returnCallback(array($this, 'driverQuote'))); + + $this->setDriverGetConnectionMethod(); + + $result = $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); + $this->assertEquals('SELECT * place_val FROM place_val AND new_val', $result); + } + + /** + * @runInSeparateProcess + */ + public function testExecuteNoPlaceholders() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->setDriverGetConnectionMethod(); + + $this->sql = 'PLAIN SQL'; + $result = $this->stmt->execute(array()); + $this->assertEquals('PLAIN SQL', $result); + } + /** * @runInSeparateProcess */ @@ -240,4 +308,14 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase return $this; } + public function driverQuote($val) + { + return $val; + } + + public function dbStatementAssemble($val) + { + return $val; + } + } \ No newline at end of file From 7897978261abdac2d31233f6dcea7b69e97de16b Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 18:06:35 +0400 Subject: [PATCH 07/25] MongoStatement::close() implemented --- model/MongoStatement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/MongoStatement.php b/model/MongoStatement.php index da353ee..0a94548 100644 --- a/model/MongoStatement.php +++ b/model/MongoStatement.php @@ -55,7 +55,7 @@ class MongoStatement extends DbStatement public function close() { - // TODO: Implement close() method. + $this->result = null; } /** From 5b5ccb9971f7428a83233ad30e3f17296593b6cd Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 18:07:01 +0400 Subject: [PATCH 08/25] MongoStatement class tested --- tests/model/MongoStatementTest.php | 335 +++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 tests/model/MongoStatementTest.php diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php new file mode 100644 index 0000000..c203dcc --- /dev/null +++ b/tests/model/MongoStatementTest.php @@ -0,0 +1,335 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage UnitTests + * @since 2011-11-15 + * + * Unit tests for MySQLiStatement class + */ + +require_once dirname(__FILE__) . '/../../util/profiler/CommandProfiler.php'; +require_once dirname(__FILE__) . '/../../util/profiler/Profiler.php'; +require_once dirname(__FILE__) . '/../../model/Db.php'; +require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/DbStatement.php'; +require_once dirname(__FILE__) . '/../../model/MongoStatement.php'; + +class MongoStatementTest extends PHPUnit_Framework_TestCase +{ + + + private $driver; + + private $stmt; + + private $request; + + private $testData = array( + array('one' => 11, 'two' => 12), + array('one' => 21, 'two' => 22), + array('one' => 31, 'two' => 32), + ); + + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + $this->setPreserveGlobalState(false); + return parent::run($result); + } + + public function setUp() + { + if (!isset($this->stmt)) { + $this->driver = $this->getMockBuilder('DbDriverMock') + ->disableOriginalConstructor() + ->setMethods(array('getConnection')) + ->getMock(); + $this->request = $this->getMockBuilder('MongoDbCommandMock') + ->disableOriginalConstructor() + ->setMethods(array('execute', 'bindParam')) + ->getMock(); + $this->stmt = new MongoStatement($this->driver, $this->request); + } + } + + /** + * @runInSeparateProcess + */ + public function testAffectedNumRowsNoResult() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->assertFalse($this->stmt->affectedRows()); + $this->assertFalse($this->stmt->numRows()); + + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->any()) + ->method('execute') + ->will($this->returnValue(array())); + $this->stmt->execute(); + $this->assertFalse($this->stmt->affectedRows()); + } + + /** + * @runInSeparateProcess + */ + public function testAffectedNumRows() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->any()) + ->method('execute') + ->will($this->returnValue(array('n' => 20, 'ok' => 1))); + $this->stmt->execute(); + $this->assertEquals(20, $this->stmt->affectedRows()); + } + + + /** + * @runInSeparateProcess + */ + public function testExecute() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $this->setDriverGetConnectionMethod()->setRequestExecuteMethod(); + $this->assertTrue($this->stmt->execute()); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage MongoDB request error. + */ + public function testExecuteNoResult() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->any()) + ->method('execute') + ->will($this->returnValue(false)); + $this->stmt->execute(); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage No connection to MongoDB server. + */ + public function testExecuteNoConnection() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->driver + ->expects($this->any()) + ->method('getConnection') + ->will($this->returnValue(false)); + $this->stmt->execute(); + } + + /** + * @runInSeparateProcess + */ + public function testExecuteWithDebug() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->setDriverGetConnectionMethod()->setRequestExecuteMethod(); + $this->assertTrue($this->stmt->execute()); + $this->assertEquals(10, $this->stmt->numRows()); + } + + /** + * @runInSeparateProcess + */ + public function testBindParam() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + + $this->request + ->expects($this->once()) + ->method('bindParam') + ->will($this->returnValue(true)); + + $this->setDriverGetConnectionMethod()->setRequestExecuteMethod(); + $this->assertTrue($this->stmt->execute(array('one' => 'two'))); + + $this->assertAttributeNotEquals(null, 'result', $this->stmt); + $this->stmt->close(); + $this->assertAttributeEquals(null, 'result', $this->stmt); + } + + /** + * @runInSeparateProcess + */ + public function testFetch() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->assertFalse($this->stmt->fetch()); + + $this->setDriverGetConnectionMethod()->setRequestForFetch(); + + $this->stmt->execute(); + $result = $this->stmt->fetch(); + $this->assertEquals('prev', $result->next); + $this->assertFalse($this->stmt->fetch()); + } + + /** + * @runInSeparateProcess + */ + public function testFetchWithInitialArray() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->assertFalse($this->stmt->fetch()); + + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $this->assertFalse($this->stmt->fetch()); + } + + /** + * @runInSeparateProcess + */ + public function testFetchAssocFromCursor() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->assertFalse($this->stmt->fetch()); + + $this->setDriverGetConnectionMethod()->setRequestForFetch(); + + $this->stmt->execute(); + $result = $this->stmt->fetch(Db::FETCH_ASSOC); + $this->assertEquals('prev', $result['next']); + $this->assertEquals(array(), $this->stmt->fetch(Db::FETCH_ASSOC)); + } + + /** + * @runInSeparateProcess + */ + public function testFetchAssocFromArray() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->assertFalse($this->stmt->fetch()); + + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $result = $this->stmt->fetch(Db::FETCH_ASSOC); + $this->assertEquals('val', $result['some']); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage Invalid fetch mode "222" specified + */ + public function testFetchWrongMode() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->assertFalse($this->stmt->fetch()); + + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $result = $this->stmt->fetch(222); + } + + private function setDriverGetConnectionMethod() + { + $mongoMock = $this->getMock('Mongo'); + + $this->driver + ->expects($this->any()) + ->method('getConnection') + ->will($this->returnValue($mongoMock)); + return $this; + } + + public function setRequestExecuteMethod() + { + $resultMock = $this->getMockBuilder('MongoCursor') + ->setMethods(array('count')) + ->disableOriginalConstructor() + ->getMock(); + + $resultMock + ->expects($this->any()) + ->method('count') + ->will($this->returnValue(10)); + + $this->request + ->expects($this->any()) + ->method('execute') + ->will($this->returnValue($resultMock)); + + return $this; + } + + public function setRequestForFetch() + { + $resultMock = $this->getMockBuilder('MongoCursor') + ->setMethods(array('count', 'getNext')) + ->disableOriginalConstructor() + ->getMock(); + + $resultMock + ->expects($this->any()) + ->method('count') + ->will($this->returnValue(10)); + $resultMock + ->expects($this->at(0)) + ->method('getNext') + ->will($this->returnValue(array('next' => 'prev', '_id' => 10))); + $resultMock + ->expects($this->at(1)) + ->method('getNext') + ->will($this->returnValue(array())); + + $this->request + ->expects($this->any()) + ->method('execute') + ->will($this->returnValue($resultMock)); + + return $this; + } +} \ No newline at end of file From 594ec034f91a2fb776941ca2a5ea36af1e1e79f4 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 19:19:30 +0400 Subject: [PATCH 09/25] added Order, Limit, Skip methods to MongoStatement --- model/DbStatement.php | 12 ----- model/MongoStatement.php | 30 +++++++++++++ model/MySQLiStatement.php | 12 +++++ tests/model/DbStatementTest.php | 6 +-- tests/model/MongoDriverTest.php | 39 ++++++++++++++++- tests/model/MongoStatementTest.php | 90 +++++++++++++++++++++++++++++++++++++- 6 files changed, 171 insertions(+), 18 deletions(-) diff --git a/model/DbStatement.php b/model/DbStatement.php index 323b61d..5fcf52c 100644 --- a/model/DbStatement.php +++ b/model/DbStatement.php @@ -88,18 +88,6 @@ abstract class DbStatement return false; } - /** - * @return array - */ - public function fetchPairs() - { - $data = array(); - while ($row = $this->fetch(Db::FETCH_NUM)) { - $data[$row[0]] = $row[1]; - } - return $data; - } - /* Abstract methods */ abstract public function bindParam($param, &$value); diff --git a/model/MongoStatement.php b/model/MongoStatement.php index 0a94548..8b86913 100644 --- a/model/MongoStatement.php +++ b/model/MongoStatement.php @@ -14,6 +14,36 @@ class MongoStatement extends DbStatement { + public function order($sort = array()) + { + if ($this->result instanceof MongoCursor) { + $this->result->sort($sort); + return $this; + } else { + throw new Exception('MongoStatement error. Impossible order results of opened cursor.'); + } + } + + public function skip($skip = 0) + { + if ($this->result instanceof MongoCursor) { + $this->result->skip($skip); + return $this; + } else { + throw new Exception('MongoStatement error. Impossible skip results of opened cursor.'); + } + } + + public function limit($limit = 0) + { + if ($this->result instanceof MongoCursor) { + $this->result->limit($limit); + return $this; + } else { + throw new Exception('MongoStatement error. Impossible limit results of opened cursor.'); + } + } + public function fetch($style = Db::FETCH_OBJ) { if (!$this->result) { diff --git a/model/MySQLiStatement.php b/model/MySQLiStatement.php index e0fb4cf..3024d67 100644 --- a/model/MySQLiStatement.php +++ b/model/MySQLiStatement.php @@ -117,6 +117,18 @@ class MySQLiStatement extends DbStatement { return $this->result->fetch_object($class); } + + /** + * @return array + */ + public function fetchPairs() + { + $data = array(); + while ($row = $this->fetch(Db::FETCH_NUM)) { + $data[$row[0]] = $row[1]; + } + return $data; + } public function close() { diff --git a/tests/model/DbStatementTest.php b/tests/model/DbStatementTest.php index b19a955..a2ec50c 100644 --- a/tests/model/DbStatementTest.php +++ b/tests/model/DbStatementTest.php @@ -65,9 +65,9 @@ class DbStatementTest extends PHPUnit_Framework_TestCase $this->assertEquals(11, $result[0]->one); $this->assertEquals(32, $result[2]->two); - reset($this->testData); - $result = $this->stmt->fetchPairs(); - $this->assertEquals(31, $result['one']); +// reset($this->testData); +// $result = $this->stmt->fetchPairs(); +// $this->assertEquals(31, $result['one']); } public function dbStatementFetch($style) diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index bfc2bef..5bbd441 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -105,7 +105,6 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertFalse($mongo->isConnected()); } - /** * @runInSeparateProcess */ @@ -139,6 +138,44 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess */ + public function testOrderSkipLimit() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $mongo = new MongoDriver($this->conf); + + $count = $mongo->find('items', array())->numRows(); + + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(2, $mongo->find('items', array('name' => 'eggs'))->numRows()); + $mongo->insert('items', array('name' => 'fdsbssc')); + $mongo->insert('items', array('name' => 'boc')); + $mongo->insert('items', array('name' => 'abc')); + $mongo->insert('items', array('name' => 'vcxxc')); + $mongo->insert('items', array('name' => 'abbc')); + $mongo->insert('items', array('name' => 'dsbssc')); + $mongo->insert('items', array('name' => 'bssc')); + + $data = $mongo->find('items', array()); + $this->assertEquals($count + 7, $data->numRows()); + $data->order(array('name' => 1)); + $this->assertEquals('abbc', $data->fetch()->name); + $this->assertEquals('abc', $data->fetch()->name); + $this->assertEquals('boc', $data->fetch()->name); + $data = $mongo->find('items', array()); + $data->order(array('name' => -1)); + $data->skip(3); + $data->limit(1); + while($row = $data->fetch()) { + $this->assertEquals('fdsbssc', $row->name); + } + } + + /** + * @runInSeparateProcess + */ public function testGet() { if (!defined('DEBUG')) { diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php index c203dcc..c7e10c1 100644 --- a/tests/model/MongoStatementTest.php +++ b/tests/model/MongoStatementTest.php @@ -271,7 +271,84 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue(array('some' => 'val'))); $this->stmt->execute(); - $result = $this->stmt->fetch(222); + $this->stmt->fetch(222); + } + + /** + * @runInSeparateProcess + */ + public function testSkipOrderLimit() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->setDriverGetConnectionMethod()->setRequestForFetch(); + + $this->stmt->execute(); + $this->assertInstanceOf('MongoStatement', $this->stmt->order(array('id' => 1))); + + $this->stmt->fetch(); + $this->stmt->fetch(); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage MongoStatement error. Impossible order results of opened cursor + */ + public function testOrderException() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $this->stmt->order(array('id' => 1)); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage MongoStatement error. Impossible skip results of opened cursor + */ + public function testSkipException() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $this->stmt->skip(array('id' => 1)); + } + + /** + * @runInSeparateProcess + * @expectedException Exception + * @expectedExceptionMessage MongoStatement error. Impossible limit results of opened cursor + */ + public function testLimitException() + { + if (!defined('DEBUG')) { + define('DEBUG', true); + } + $this->setDriverGetConnectionMethod(); + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('some' => 'val'))); + + $this->stmt->execute(); + $this->stmt->limit(array('id' => 1)); } private function setDriverGetConnectionMethod() @@ -308,12 +385,21 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase public function setRequestForFetch() { $resultMock = $this->getMockBuilder('MongoCursor') - ->setMethods(array('count', 'getNext')) + ->setMethods(array('count', 'getNext', 'limit', 'sort', 'skip')) ->disableOriginalConstructor() ->getMock(); $resultMock ->expects($this->any()) + ->method('limit'); + $resultMock + ->expects($this->any()) + ->method('sort'); + $resultMock + ->expects($this->any()) + ->method('skip'); + $resultMock + ->expects($this->any()) ->method('count') ->will($this->returnValue(10)); $resultMock From e740ae70000a6eccce7fbb875e29b968ed3cc786 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 19:19:58 +0400 Subject: [PATCH 10/25] MongoModel class created --- model/MongoModel.php | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 model/MongoModel.php diff --git a/model/MongoModel.php b/model/MongoModel.php new file mode 100644 index 0000000..02fc56e --- /dev/null +++ b/model/MongoModel.php @@ -0,0 +1,78 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage Model + * @since 2011-11-15 + */ + +abstract class MongoModel extends Model +{ + + public function find($condition) + { + return $this->db->find($this->table(), $condition); + } + + protected function order(MongoStatement $cursor, $sort = array()) + { + return $cursor->sort($sort); + } + + protected function skip(MongoStatement $cursor, $skip = 0) + { + return $cursor->skip($skip); + } + + protected function limit(MongoStatement $cursor, $limit = 0) + { + return $cursor->limit($limit); + } + + public function get($id) + { + return $this->db->get($this->table(), array('_id' => $id))->fetch(); + } + + public function delete($id) + { + return $this->db->delete($this->table(), array('id' => $id)); + } + + protected function fetchField($data, $params = array(), $field, $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->db->find($this->table(), $data, array($field => 1))->fetchField($field); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } + + protected function fetch($data, $params = array(), $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->db->find($this->table(), $data)->fetch(); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } + + protected function fetchAll($data, $params = array(), $cache_key = null) + { + if (!$cache_key || !$result = $cache_key->get()) { + $result = $this->db->find($this->table(), $data)->fetchAll(); + if ($cache_key) { + $cache_key->set($result); + } + } + return $result; + } +} \ No newline at end of file From 7e8fe0f94df29399ec5eb0926cb5d7250db0cb9a Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 17 Nov 2011 15:12:47 +0400 Subject: [PATCH 11/25] modified model classes with tests --- model/MongoDbCommand.php | 11 ++++++- model/MongoDriver.php | 5 ++-- model/MongoModel.php | 19 ++---------- model/MongoStatement.php | 10 +++++++ tests/model/MongoDbCommandTest.php | 28 ++++++++++++++++- tests/model/MongoDriverTest.php | 22 +++++++++++++- tests/model/MongoStatementTest.php | 39 +++++++++++++++++++++--- tests/model/MyDbDriver.php | 60 +++++++++++++++++++++++++++++++++++++ tests/model/MySQLiStatementTest.php | 43 ++++++++++++++++++++++++++ tests/model/SqlModelTest.php | 60 +------------------------------------ 10 files changed, 212 insertions(+), 85 deletions(-) create mode 100644 tests/model/MyDbDriver.php diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 0af1575..288f32c 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -92,9 +92,13 @@ class InsertMongoCommand extends MongoDbCommand protected $safe = true; + protected $insertId = false; + protected function concreteExecute() { - return $this->collection->insert($this->data, array('safe' => $this->safe)); + $result = $this->collection->insert($this->data, array('safe' => $this->safe)); + $this->insertId = $this->data['_id']; + return $result; } protected function checkParams() @@ -105,6 +109,11 @@ class InsertMongoCommand extends MongoDbCommand return false; } } + + public function getInsertId() + { + return $this->insertId; + } } class UpdateMongoCommand extends MongoDbCommand diff --git a/model/MongoDriver.php b/model/MongoDriver.php index 4d1219b..76efee2 100644 --- a/model/MongoDriver.php +++ b/model/MongoDriver.php @@ -55,8 +55,9 @@ class MongoDriver extends NoSqlDbDriver 'data' => $data, 'safe' => $safe ); - - return $this->query($command, $params)->affectedRows(); + $result = $this->query($command, $params); + $this->last_insert_id = $result->getInsertId(); + return $result->affectedRows(); } public function update($collection, $data, $condition = array(), $multiple = true, $upsert = false, $safe = true) diff --git a/model/MongoModel.php b/model/MongoModel.php index 02fc56e..6b050bb 100644 --- a/model/MongoModel.php +++ b/model/MongoModel.php @@ -13,26 +13,11 @@ abstract class MongoModel extends Model { - public function find($condition) + public function find($condition = array()) { return $this->db->find($this->table(), $condition); } - protected function order(MongoStatement $cursor, $sort = array()) - { - return $cursor->sort($sort); - } - - protected function skip(MongoStatement $cursor, $skip = 0) - { - return $cursor->skip($skip); - } - - protected function limit(MongoStatement $cursor, $limit = 0) - { - return $cursor->limit($limit); - } - public function get($id) { return $this->db->get($this->table(), array('_id' => $id))->fetch(); @@ -40,7 +25,7 @@ abstract class MongoModel extends Model public function delete($id) { - return $this->db->delete($this->table(), array('id' => $id)); + return $this->db->delete($this->table(), array('_id' => $id)); } protected function fetchField($data, $params = array(), $field, $cache_key = null) diff --git a/model/MongoStatement.php b/model/MongoStatement.php index 8b86913..c3b5f13 100644 --- a/model/MongoStatement.php +++ b/model/MongoStatement.php @@ -14,6 +14,8 @@ class MongoStatement extends DbStatement { + protected $insertId = false; + public function order($sort = array()) { if ($this->result instanceof MongoCursor) { @@ -133,6 +135,9 @@ class MongoStatement extends DbStatement if ($result instanceof MongoCursor || is_array($result)) { $this->result = $result; } + if($request instanceof InsertMongoCommand) { + $this->insertId = $request->getInsertId(); + } return true; } else { throw new Exception('No connection to MongoDB server.'); @@ -148,4 +153,9 @@ class MongoStatement extends DbStatement { return $this->request; } + + public function getInsertId() + { + return $this->insertId; + } } \ No newline at end of file diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index c540240..a1cc437 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -65,6 +65,28 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->bindParam('condition', array('name' => 'bread'))->bindParam('fields', array()); $result = $cmd->execute(); $this->assertEquals(0, $result->count()); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); + $cmd + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $cmd->execute(); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); + $cmd + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $cmd->execute(); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $this->assertEquals(2, $cmd->execute()->count()); + + $cmd + ->bindParam('condition', array('name' => 'insert')) + ->bindParam('fields', array()) + ->bindParam('multiple', false); + + $result = $cmd->execute(); + $this->assertEquals('insert', $result['name']); } /** @@ -81,10 +103,15 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase public function testInsertCommand() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); + $cmd ->bindParam('data', array('name' => 'insert')) ->bindParam('safe', true); + + $this->assertFalse($cmd->getInsertId()); + $this->assertArrayHasKey('n', $cmd->execute()); + $this->assertNotEmpty($cmd->getInsertId()); $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); @@ -172,5 +199,4 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); $cmd->execute(); } - } diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index 5bbd441..da61ebc 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -219,12 +219,32 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $mongo = new MongoDriver($this->conf); $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); $this->assertEquals(0, $mongo->insert('items', array('name' => 'bread'))); - //$this->assertNotEmpty($mongo->getInsertId()); + $this->assertNotEmpty($mongo->getInsertId()); $this->assertEquals(2, $mongo->find('items', array('name' => 'bread'))->numRows()); $this->assertEquals(0, $mongo->insert('items', array('name' => 'meat', 'weight' => 230))); $this->assertEquals(230, $mongo->get('items', array('name' => 'meat'))->fetch()->weight); } + public function testGetInsertId() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $mongo = new MongoDriver($this->conf); + $this->assertEquals(0, $mongo->getInsertId()); + $this->assertEquals(1, $mongo->find('items', array('name' => 'bread'))->numRows()); + $this->assertEquals(0, $mongo->insert('items', array('name' => 'bread'))); + $this->assertEquals(2, $mongo->find('items', array('name' => 'bread'))->numRows()); + $id1 = $mongo->getInsertId(); + $this->assertNotEmpty($id1); + $this->assertEquals(0, $mongo->insert('items', array('name' => 'bread'))); + $id2 = $mongo->getInsertId(); + $this->assertNotEmpty($id2); + $this->assertNotEquals($id1, $id2); + $this->assertEquals(3, $mongo->find('items', array('name' => 'bread'))->numRows()); + } + /** * @runInSeparateProcess */ diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php index c7e10c1..81eba4a 100644 --- a/tests/model/MongoStatementTest.php +++ b/tests/model/MongoStatementTest.php @@ -48,7 +48,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->getMock(); $this->request = $this->getMockBuilder('MongoDbCommandMock') ->disableOriginalConstructor() - ->setMethods(array('execute', 'bindParam')) + ->setMethods(array('execute', 'bindParam', 'getInsertId')) ->getMock(); $this->stmt = new MongoStatement($this->driver, $this->request); } @@ -84,13 +84,42 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase } $this->setDriverGetConnectionMethod(); $this->request - ->expects($this->any()) + ->expects($this->once()) ->method('execute') ->will($this->returnValue(array('n' => 20, 'ok' => 1))); $this->stmt->execute(); $this->assertEquals(20, $this->stmt->affectedRows()); } + /** + * @runInSeparateProcess + */ + public function testGetInsertId() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->setDriverGetConnectionMethod(); + + $this->request = $this->getMockBuilder('InsertMongoCommand') + ->disableOriginalConstructor() + ->setMethods(array('execute', 'bindParam', 'getInsertId')) + ->getMock(); + + $this->request + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue(array('n' => 20, 'ok' => 1))); + $this->request + ->expects($this->once()) + ->method('getInsertId') + ->will($this->returnValue('4b0rrs')); + + $this->stmt = new MongoStatement($this->driver, $this->request); + + $this->stmt->execute(); + $this->assertEquals('4b0rrs', $this->stmt->getInsertId()); + } /** * @runInSeparateProcess @@ -286,6 +315,8 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase $this->stmt->execute(); $this->assertInstanceOf('MongoStatement', $this->stmt->order(array('id' => 1))); + $this->assertInstanceOf('MongoStatement', $this->stmt->limit(10)); + $this->assertInstanceOf('MongoStatement', $this->stmt->skip(1)); $this->stmt->fetch(); $this->stmt->fetch(); @@ -368,7 +399,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->setMethods(array('count')) ->disableOriginalConstructor() ->getMock(); - + $resultMock ->expects($this->any()) ->method('count') @@ -381,7 +412,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase return $this; } - + public function setRequestForFetch() { $resultMock = $this->getMockBuilder('MongoCursor') diff --git a/tests/model/MyDbDriver.php b/tests/model/MyDbDriver.php new file mode 100644 index 0000000..c11fb17 --- /dev/null +++ b/tests/model/MyDbDriver.php @@ -0,0 +1,60 @@ + 'MockDbDriver', 'hostname' => 'somehost', 'database' => 'db', 'username' => 'test', 'password' => '1234'); + return new MockDbDriver($conf); + } + + public function execute() + { + return true; + } + + public function fetch() + { + return true; + } + + public function fetchField($field) + { + return $field; + } + + public function fetchAll() + { + return true; + } +} \ No newline at end of file diff --git a/tests/model/MySQLiStatementTest.php b/tests/model/MySQLiStatementTest.php index dbdf27f..efbaac6 100644 --- a/tests/model/MySQLiStatementTest.php +++ b/tests/model/MySQLiStatementTest.php @@ -175,6 +175,49 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess */ + public function testFetchPairs() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $resultMock = $this->getMockBuilder('mysqli_result') + ->disableOriginalConstructor() + ->setMethods(array('fetch_array', 'close')) + ->setMockClassName('Mysqli_Result_Mock') + ->getMock(); + $resultMock + ->expects($this->at(0)) + ->method('fetch_array') + ->will($this->returnValue(array('pair', 'value'))); + $resultMock + ->expects($this->at(1)) + ->method('fetch_array') + ->will($this->returnValue(false)); + $resultMock + ->expects($this->any()) + ->method('close') + ->will($this->returnValue(TRUE)); + + $mysqliMock = $this->getMock('MysqliDrvr', array('query')); + $mysqliMock + ->expects($this->any()) + ->method('query') + ->with($this->anything()) + ->will($this->returnValue($resultMock)); + + $this->driver + ->expects($this->any()) + ->method('getConnection') + ->will($this->returnValue($mysqliMock)); + + $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); + $this->assertEquals(array('pair' => 'value'), $this->stmt->fetchPairs()); + } + + /** + * @runInSeparateProcess + */ public function testFetchWithDebug() { if (!defined('DEBUG')) { diff --git a/tests/model/SqlModelTest.php b/tests/model/SqlModelTest.php index 18de4ce..429516b 100644 --- a/tests/model/SqlModelTest.php +++ b/tests/model/SqlModelTest.php @@ -16,6 +16,7 @@ require_once dirname(__FILE__) . '/../../cache/Cacher.php'; require_once dirname(__FILE__) . '/../../model/DbExpr.php'; require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/MyDbDriver.php'; require_once dirname(__FILE__) . '/../../model/Model.php'; require_once dirname(__FILE__) . '/../../model/SqlModel.php'; @@ -246,63 +247,4 @@ class SqlModelTest extends PHPUnit_Framework_TestCase return $className; } } -} - -abstract class MyDbDriver extends DbDriver -{ - public function getInsertId($table = null, $key = null) - { - return true; - } - - public function quoteIdentifier($param) - { - return $param; - } - - public function quote($param) - { - return $param; - } - - public function insert($table, $bind, $on_duplicate = array()) - { - return $table; - } - - public function update($table, $bind, $where = '') - { - return $table; - } - - public function delete($table, $where = '') - { - return $table; - } - - public function query($sql, $params = array()) - { - $conf = array('driver' => 'MockDbDriver', 'hostname' => 'localhost', 'database' => 'db', 'username' => 'test', 'password' => '1234'); - return new MockDbDriver($conf); - } - - public function execute() - { - return true; - } - - public function fetch() - { - return true; - } - - public function fetchField($field) - { - return $field; - } - - public function fetchAll() - { - return true; - } } \ No newline at end of file From 7094cd5073668c08c00ba5ddcd20922402ad52f1 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 17 Nov 2011 15:13:11 +0400 Subject: [PATCH 12/25] MongoModel class tested --- tests/model/MongoModelTest.php | 203 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 tests/model/MongoModelTest.php diff --git a/tests/model/MongoModelTest.php b/tests/model/MongoModelTest.php new file mode 100644 index 0000000..00403a0 --- /dev/null +++ b/tests/model/MongoModelTest.php @@ -0,0 +1,203 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage UnitTests + * @since 2011-11-7 + * + * Unit tests for MongoModel class + */ + +require_once dirname(__FILE__) . '/../../Registry.php'; +require_once dirname(__FILE__) . '/../../Config.php'; +require_once dirname(__FILE__) . '/../../cache/Cacher.php'; +require_once dirname(__FILE__) . '/../../model/DbExpr.php'; +require_once dirname(__FILE__) . '/../../model/Db.php'; +require_once dirname(__FILE__) . '/../../model/MongoDbCommand.php'; +require_once dirname(__FILE__) . '/../../model/DbStatement.php'; +require_once dirname(__FILE__) . '/../../model/MongoStatement.php'; +require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php'; +require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; +require_once dirname(__FILE__) . '/../../model/Model.php'; +require_once dirname(__FILE__) . '/../../model/MongoModel.php'; + +class MongoModelTest extends PHPUnit_Framework_TestCase +{ + + private $model; + + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + $this->setPreserveGlobalState(false); + return parent::run($result); + } + + public function setUp() + { + $conf = array('default' => array('driver' => 'MongoDriver', 'hostname' => 'localhost', 'database' => 'test', 'username' => 'test', 'password' => '1234', 'port' => 27017)); + + $this->dbSetUp($conf); + + Config::set('Db', $conf); + if (!class_exists('MockModel')) { + $this->model = $this->getMockForAbstractClass('MongoModel', array(), 'MongoMockModel'); + } else { + $this->model = new MongoMockModel(); + } + set_new_overload(array($this, 'newCallback')); + } + + public function testModel() + { + $this->assertInstanceOf('MongoMockModel', $this->model); + } + + /** + * @runInSeparateProcess + */ + public function testFind() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $result = $this->model->find(); + $this->assertInstanceOf('MongoStatement', $result); + $this->assertEquals('milk', $result->limit(2)->order(array('name' => -1))->fetch()->name); + $this->assertEquals('fish', $result->fetch()->name); + } + + /** + * @runInSeparateProcess + */ + public function testGet() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $result = $this->model->find()->limit(1)->order(array('name' => 1)); + $result = $result->fetch(); + $this->assertEquals('bread', $result->name); + $id = $result->_id; + $this->assertEquals(10, $this->model->get($id)->quantity); + } + + /** + * @runInSeparateProcess + */ + public function testDelete() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $result = $this->model->find()->limit(1)->order(array('name' => 1)); + $id = $result->fetch()->_id; + $this->assertEquals(1, $this->model->delete($id)); + $this->assertFalse($this->model->find(array('name' => 'bread'))->fetch()); + } + + /** + * @runInSeparateProcess + */ + public function testFetch() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $mock = $this->getMock('CacheKey', array('set', 'get')); + $mock->expects($this->exactly(3)) + ->method('set') + ->will($this->returnValue(true)); + $mock->expects($this->exactly(3)) + ->method('get') + ->will($this->returnValue(false)); + + $model = new ReflectionClass('MongoModel'); + $method = $model->getMethod('fetchField'); + $method->setAccessible(true); + + $result = $method->invoke($this->model, array('name' => 'milk'), array(), 'quantity', $mock); + $this->assertEquals(1, $result); + + $model = new ReflectionClass('MongoModel'); + $method = $model->getMethod('fetch'); + $method->setAccessible(true); + + $result = $method->invoke($this->model, array('name' => 'bread'), array(), $mock); + $this->assertEquals('bread', $result->name); + + $model = new ReflectionClass('MongoModel'); + $method = $model->getMethod('fetchAll'); + $method->setAccessible(true); + + $result = $method->invoke($this->model, array('name' => 'eggs'), array(), $mock); + $this->assertEquals(2, count($result)); + $this->assertEquals('eggs', $result[0]->name); + } + + public function tearDown() + { + $conf = array('driver' => 'MongoDriver', 'hostname' => 'localhost', 'database' => 'test', 'username' => 'test', 'password' => '1234', 'port' => 27017); + + + $connection = new Mongo('mongodb://' . $conf['hostname'] . ':' . $conf['port']); + $db = $connection->selectDB($conf['database']); + $db->authenticate($conf['username'], $conf['password']); + $collection = 'mongomock'; + $db->dropCollection($collection); + } + + protected function newCallback($className) + { + switch ($className) { + case 'CacheKey': + return 'MockCacheKey'; + default: + return $className; + } + } + + public function dbSetUp($conf) + { + $data = array( + array( + 'name' => 'bread', + 'price' => 3.2, + 'quantity' => 10 + ), + array( + 'name' => 'eggs', + 'price' => 2.1, + 'quantity' => 20 + ), + array( + 'name' => 'fish', + 'price' => 13.2, + 'quantity' => 2 + ), + array( + 'name' => 'milk', + 'price' => 3.8, + 'quantity' => 1 + ), + array( + 'name' => 'eggs', + 'price' => 2.3, + 'quantity' => 5 + ) + ); + $connection = new Mongo('mongodb://' . $conf['default']['hostname'] . ':' . $conf['default']['port']); + $db = $connection->selectDB($conf['default']['database']); + $db->authenticate($conf['default']['username'], $conf['default']['password']); + $collection = 'mongomock'; + $db->dropCollection($collection); + $collection = $db->selectCollection($collection); + foreach($data as $document) { + $collection->insert($document); + } + } + +} From 2f3a0d170fca694ed62589d01329f274a360b404 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 17 Nov 2011 15:13:53 +0400 Subject: [PATCH 13/25] Session class tested and modified for new Model hierarchy --- session/Session.model.php | 2 +- tests/session/SessionModelTest.php | 110 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/session/SessionModelTest.php diff --git a/session/Session.model.php b/session/Session.model.php index 22d57cd..769be5e 100644 --- a/session/Session.model.php +++ b/session/Session.model.php @@ -9,7 +9,7 @@ * @filesource $URL$ */ -class SessionModel extends Model +class SessionModel extends SqlModel { protected $life_time; diff --git a/tests/session/SessionModelTest.php b/tests/session/SessionModelTest.php new file mode 100644 index 0000000..f608d04 --- /dev/null +++ b/tests/session/SessionModelTest.php @@ -0,0 +1,110 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage UnitTests + * @since 2011-11-15 + * + * Unit tests for SessionModel class + */ + +require_once dirname(__FILE__) . '/../../Registry.php'; +require_once dirname(__FILE__) . '/../../Config.php'; +require_once dirname(__FILE__) . '/../../classes/Env.class.php'; +require_once dirname(__FILE__) . '/../../model/Db.php'; +require_once dirname(__FILE__) . '/../../model/DbDriver.php'; +require_once dirname(__FILE__) . '/../model/MyDbDriver.php'; +require_once dirname(__FILE__) . '/../../model/Model.php'; +require_once dirname(__FILE__) . '/../../model/SqlModel.php'; +require_once dirname(__FILE__) . '/../../session/Session.model.php'; + +class SessionModelTest extends PHPUnit_Framework_TestCase +{ + + protected $model; + + public function setUp() + { + $conf = array('default' => array('driver' => 'MockDbDriver', 'hostname' => 'somehost', 'database' => 'db', 'username' => 'test', 'password' => '1234')); + if (!class_exists('MockDbDriver')) { + $this->getMockForAbstractClass('MyDbDriver', array($conf), 'MockDbDriver', false); + } + if (!class_exists('MockDbExpr')) { + $this->getMock('DbExpr', array(), array(), 'MockDbExpr', false); + } + + Config::set('Db', $conf); + + set_new_overload(array($this, 'newCallback')); + } + + public function testOpen() + { + $this->model = new SessionModel(); + $this->assertTrue($this->model->open('path', 'name')); + } + + public function testClose() + { + $this->model = new SessionModel(); + $this->assertTrue($this->model->close()); + } + + public function testRead() + { + $this->model = new SessionModel(); + $this->assertEquals('data', $this->model->read(1)); + } + + public function testWrite() + { + $this->model = new SessionModel(); + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $this->assertEmpty(Env::Server('HTTP_X_FORWARDED_FOR')); + + $this->assertTrue($this->model->write(2, 'user|.s:20;id=2;id=2')); + } + + public function testDestroy() + { + $this->model = new SessionModel(); + $this->assertTrue($this->model->destroy(2)); + } + public function testGc() + { + $this->model = new SessionModel(); + $this->assertTrue($this->model->gc(2000)); + } + + public function testDestroyByUserId() + { + $this->model = new SessionModel(); + $this->assertEquals('session', $this->model->destroyByUserId(12)); + } + + public function tearDown() + { + Config::getInstance()->offsetUnset('Db'); + $config = new ReflectionClass('Db'); + $registry = $config->getProperty('connections'); + $registry->setAccessible(true); + $registry->setValue('Db', array()); + unset_new_overload(); + } + + protected function newCallback($className) + { + switch ($className) { + case 'DbExpr': + return 'MockDbExpr'; + case 'MockDbDriver': + return 'MockDbDriver'; + case 'CacheKey': + return 'MockCacheKey'; + default: + return $className; + } + } +} From d360e7c0237ab04f59a1962d375b0a7df5fce38f Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 17 Nov 2011 15:48:41 +0400 Subject: [PATCH 14/25] added TODO for class methods --- model/MongoModel.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/model/MongoModel.php b/model/MongoModel.php index 6b050bb..8063862 100644 --- a/model/MongoModel.php +++ b/model/MongoModel.php @@ -28,6 +28,9 @@ abstract class MongoModel extends Model return $this->db->delete($this->table(), array('_id' => $id)); } + /** + * @TODO: check for limits (if just one record needed) + */ protected function fetchField($data, $params = array(), $field, $cache_key = null) { if (!$cache_key || !$result = $cache_key->get()) { @@ -39,6 +42,9 @@ abstract class MongoModel extends Model return $result; } + /** + * @TODO: check for limits (if just one record needed) + */ protected function fetch($data, $params = array(), $cache_key = null) { if (!$cache_key || !$result = $cache_key->get()) { From 8a2e814438a2a4fd7289ec1afd7ca9c932d42c37 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Fri, 18 Nov 2011 13:59:36 +0400 Subject: [PATCH 15/25] SqlDbDriver::insert - $on_duplicate parameter recovered --- model/SqlDbDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/SqlDbDriver.php b/model/SqlDbDriver.php index de78409..fa86668 100644 --- a/model/SqlDbDriver.php +++ b/model/SqlDbDriver.php @@ -41,7 +41,7 @@ abstract class SqlDbDriver extends DbDriver * @param mixed $on_duplicate * @return int Affected rows count */ - public function insert($table, $data) + public function insert($table, $data, $on_duplicate = array()) { $columns = array(); foreach ($data as $col => $val) { From 54b991314d93a2e6bf789921b49536e6d7fe1da9 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 22 Nov 2011 14:18:42 +0400 Subject: [PATCH 16/25] removed Model::order() to SqlModel --- model/Model.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/model/Model.php b/model/Model.php index 10e97f8..fd4fd01 100644 --- a/model/Model.php +++ b/model/Model.php @@ -152,15 +152,6 @@ abstract class Model abstract public function delete($id); /** - * Creates order sql string - * - * @param array $params - * @param array $sortable - * @return string - */ - abstract protected function order($params, $sortable = array('id')); - - /** * @param string $sql * @param array $params * @param string $field From a87845980d000bc0f57b9079f2e91e868212ed2e Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 22 Nov 2011 16:29:00 +0400 Subject: [PATCH 17/25] added methods to MongoDriver for custom commands, findAndModify, count --- model/MongoDbCommand.php | 26 ++++++++++++++++++++- model/MongoDriver.php | 31 +++++++++++++++++++++++++ model/MongoModel.php | 5 ++++ model/MongoStatement.php | 18 +++++++++++---- tests/model/MongoDbCommandTest.php | 24 ++++++++++++++++++- tests/model/MongoDriverTest.php | 47 ++++++++++++++++++++++++++++++++++++++ tests/model/MongoModelTest.php | 12 ++++++++++ 7 files changed, 156 insertions(+), 7 deletions(-) diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 288f32c..7c9328d 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -18,6 +18,8 @@ class MongoCommandBuilder const REMOVE = 'Remove'; + const COMMAND = 'Command'; + static public function factory($type, $collection = null) { $class = ucfirst($type) . 'MongoCommand'; @@ -167,4 +169,26 @@ class RemoveMongoCommand extends MongoDbCommand } } - + class CommandMongoCommand extends MongoDbCommand + { + protected $command; + + protected function concreteExecute() + { + $db = $this->collection->db; + if($db instanceof MongoDB) { + return $db->command($this->command); + } else { + return false; + } + } + + protected function checkParams() + { + if (isset($this->collection) && isset($this->command)) { + return true; + } else { + return false; + } + } + } diff --git a/model/MongoDriver.php b/model/MongoDriver.php index 76efee2..8efd68b 100644 --- a/model/MongoDriver.php +++ b/model/MongoDriver.php @@ -26,6 +26,11 @@ class MongoDriver extends NoSqlDbDriver return $this->connection->selectCollection($this->db, $name); } + public function count($collection, $query = array(), $limit = 0, $skip = 0) + { + return $this->getCollection($collection)->count($query, $limit, $skip); + } + public function find($collection, $condition = array(), $fields = array()) { $command = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->getCollection($collection)); @@ -48,6 +53,30 @@ class MongoDriver extends NoSqlDbDriver return $this->query($command, $params); } + public function findAndModify($collection, $query, $update, $sort = array(), $field = array(), $upsert = false, $new = false, $remove = false) + { + $command = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $this->getCollection($collection)); + $cmd = array( + 'findAndModify' => $collection, + 'query' => $query, + 'update' => $update, + 'sort' => $sort, + 'fields' => $field, + 'new' => $new, + 'upsert' => $upsert, + 'remove' => $remove + ); + $params = array('command' => $cmd); + return $this->query($command, $params); + } + + public function command($collection, $cmd) + { + $command = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $this->getCollection($collection)); + $params = array('command' => $cmd); + return $this->query($command, $params); + } + public function insert($collection, $data, $safe = true) { $command = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->getCollection($collection)); @@ -85,6 +114,8 @@ class MongoDriver extends NoSqlDbDriver return $this->query($command, $params)->affectedRows(); } + + /** * @param mixed $request * @return DbStatement diff --git a/model/MongoModel.php b/model/MongoModel.php index 8063862..8eb1814 100644 --- a/model/MongoModel.php +++ b/model/MongoModel.php @@ -13,6 +13,11 @@ abstract class MongoModel extends Model { + public function count($query = array(), $limit = 0, $skip = 0) + { + return $this->db->count($this->table(), $query, $limit, $skip); + } + public function find($condition = array()) { return $this->db->find($this->table(), $condition); diff --git a/model/MongoStatement.php b/model/MongoStatement.php index c3b5f13..399cb67 100644 --- a/model/MongoStatement.php +++ b/model/MongoStatement.php @@ -16,7 +16,7 @@ class MongoStatement extends DbStatement protected $insertId = false; - public function order($sort = array()) + public function order($sort = array()) { if ($this->result instanceof MongoCursor) { $this->result->sort($sort); @@ -96,8 +96,10 @@ class MongoStatement extends DbStatement public function affectedRows() { if (is_array($this->result)) { - if (isset($this->result['ok']) && $this->result['ok'] == 1 && isset($this->result['n'])) { - return $this->result['n']; + if (isset($this->result['ok']) && $this->result['ok'] == 1) { + if (isset($this->result['n'])) { + return $this->result['n']; + } } else { return false; } @@ -115,7 +117,7 @@ class MongoStatement extends DbStatement } /** - * @param MongoDbComand $request + * @param MongoDbCommand $request * @return bool */ protected function driverExecute($request) @@ -134,8 +136,14 @@ class MongoStatement extends DbStatement } if ($result instanceof MongoCursor || is_array($result)) { $this->result = $result; + if (is_array($result) && isset($result['value'])) { + $this->result = $result['value']; + } + if (is_array($result) && isset($result['values'])) { + $this->result = $result['values']; + } } - if($request instanceof InsertMongoCommand) { + if ($request instanceof InsertMongoCommand) { $this->insertId = $request->getInsertId(); } return true; diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index a1cc437..75a18d6 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -84,7 +84,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase ->bindParam('condition', array('name' => 'insert')) ->bindParam('fields', array()) ->bindParam('multiple', false); - + $result = $cmd->execute(); $this->assertEquals('insert', $result['name']); } @@ -199,4 +199,26 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); $cmd->execute(); } + + /** + * @expectedException Exception + * @expectedExceptionMessage CommandMongoCommand error. Bind all required params first. + */ + public function testCommandCommandNotAllParamsBinded() + { + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $this->collection); + $cmd->execute(); + } + + public function testCommandCommandNotMongoDb() + { + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); + $cmd->bindParam('command', array()); + $this->assertFalse($cmd->execute()); + } +} + +class CollectionMock +{ + public $db = array(); } diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index da61ebc..edcedd4 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -176,6 +176,20 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess */ + public function testCount() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); + $this->assertEquals(5, $mongo->count('items')); + $this->assertEquals(2, $mongo->count('items', array('name' => 'eggs'))); + $this->assertEquals(1, $mongo->count('items', array('name' => 'eggs'), 1)); + } + + /** + * @runInSeparateProcess + */ public function testGet() { if (!defined('DEBUG')) { @@ -279,4 +293,37 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertEquals(1, $mongo->update('items', array('$set' => array('price' => 200, 'date' => 'today')), array('name' => 'ball'), true, true)); $this->assertEquals('today', $mongo->get('items', array('name' => 'ball'))->fetch()->date); } + + /** + * @runInSeparateProcess + */ + public function testFindAndModify() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $mongo = new MongoDriver($this->conf); + + $this->assertEquals(10, $mongo->get('items', array('name' => 'bread'))->fetch()->quantity); + $result = $mongo->findAndModify('items', array('name' => 'bread'), array('$set' => array('quantity' => 20))); + $this->assertEquals(10, $result->fetch()->quantity); + $this->assertEquals(20, $mongo->get('items', array('name' => 'bread'))->fetch()->quantity); + } + + /** + * @runInSeparateProcess + */ + public function testCommand() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $mongo = new MongoDriver($this->conf); + $result = $mongo->command('items', array('distinct' =>'items', 'key' => 'name')); + $this->assertEquals(4, count($result->fetch(DB::FETCH_ASSOC))); + + + + } } \ No newline at end of file diff --git a/tests/model/MongoModelTest.php b/tests/model/MongoModelTest.php index 00403a0..695b788 100644 --- a/tests/model/MongoModelTest.php +++ b/tests/model/MongoModelTest.php @@ -101,6 +101,18 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess */ + public function testCount() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + $this->assertEquals(5, $this->model->count()); + $this->assertEquals(2, $this->model->count(array('name' => 'eggs'))); + } + + /** + * @runInSeparateProcess + */ public function testFetch() { if (!defined('DEBUG')) { From 8464c8edac07905198f23b127598affcf24dce98 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 24 Nov 2011 19:22:12 +0400 Subject: [PATCH 18/25] modified Autoloader for folders exclusion --- Load.php | 34 +++++++++++++++++++++++++--------- util/AutoloadBuilder.php | 20 +++++++++++++++++++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/Load.php b/Load.php index 432cd2c..f2085ad 100644 --- a/Load.php +++ b/Load.php @@ -11,10 +11,27 @@ class Load { - + static protected $file; + static protected $autoload; + static protected $exclude = array('/.git', '/lib/core/tests', '/lib/core/.git'); + + /** + * Add exclude path for autoload. Should be called before setAutoloadFrom + * @static + * @param array $exclude list of relative path (from project root) + * @return void + */ + static public function setExclude($exclude = array()) + { + if(!is_array($exclude)) { + $exclude = array($exclude); + } + self::$exclude = array_merge(self::$exclude, $exclude); + } + static public function setAutoloadFrom($file) { self::$file = $file; @@ -23,7 +40,7 @@ class Load } self::$autoload = require(self::$file); } - + static public function autoload($class) { if (isset(self::$autoload[$class])) { @@ -35,16 +52,16 @@ class Load self::buildAutoload(); } if (isset(self::$autoload[$class])) { - require(PATH . self::$autoload[$class]); + require(PATH . self::$autoload[$class]); } } } - + static public function getFilePath($class) { return self::$autoload[$class]; } - + static protected function buildAutoload() { ignore_user_abort(true); @@ -52,12 +69,11 @@ class Load if (!file_exists($dir) && !mkdir($dir)) { trigger_error('Can\'t create directory: "' . $dir . '"', E_USER_ERROR); } - + $scan = array(PATH . '/' . APP . '/src', PATH . '/lib'); - require_once(PATH . '/lib/core/util/AutoloadBuilder.php'); - - $builder = new AutoloadBuilder(self::$file, $scan); + + $builder = new AutoloadBuilder(self::$file, $scan, self::$exclude); $builder->build(); ignore_user_abort(false); } diff --git a/util/AutoloadBuilder.php b/util/AutoloadBuilder.php index 4e4ff74..3f64ef6 100644 --- a/util/AutoloadBuilder.php +++ b/util/AutoloadBuilder.php @@ -20,14 +20,17 @@ class AutoloadBuilder protected $dirs; + protected $exclude; + protected $handler; protected $regex = '/(class|interface) ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/'; - public function __construct($autoload, $dirs = array()) + public function __construct($autoload, $dirs = array(), $exclude = array()) { $this->autoload = $autoload; $this->dirs = $dirs; + $this->exclude = $exclude; } public function build() @@ -42,6 +45,10 @@ class AutoloadBuilder ); foreach ($iterator as $file) { + + if($this->isExcluded($file->getRealPath())) { + continue; + } // skip non php files $e = explode('.', $file->getFileName()); if ((end($e) !== 'php') || $this->rightSubstr($file->getFileName(), 8) == 'Test.php') { @@ -68,6 +75,17 @@ class AutoloadBuilder $this->closeAutoload(); } + protected function isExcluded($file) + { + foreach ($this->exclude as $dir) { + if (stripos($file, PATH . $dir) === 0) { + return true; + } + } + return false; + } + + protected function openAutoload() { $this->write(" Date: Thu, 24 Nov 2011 19:22:49 +0400 Subject: [PATCH 19/25] replaced \r\n with PHP_EOL in CliLogger --- logger/CliLogger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger/CliLogger.php b/logger/CliLogger.php index 121cda1..cfc8e5e 100644 --- a/logger/CliLogger.php +++ b/logger/CliLogger.php @@ -13,7 +13,7 @@ class CliLogger extends Logger protected function concreteLog($message) { // Заменяем окончания строк на их символы - $message = str_replace(array("\r", "\n"), array('\r', '\n'), $message); + $message = str_replace(array("\r\n"), array(PHP_EOL), $message); $out = microtime(true) . " \t: " . $this->pid . trim($message) . PHP_EOL; print($out); } From a02c3992bda2a94e2babd1636ed997f543cf30f6 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Thu, 24 Nov 2011 19:24:05 +0400 Subject: [PATCH 20/25] added __toString() to MongoCommand - need to implement in subclasses --- model/MongoDbCommand.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 7c9328d..d348d4c 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -59,6 +59,14 @@ abstract class MongoDbCommand abstract protected function concreteExecute(); abstract protected function checkParams(); + + /** + * @TODO: implement method in subclasses for Profiler + */ + public function __toString() + { + return get_called_class(); + } } class FindMongoCommand extends MongoDbCommand From 71cc211c4f675027ada7a30493a4444a8b66087b Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 6 Dec 2011 14:05:18 +0400 Subject: [PATCH 21/25] modified classes for GeneralException thrown --- model/MongoDbCommand.php | 2 +- model/MongoModel.php | 8 ++++++++ model/MongoStatement.php | 12 ++++++------ model/MySQLiStatement.php | 4 ++-- tests/model/MongoDbCommandTest.php | 11 ++++++----- tests/model/MongoDriverTest.php | 3 ++- tests/model/MongoModelTest.php | 14 ++++++++++++++ tests/model/MongoStatementTest.php | 13 +++++++------ tests/model/MySQLiStatementTest.php | 10 +++++----- 9 files changed, 51 insertions(+), 26 deletions(-) diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index d348d4c..88a849d 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -38,7 +38,7 @@ abstract class MongoDbCommand if ($this->checkParams()) { return $this->concreteExecute(); } else { - throw new Exception(get_called_class() . ' error. Bind all required params first.'); + throw new GeneralException(get_called_class() . ' error. Bind all required params first.'); } } diff --git a/model/MongoModel.php b/model/MongoModel.php index 8eb1814..cb433dc 100644 --- a/model/MongoModel.php +++ b/model/MongoModel.php @@ -34,6 +34,14 @@ abstract class MongoModel extends Model } /** + * @TODO: test method + */ + public function deleteAll($query = array()) + { + $this->db->delete($this->table(), $query); + } + + /** * @TODO: check for limits (if just one record needed) */ protected function fetchField($data, $params = array(), $field, $cache_key = null) diff --git a/model/MongoStatement.php b/model/MongoStatement.php index 399cb67..9060dee 100644 --- a/model/MongoStatement.php +++ b/model/MongoStatement.php @@ -22,7 +22,7 @@ class MongoStatement extends DbStatement $this->result->sort($sort); return $this; } else { - throw new Exception('MongoStatement error. Impossible order results of opened cursor.'); + throw new GeneralException('MongoStatement error. Impossible order results of opened cursor.'); } } @@ -32,7 +32,7 @@ class MongoStatement extends DbStatement $this->result->skip($skip); return $this; } else { - throw new Exception('MongoStatement error. Impossible skip results of opened cursor.'); + throw new GeneralException('MongoStatement error. Impossible skip results of opened cursor.'); } } @@ -42,7 +42,7 @@ class MongoStatement extends DbStatement $this->result->limit($limit); return $this; } else { - throw new Exception('MongoStatement error. Impossible limit results of opened cursor.'); + throw new GeneralException('MongoStatement error. Impossible limit results of opened cursor.'); } } @@ -65,7 +65,7 @@ class MongoStatement extends DbStatement } break; default: - throw new Exception('Invalid fetch mode "' . $style . '" specified'); + throw new GeneralException('Invalid fetch mode "' . $style . '" specified'); } return $row; } @@ -132,7 +132,7 @@ class MongoStatement extends DbStatement $result = $request->execute(); } if ($result === false) { - throw new Exception('MongoDB request error.'); + throw new GeneralException('MongoDB request error.'); } if ($result instanceof MongoCursor || is_array($result)) { $this->result = $result; @@ -148,7 +148,7 @@ class MongoStatement extends DbStatement } return true; } else { - throw new Exception('No connection to MongoDB server.'); + throw new GeneralException('No connection to MongoDB server.'); } } diff --git a/model/MySQLiStatement.php b/model/MySQLiStatement.php index 47160cc..b489899 100644 --- a/model/MySQLiStatement.php +++ b/model/MySQLiStatement.php @@ -25,10 +25,10 @@ class MySQLiStatement extends DbStatement } if (count($this->map) > 0) { if (!is_string($param) && !is_int($param)) { - throw new Exception('Placeholder must be an array or string'); + throw new GeneralException('Placeholder must be an array or string'); } if (is_object($value) && ! ($value instanceof DbExpr)) { - throw new Exception('Objects excepts DbExpr not allowed.'); + throw new GeneralException('Objects excepts DbExpr not allowed.'); } if (isset($this->map[$param])) { $this->params[$param] = &$value; diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index 75a18d6..a325379 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -14,6 +14,7 @@ require_once dirname(__FILE__) . '/../../model/DbDriver.php'; require_once dirname(__FILE__) . '/../../model/NoSqlDbDriver.php'; require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; require_once dirname(__FILE__) . '/../../model/MongoDbCommand.php'; +require_once dirname(__FILE__) . '/../../exception/GeneralException.php'; class MongoDbCommandTest extends PHPUnit_Framework_TestCase { @@ -90,7 +91,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage FindMongoCommand error. Bind all required params first. */ public function testFindCommandNotAllParamsBinded() @@ -120,7 +121,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage InsertMongoCommand error. Bind all required params first. */ public function testInsertCommandNotAllParamsBinded() @@ -155,7 +156,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage UpdateMongoCommand error. Bind all required params first. */ public function testUpdateCommandNotAllParamsBinded() @@ -191,7 +192,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage RemoveMongoCommand error. Bind all required params first. */ public function testRemoveCommandNotAllParamsBinded() @@ -201,7 +202,7 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage CommandMongoCommand error. Bind all required params first. */ public function testCommandCommandNotAllParamsBinded() diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index edcedd4..a027f1b 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -17,6 +17,7 @@ require_once dirname(__FILE__) . '/../../model/MongoDbCommand.php'; require_once dirname(__FILE__) . '/../../model/DbStatement.php'; require_once dirname(__FILE__) . '/../../model/MongoStatement.php'; require_once dirname(__FILE__) . '/../../model/MongoDriver.php'; +require_once dirname(__FILE__) . '/../../exception/GeneralException.php'; class MongoDriverTest extends PHPUnit_Framework_TestCase { @@ -72,7 +73,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage Configuration must have a "hostname". */ public function testGetConnectionNoHostname() diff --git a/tests/model/MongoModelTest.php b/tests/model/MongoModelTest.php index 695b788..cc3d8a6 100644 --- a/tests/model/MongoModelTest.php +++ b/tests/model/MongoModelTest.php @@ -101,6 +101,20 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess */ + public function testDeleteAll() + { + if (!defined('DEBUG')) { + define('DEBUG', false); + } + + $this->assertEquals(2, $this->model->count(array('name' => 'eggs'))); + $this->assertEquals(0, $this->model->deleteAll(array('name' => 'eggs'))); + $this->assertFalse($this->model->find(array('name' => 'eggs'))->fetch()); + } + + /** + * @runInSeparateProcess + */ public function testCount() { if (!defined('DEBUG')) { diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php index 81eba4a..a09a324 100644 --- a/tests/model/MongoStatementTest.php +++ b/tests/model/MongoStatementTest.php @@ -16,6 +16,7 @@ require_once dirname(__FILE__) . '/../../model/Db.php'; require_once dirname(__FILE__) . '/../../model/DbDriver.php'; require_once dirname(__FILE__) . '/../../model/DbStatement.php'; require_once dirname(__FILE__) . '/../../model/MongoStatement.php'; +require_once dirname(__FILE__) . '/../../exception/GeneralException.php'; class MongoStatementTest extends PHPUnit_Framework_TestCase { @@ -136,7 +137,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage MongoDB request error. */ public function testExecuteNoResult() @@ -154,7 +155,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage No connection to MongoDB server. */ public function testExecuteNoConnection() @@ -283,7 +284,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage Invalid fetch mode "222" specified */ public function testFetchWrongMode() @@ -324,7 +325,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage MongoStatement error. Impossible order results of opened cursor */ public function testOrderException() @@ -344,7 +345,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage MongoStatement error. Impossible skip results of opened cursor */ public function testSkipException() @@ -364,7 +365,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage MongoStatement error. Impossible limit results of opened cursor */ public function testLimitException() diff --git a/tests/model/MySQLiStatementTest.php b/tests/model/MySQLiStatementTest.php index 71b72bb..c50149d 100644 --- a/tests/model/MySQLiStatementTest.php +++ b/tests/model/MySQLiStatementTest.php @@ -63,7 +63,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage Placeholder must be an array or string * @TODO: change Exception Message 'Placeholder must be an array or string' - no arrays available */ @@ -74,7 +74,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Exception + * @expectedException GeneralException * @expectedExceptionMessage Objects excepts DbExpr not allowed. */ public function testBindParamExceptionWrongObject() @@ -101,7 +101,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase $this->setDriverGetConnectionMethod(); $result = $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); - $this->assertEquals('SELECT * place_val FROM place_val AND new_val', $result); + $this->assertTrue($result); } /** @@ -116,7 +116,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase $this->sql = 'PLAIN SQL'; $result = $this->stmt->execute(array()); - $this->assertEquals('PLAIN SQL', $result); + $this->assertTrue($result); } /** @@ -217,7 +217,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue($mysqliMock)); $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); - $this->assertEquals(array('pair' => 'value'), $this->stmt->fetchPairs()); + $this->assertSame(array('pair' => 'value'), $this->stmt->fetchPairs()); } /** From a9427450393d282a46c5194a462bddd8f3ee97e8 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 6 Dec 2011 14:16:13 +0400 Subject: [PATCH 22/25] two TODO closed for exceptions --- model/MongoModel.php | 11 +---------- model/MySQLiStatement.php | 2 +- tests/model/MySQLiStatementTest.php | 3 +-- tests/model/SqlModelTest.php | 3 --- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/model/MongoModel.php b/model/MongoModel.php index cb433dc..91f2bf7 100644 --- a/model/MongoModel.php +++ b/model/MongoModel.php @@ -33,17 +33,11 @@ abstract class MongoModel extends Model return $this->db->delete($this->table(), array('_id' => $id)); } - /** - * @TODO: test method - */ public function deleteAll($query = array()) { $this->db->delete($this->table(), $query); } - - /** - * @TODO: check for limits (if just one record needed) - */ + protected function fetchField($data, $params = array(), $field, $cache_key = null) { if (!$cache_key || !$result = $cache_key->get()) { @@ -55,9 +49,6 @@ abstract class MongoModel extends Model return $result; } - /** - * @TODO: check for limits (if just one record needed) - */ protected function fetch($data, $params = array(), $cache_key = null) { if (!$cache_key || !$result = $cache_key->get()) { diff --git a/model/MySQLiStatement.php b/model/MySQLiStatement.php index b489899..0176cfd 100644 --- a/model/MySQLiStatement.php +++ b/model/MySQLiStatement.php @@ -25,7 +25,7 @@ class MySQLiStatement extends DbStatement } if (count($this->map) > 0) { if (!is_string($param) && !is_int($param)) { - throw new GeneralException('Placeholder must be an array or string'); + throw new GeneralException('Placeholder must be an integer or string'); } if (is_object($value) && ! ($value instanceof DbExpr)) { throw new GeneralException('Objects excepts DbExpr not allowed.'); diff --git a/tests/model/MySQLiStatementTest.php b/tests/model/MySQLiStatementTest.php index c50149d..9d7c4bc 100644 --- a/tests/model/MySQLiStatementTest.php +++ b/tests/model/MySQLiStatementTest.php @@ -64,8 +64,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase /** * @expectedException GeneralException - * @expectedExceptionMessage Placeholder must be an array or string - * @TODO: change Exception Message 'Placeholder must be an array or string' - no arrays available + * @expectedExceptionMessage Placeholder must be an integer or string */ public function testBindParamExceptionParam() { diff --git a/tests/model/SqlModelTest.php b/tests/model/SqlModelTest.php index b09c6bb..5528c5f 100644 --- a/tests/model/SqlModelTest.php +++ b/tests/model/SqlModelTest.php @@ -92,9 +92,6 @@ class SqlModelTest extends PHPUnit_Framework_TestCase $this->assertSame(' ORDER BY name ASC', $method->invoke($this->model, array('sort' => 'name'), array('id', 'name'))); $this->assertEmpty($method->invoke($this->model, array())); - /** - * @TODO: Model::order - check DESC condition - make case insensitive - */ $this->assertSame(' ORDER BY name ASC', $method->invoke($this->model, array('sort' => 'name', 'order' => 'DESC'), array('id', 'name'))); } From 650f311b2b09382fe2783409612cc98a78532cae Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 6 Dec 2011 16:22:04 +0400 Subject: [PATCH 23/25] remove expectedException from annotation --- tests/model/MongoDriverTest.php | 14 ++------------ tests/model/MongoStatementTest.php | 18 ++++++------------ tests/model/MySQLiStatementTest.php | 20 +++++++------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index a027f1b..4a102e7 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -72,25 +72,18 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase } } - /** - * @expectedException GeneralException - * @expectedExceptionMessage Configuration must have a "hostname". - */ public function testGetConnectionNoHostname() { unset($this->conf['hostname']); + $this->setExpectedException('GeneralException', 'Configuration must have a "hostname"'); $mongo = new MongoDriver($this->conf); - $mongo->getConnection(); } - /** - * @expectedException MongoConnectionException - * @expectedExceptionMessage Couldn't authenticate with database - */ public function testGetConnectionWrongPassword() { $this->conf['password'] = 'nopass'; $mongo = new MongoDriver($this->conf); + $this->setExpectedException('MongoConnectionException', 'Couldn\'t authenticate with database'); $this->assertInstanceOf('MongoDB', $mongo->getConnection()); } @@ -323,8 +316,5 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $mongo = new MongoDriver($this->conf); $result = $mongo->command('items', array('distinct' =>'items', 'key' => 'name')); $this->assertEquals(4, count($result->fetch(DB::FETCH_ASSOC))); - - - } } \ No newline at end of file diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php index a09a324..e18c5d6 100644 --- a/tests/model/MongoStatementTest.php +++ b/tests/model/MongoStatementTest.php @@ -137,8 +137,6 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage MongoDB request error. */ public function testExecuteNoResult() { @@ -150,13 +148,12 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->expects($this->any()) ->method('execute') ->will($this->returnValue(false)); + $this->setExpectedException('GeneralException', 'MongoDB request error.'); $this->stmt->execute(); } /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage No connection to MongoDB server. */ public function testExecuteNoConnection() { @@ -167,6 +164,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->expects($this->any()) ->method('getConnection') ->will($this->returnValue(false)); + $this->setExpectedException('GeneralException', 'No connection to MongoDB server.'); $this->stmt->execute(); } @@ -284,8 +282,6 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage Invalid fetch mode "222" specified */ public function testFetchWrongMode() { @@ -301,6 +297,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue(array('some' => 'val'))); $this->stmt->execute(); + $this->setExpectedException('GeneralException', 'Invalid fetch mode "222" specified'); $this->stmt->fetch(222); } @@ -325,8 +322,6 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage MongoStatement error. Impossible order results of opened cursor */ public function testOrderException() { @@ -340,13 +335,12 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue(array('some' => 'val'))); $this->stmt->execute(); + $this->setExpectedException('GeneralException', 'MongoStatement error. Impossible order results of opened cursor'); $this->stmt->order(array('id' => 1)); } /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage MongoStatement error. Impossible skip results of opened cursor */ public function testSkipException() { @@ -360,13 +354,12 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue(array('some' => 'val'))); $this->stmt->execute(); + $this->setExpectedException('GeneralException', 'MongoStatement error. Impossible skip results of opened cursor'); $this->stmt->skip(array('id' => 1)); } /** * @runInSeparateProcess - * @expectedException GeneralException - * @expectedExceptionMessage MongoStatement error. Impossible limit results of opened cursor */ public function testLimitException() { @@ -380,6 +373,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnValue(array('some' => 'val'))); $this->stmt->execute(); + $this->setExpectedException('GeneralException', 'MongoStatement error. Impossible limit results of opened cursor'); $this->stmt->limit(array('id' => 1)); } diff --git a/tests/model/MySQLiStatementTest.php b/tests/model/MySQLiStatementTest.php index 9d7c4bc..83dfc96 100644 --- a/tests/model/MySQLiStatementTest.php +++ b/tests/model/MySQLiStatementTest.php @@ -62,23 +62,17 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase $this->assertTrue($this->stmt->bindParam('place', $val)); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage Placeholder must be an integer or string - */ public function testBindParamExceptionParam() { $val = 1; + $this->setExpectedException('GeneralException', 'Placeholder must be an integer or string'); $this->stmt->bindParam(array(), $val); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage Objects excepts DbExpr not allowed. - */ public function testBindParamExceptionWrongObject() { $val = $this->getMock('NotDbExpr'); + $this->setExpectedException('GeneralException', 'Objects excepts DbExpr not allowed.'); $this->stmt->bindParam('paa', $val); } @@ -98,7 +92,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase ->will($this->returnCallback(array($this, 'driverQuote'))); $this->setDriverGetConnectionMethod(); - + $result = $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); $this->assertTrue($result); } @@ -112,7 +106,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase define('DEBUG', false); } $this->setDriverGetConnectionMethod(); - + $this->sql = 'PLAIN SQL'; $result = $this->stmt->execute(array()); $this->assertTrue($result); @@ -214,7 +208,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase ->expects($this->any()) ->method('getConnection') ->will($this->returnValue($mysqliMock)); - + $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); $this->assertSame(array('pair' => 'value'), $this->stmt->fetchPairs()); } @@ -302,7 +296,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase $this->stmt->execute(array('place' => 'place_val', 'new' => 'new_val')); $this->setExpectedException('GeneralException'); - + $this->stmt->fetch(324); } @@ -336,7 +330,7 @@ class MySQLiStatementTest extends PHPUnit_Framework_TestCase ->method('query') ->with($this->anything()) ->will($this->returnValue($resultMock)); - + $this->driver ->expects($this->any()) ->method('getConnection') From b9f771806312a3eadd85ea1ddb0ade15c53c90e2 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 6 Dec 2011 16:22:37 +0400 Subject: [PATCH 24/25] added __toString() for MongoDbCommands --- model/MongoDbCommand.php | 123 +++++++++++++++++++++++++++++++------ tests/model/MongoDbCommandTest.php | 85 +++++++++++++++++++------ 2 files changed, 170 insertions(+), 38 deletions(-) diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 88a849d..2f2c095 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -63,10 +63,7 @@ abstract class MongoDbCommand /** * @TODO: implement method in subclasses for Profiler */ - public function __toString() - { - return get_called_class(); - } + abstract public function __toString(); } class FindMongoCommand extends MongoDbCommand @@ -79,7 +76,7 @@ class FindMongoCommand extends MongoDbCommand protected function concreteExecute() { - if($this->multiple) { + if ($this->multiple) { return $this->collection->find($this->condition, $this->fields); } else { return $this->collection->findOne($this->condition, $this->fields); @@ -94,6 +91,26 @@ class FindMongoCommand extends MongoDbCommand return false; } } + + public function __toString() + { + if ($this->checkParams()) { + $result = 'Collection: ' . $this->collection . PHP_EOL; + ob_start(); + var_dump($this->condition); + $condition = ob_get_clean(); + $result .= 'Condition: ' . $condition . PHP_EOL; + ob_start(); + var_dump($this->fields); + $fields = ob_get_clean(); + $result .= 'Fields: ' . $fields . PHP_EOL; + $mult = $this->multiple ? 'TRUE' : 'FALSE'; + $result .= 'Multiple fields: ' . $mult . PHP_EOL; + return $result; + } else { + return 'Command properties not set'; + } + } } class InsertMongoCommand extends MongoDbCommand @@ -124,6 +141,22 @@ class InsertMongoCommand extends MongoDbCommand { return $this->insertId; } + + public function __toString() + { + if ($this->checkParams()) { + $result = 'Collection: ' . $this->collection . PHP_EOL; + ob_start(); + var_dump($this->data); + $data = ob_get_clean(); + $result .= 'Data: ' . $data . PHP_EOL; + $safe = $this->safe ? 'TRUE' : 'FALSE'; + $result .= 'Safe operation: ' . $safe . PHP_EOL; + return $result; + } else { + return 'Command properties not set'; + } + } } class UpdateMongoCommand extends MongoDbCommand @@ -154,6 +187,30 @@ class UpdateMongoCommand extends MongoDbCommand return false; } } + + public function __toString() + { + if ($this->checkParams()) { + $result = 'Collection: ' . $this->collection . PHP_EOL; + ob_start(); + var_dump($this->condition); + $condition = ob_get_clean(); + $result .= 'Condition: ' . $condition . PHP_EOL; + ob_start(); + var_dump($this->data); + $data = ob_get_clean(); + $result .= 'Data: ' . $data . PHP_EOL; + $mult = $this->multiple ? 'TRUE' : 'FALSE'; + $result .= 'Multiple fields: ' . $mult . PHP_EOL; + $upsert = $this->upsert ? 'TRUE' : 'FALSE'; + $result .= 'Upsert: ' . $upsert . PHP_EOL; + $safe = $this->safe ? 'TRUE' : 'FALSE'; + $result .= 'Safe operation: ' . $safe . PHP_EOL; + return $result; + } else { + return 'Command properties not set'; + } + } } class RemoveMongoCommand extends MongoDbCommand @@ -175,21 +232,37 @@ class RemoveMongoCommand extends MongoDbCommand return false; } } + + public function __toString() + { + if ($this->checkParams()) { + $result = 'Collection: ' . $this->collection . PHP_EOL; + ob_start(); + var_dump($this->condition); + $condition = ob_get_clean(); + $result .= 'Condition: ' . $condition . PHP_EOL; + $safe = $this->safe ? 'TRUE' : 'FALSE'; + $result .= 'Safe operation: ' . $safe . PHP_EOL; + return $result; + } else { + return 'Command properties not set'; + } + } } - class CommandMongoCommand extends MongoDbCommand - { - protected $command; +class CommandMongoCommand extends MongoDbCommand +{ + protected $command; - protected function concreteExecute() - { - $db = $this->collection->db; - if($db instanceof MongoDB) { - return $db->command($this->command); - } else { - return false; - } - } + protected function concreteExecute() + { + $db = $this->collection->db; + if ($db instanceof MongoDB) { + return $db->command($this->command); + } else { + return false; + } + } protected function checkParams() { @@ -199,4 +272,18 @@ class RemoveMongoCommand extends MongoDbCommand return false; } } - } + + public function __toString() + { + if ($this->checkParams()) { + $result = 'Collection: ' . $this->collection . PHP_EOL; + ob_start(); + var_dump($this->command); + $command = ob_get_clean(); + $result .= 'Command: ' . $command . PHP_EOL; + return $result; + } else { + return 'Command properties not set'; + } + } +} diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index a325379..2d08e61 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -90,14 +90,11 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals('insert', $result['name']); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage FindMongoCommand error. Bind all required params first. - */ public function testFindCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); $cmd->bindParam('condition', array('name' => 'bread')); + $this->setExpectedException('GeneralException', 'FindMongoCommand error. Bind all required params first'); $cmd->execute(); } @@ -120,13 +117,10 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(1, $result->count()); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage InsertMongoCommand error. Bind all required params first. - */ public function testInsertCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); + $this->setExpectedException('GeneralException', 'InsertMongoCommand error. Bind all required params first'); $cmd->execute(); } @@ -155,14 +149,11 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(1, $result->count()); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage UpdateMongoCommand error. Bind all required params first. - */ public function testUpdateCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->collection); $cmd->bindParam('data', array('name' => 'bread')); + $this->setExpectedException('GeneralException', 'UpdateMongoCommand error. Bind all required params first'); $cmd->execute(); } @@ -191,23 +182,17 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(0, $result->count()); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage RemoveMongoCommand error. Bind all required params first. - */ public function testRemoveCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); + $this->setExpectedException('GeneralException', 'RemoveMongoCommand error. Bind all required params first.'); $cmd->execute(); } - /** - * @expectedException GeneralException - * @expectedExceptionMessage CommandMongoCommand error. Bind all required params first. - */ public function testCommandCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $this->collection); + $this->setExpectedException('GeneralException', 'CommandMongoCommand error. Bind all required params first'); $cmd->execute(); } @@ -217,9 +202,69 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->bindParam('command', array()); $this->assertFalse($cmd->execute()); } + + public function testCommandCommand() + { + $col = new CollectionMock(); + $col->db = $this->getMock('MongoDb', array('command'), array(), 'SomeMongoMock', false); + $col->db + ->expects($this->once()) + ->method('command') + ->will($this->returnValue(true)); + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $col); + $cmd->bindParam('command', array()); + $this->assertTrue($cmd->execute()); + } + + public function testToStringParamsNotSet() + { + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); + $this->assertSame('Command properties not set', $cmd->__toString()); + } + + public function testToString() + { + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); + $this->assertSame('Command properties not set', $cmd->__toString()); + $cmd->bindParam('command', array()); + $this->assertStringStartsWith('Collection: CollectionMock', $cmd->__toString()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); + $this->assertSame('Command properties not set', $cmd->__toString()); + $cmd + ->bindParam('data', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertStringStartsWith('Collection: ', $cmd->__toString()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); + $this->assertSame('Command properties not set', $cmd->__toString()); + $cmd->bindParam('condition', array('name' => 'insert'))->bindParam('fields', array()); + $this->assertStringStartsWith('Collection: ', $cmd->__toString()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); + $this->assertSame('Command properties not set', $cmd->__toString()); + $cmd + ->bindParam('condition', array('name' => 'insert')) + ->bindParam('safe', true); + $this->assertStringStartsWith('Collection: ', $cmd->__toString()); + + $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->collection); + $this->assertSame('Command properties not set', $cmd->__toString()); + $cmd + ->bindParam('condition', array('name' => 'insert')) + ->bindParam('data', array('$set' => array('name' => 'update'))) + ->bindParam('upsert', false) + ->bindParam('safe', true); + $this->assertStringStartsWith('Collection: ', $cmd->__toString()); + } } class CollectionMock { public $db = array(); + + public function __toString() + { + return 'CollectionMock'; + } } From 2c611dc3db769c899cbe806a93a3e54e1cd1ef56 Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 6 Dec 2011 16:53:16 +0400 Subject: [PATCH 25/25] added Mongo group for tests --- tests/model/MongoDbCommandTest.php | 42 ++++++++++++++++++++++++++++++++++++++ tests/model/MongoDriverTest.php | 23 +++++++++++++++++++++ tests/model/MongoModelTest.php | 9 ++++++++ tests/model/MongoStatementTest.php | 17 +++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/tests/model/MongoDbCommandTest.php b/tests/model/MongoDbCommandTest.php index 2d08e61..8302b42 100644 --- a/tests/model/MongoDbCommandTest.php +++ b/tests/model/MongoDbCommandTest.php @@ -44,6 +44,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->collection = $db->selectCollection($collection); } + /** + * @group Mongo + */ public function testCommandFactory() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND); @@ -60,6 +63,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertInstanceOf('RemoveMongoCommand', $cmd); } + /** + * @group Mongo + */ public function testFindCommand() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); @@ -90,6 +96,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals('insert', $result['name']); } + /** + * @group Mongo + */ public function testFindCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->collection); @@ -98,6 +107,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->execute(); } + /** + * @group Mongo + */ public function testInsertCommand() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); @@ -117,6 +129,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(1, $result->count()); } + /** + * @group Mongo + */ public function testInsertCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); @@ -124,6 +139,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->execute(); } + /** + * @group Mongo + */ public function testUpdateCommand() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); @@ -149,6 +167,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(1, $result->count()); } + /** + * @group Mongo + */ public function testUpdateCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->collection); @@ -157,6 +178,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->execute(); } + /** + * @group Mongo + */ public function testRemoveCommand() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->collection); @@ -182,6 +206,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertEquals(0, $result->count()); } + /** + * @group Mongo + */ public function testRemoveCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->collection); @@ -189,6 +216,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->execute(); } + /** + * @group Mongo + */ public function testCommandCommandNotAllParamsBinded() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, $this->collection); @@ -196,6 +226,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $cmd->execute(); } + /** + * @group Mongo + */ public function testCommandCommandNotMongoDb() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); @@ -203,6 +236,9 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertFalse($cmd->execute()); } + /** + * @group Mongo + */ public function testCommandCommand() { $col = new CollectionMock(); @@ -216,12 +252,18 @@ class MongoDbCommandTest extends PHPUnit_Framework_TestCase $this->assertTrue($cmd->execute()); } + /** + * @group Mongo + */ public function testToStringParamsNotSet() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); $this->assertSame('Command properties not set', $cmd->__toString()); } + /** + * @group Mongo + */ public function testToString() { $cmd = MongoCommandBuilder::factory(MongoCommandBuilder::COMMAND, new CollectionMock()); diff --git a/tests/model/MongoDriverTest.php b/tests/model/MongoDriverTest.php index 4a102e7..b673d4f 100644 --- a/tests/model/MongoDriverTest.php +++ b/tests/model/MongoDriverTest.php @@ -72,6 +72,9 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase } } + /** + * @group Mongo + */ public function testGetConnectionNoHostname() { unset($this->conf['hostname']); @@ -79,6 +82,9 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $mongo = new MongoDriver($this->conf); } + /** + * @group Mongo + */ public function testGetConnectionWrongPassword() { $this->conf['password'] = 'nopass'; @@ -87,6 +93,9 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertInstanceOf('MongoDB', $mongo->getConnection()); } + /** + * @group Mongo + */ public function testGetConnection() { $mongo = new MongoDriver($this->conf); @@ -101,6 +110,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFind() { @@ -131,6 +141,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testOrderSkipLimit() { @@ -169,6 +180,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testCount() { @@ -183,6 +195,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testGet() { @@ -200,6 +213,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testRemove() { @@ -217,6 +231,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testInsert() { @@ -233,6 +248,10 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase $this->assertEquals(230, $mongo->get('items', array('name' => 'meat'))->fetch()->weight); } + /** + * @runInSeparateProcess + * @group Mongo + */ public function testGetInsertId() { if (!defined('DEBUG')) { @@ -255,6 +274,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testUpdate() { @@ -274,6 +294,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testUpsert() { @@ -290,6 +311,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFindAndModify() { @@ -307,6 +329,7 @@ class MongoDriverTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testCommand() { diff --git a/tests/model/MongoModelTest.php b/tests/model/MongoModelTest.php index cc3d8a6..c9cc58c 100644 --- a/tests/model/MongoModelTest.php +++ b/tests/model/MongoModelTest.php @@ -50,6 +50,9 @@ class MongoModelTest extends PHPUnit_Framework_TestCase set_new_overload(array($this, 'newCallback')); } + /** + * @group Mongo + */ public function testModel() { $this->assertInstanceOf('MongoMockModel', $this->model); @@ -57,6 +60,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFind() { @@ -71,6 +75,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testGet() { @@ -86,6 +91,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testDelete() { @@ -100,6 +106,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testDeleteAll() { @@ -114,6 +121,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testCount() { @@ -126,6 +134,7 @@ class MongoModelTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetch() { diff --git a/tests/model/MongoStatementTest.php b/tests/model/MongoStatementTest.php index e18c5d6..c945925 100644 --- a/tests/model/MongoStatementTest.php +++ b/tests/model/MongoStatementTest.php @@ -57,6 +57,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testAffectedNumRowsNoResult() { @@ -77,6 +78,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testAffectedNumRows() { @@ -94,6 +96,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testGetInsertId() { @@ -124,6 +127,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testExecute() { @@ -137,6 +141,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testExecuteNoResult() { @@ -154,6 +159,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testExecuteNoConnection() { @@ -170,6 +176,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testExecuteWithDebug() { @@ -183,6 +190,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testBindParam() { @@ -205,6 +213,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetch() { @@ -223,6 +232,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetchWithInitialArray() { @@ -243,6 +253,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetchAssocFromCursor() { @@ -261,6 +272,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetchAssocFromArray() { @@ -282,6 +294,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testFetchWrongMode() { @@ -303,6 +316,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testSkipOrderLimit() { @@ -322,6 +336,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testOrderException() { @@ -341,6 +356,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testSkipException() { @@ -360,6 +376,7 @@ class MongoStatementTest extends PHPUnit_Framework_TestCase /** * @runInSeparateProcess + * @group Mongo */ public function testLimitException() {