From 5a2c3dd2b7ac8373eb45010641dc53492ef28f66 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Fri, 2 Sep 2011 21:52:38 +1000 Subject: [PATCH 01/33] Changes allow setting of driver wide colleciton options for safe, fsync & timeout. --- models/datasources/mongodb_source.php | 29 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/models/datasources/mongodb_source.php b/models/datasources/mongodb_source.php index bf03e29..b776e7d 100644 --- a/models/datasources/mongodb_source.php +++ b/models/datasources/mongodb_source.php @@ -92,6 +92,7 @@ class MongodbSource extends DboSource { 'login' => '', 'password' => '', 'replicaset' => '', + 'collection_options' => array() ); /** @@ -256,16 +257,17 @@ public function insertMulti($table, $fields, $values) { $data[] = array_combine($fields, $row); } $this->_prepareLogQuery($table); // just sets a timer + $params = array_merge($this->config['collection_options'], array('safe' => true)); try{ $return = $this->_db ->selectCollection($table) - ->batchInsert($data, array('safe' => true)); + ->batchInsert($data, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); } if ($this->fullDebug) { - $this->logQuery("db.{$table}.insertMulti( :data , array('safe' => true))", compact('data')); + $this->logQuery("db.{$table}.insertMulti( :data , :params )", compact('data','params')); } } @@ -466,16 +468,17 @@ public function create(&$Model, $fields = null, $values = null) { } $this->_prepareLogQuery($Model); // just sets a timer + $params = $this->config['collection_options']; try{ $return = $this->_db ->selectCollection($Model->table) - ->insert($data, true); + ->insert($data, true, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); } if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.insert( :data , true)", compact('data')); + $this->logQuery("db.{$Model->useTable}.insert( :data , true, :params )", compact('data','params')); } if (!empty($return) && $return['ok']) { @@ -716,27 +719,28 @@ public function update(&$Model, $fields = null, $values = null, $conditions = nu unset($data['_id']); $data = $this->setMongoUpdateOperator($Model, $data); - + $params = array_merge($this->config['collection_options'], array("multiple" => true)); try{ - $return = $mongoCollectionObj->update($cond, $data, array("multiple" => false)); + $return = $mongoCollectionObj->update($cond, $data, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); } if ($this->fullDebug) { $this->logQuery("db.{$Model->useTable}.update( :conditions, :data, :params )", - array('conditions' => $cond, 'data' => $data, 'params' => array("multiple" => false)) + array('conditions' => $cond, 'data' => $data, 'params' => $params) ); } } else { + $params = $this->config['collection_options']; try{ - $return = $mongoCollectionObj->save($data); + $return = $mongoCollectionObj->save($data, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); } if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.save( :data )", compact('data')); + $this->logQuery("db.{$Model->useTable}.save( :data, :params )", compact('data', 'params')); } } return $return; @@ -804,12 +808,13 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { $this->_stripAlias($fields, $Model->alias, false, 'value'); $fields = $this->setMongoUpdateOperator($Model, $fields); - + $params = array_merge($this->config['collection_options'], array("multiple" => true)); + $this->_prepareLogQuery($Model); // just sets a timer try{ $return = $this->_db ->selectCollection($Model->table) - ->update($conditions, $fields, array("multiple" => true)); + ->update($conditions, $fields, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); @@ -817,7 +822,7 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { if ($this->fullDebug) { $this->logQuery("db.{$Model->useTable}.update( :conditions, :fields, :params )", - array('conditions' => $conditions, 'fields' => $fields, 'params' => array("multiple" => true)) + array('conditions' => $conditions, 'fields' => $fields, 'params' => $params) ); } return $return; From 9b2d9901ac8bed3ee37154993ad46822569f1aff Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Mon, 5 Sep 2011 12:33:39 +1000 Subject: [PATCH 02/33] Fixed bug in insert introduced in previous commit. --- models/datasources/mongodb_source.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/models/datasources/mongodb_source.php b/models/datasources/mongodb_source.php index b776e7d..4793439 100644 --- a/models/datasources/mongodb_source.php +++ b/models/datasources/mongodb_source.php @@ -190,6 +190,8 @@ public function connect() { $this->connection = new Mongo($host, true, $this->config['persistent']); } + $this->connection->setSlaveOkay($this->config['slaveok']); + if ($this->_db = $this->connection->selectDB($this->config['database'])) { if (!empty($this->config['login']) && $this->_driverVersion < '1.2.0') { $return = $this->_db->authenticate($this->config['login'], $this->config['password']); @@ -201,7 +203,7 @@ public function connect() { $this->connected = true; } - + } catch(MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); @@ -472,13 +474,13 @@ public function create(&$Model, $fields = null, $values = null) { try{ $return = $this->_db ->selectCollection($Model->table) - ->insert($data, true, $params); + ->insert($data, $params); } catch (MongoException $e) { $this->error = $e->getMessage(); trigger_error($this->error); } if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.insert( :data , true, :params )", compact('data','params')); + $this->logQuery("db.{$Model->useTable}.insert( :data , :params )", compact('data','params')); } if (!empty($return) && $return['ok']) { @@ -713,6 +715,7 @@ public function update(&$Model, $fields = null, $values = null, $conditions = nu } $this->_prepareLogQuery($Model); // just sets a timer + $return = false; if (!empty($data['_id'])) { $this->_convertId($data['_id']); $cond = array('_id' => $data['_id']); From afe9b9ed0776fb1ec49aedc7ab155c36b40bcefc Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Mon, 12 Sep 2011 08:58:07 +1000 Subject: [PATCH 03/33] Updated to use collectionOptions on a per collection basis rather than driver wide. --- models/datasources/mongodb_source.php | 35 ++++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/models/datasources/mongodb_source.php b/models/datasources/mongodb_source.php index 4793439..e21c4d3 100644 --- a/models/datasources/mongodb_source.php +++ b/models/datasources/mongodb_source.php @@ -91,10 +91,29 @@ class MongodbSource extends DboSource { 'port' => '27017', 'login' => '', 'password' => '', - 'replicaset' => '', - 'collection_options' => array() + 'replicaset' => '' ); +/** + * collection options + * + * set collection options for various mongo write operations. + * options can be found in the php manual + * http://www.php.net/manual/en/mongocollection.save.php + * http://www.php.net/manual/en/mongocollection.insert.php + * http://www.php.net/manual/en/mongocollection.batchinsert.php + * http://www.php.net/manual/en/mongocollection.update.php + * + * @var array + */ + + public $collectionOptions = array( + 'save' => array(), + 'insert' => array(), + 'batchInsert' => array(), + 'update' => array() + ); + /** * column definition * @@ -190,8 +209,6 @@ public function connect() { $this->connection = new Mongo($host, true, $this->config['persistent']); } - $this->connection->setSlaveOkay($this->config['slaveok']); - if ($this->_db = $this->connection->selectDB($this->config['database'])) { if (!empty($this->config['login']) && $this->_driverVersion < '1.2.0') { $return = $this->_db->authenticate($this->config['login'], $this->config['password']); @@ -259,7 +276,7 @@ public function insertMulti($table, $fields, $values) { $data[] = array_combine($fields, $row); } $this->_prepareLogQuery($table); // just sets a timer - $params = array_merge($this->config['collection_options'], array('safe' => true)); + $params = array_merge($this->collectionOptions['batchInsert'], array('safe' => true)); try{ $return = $this->_db ->selectCollection($table) @@ -470,7 +487,7 @@ public function create(&$Model, $fields = null, $values = null) { } $this->_prepareLogQuery($Model); // just sets a timer - $params = $this->config['collection_options']; + $params = $this->collectionOptions['insert']; try{ $return = $this->_db ->selectCollection($Model->table) @@ -722,7 +739,7 @@ public function update(&$Model, $fields = null, $values = null, $conditions = nu unset($data['_id']); $data = $this->setMongoUpdateOperator($Model, $data); - $params = array_merge($this->config['collection_options'], array("multiple" => true)); + $params = array_merge($this->collectionOptions['update'], array("multiple" => true)); try{ $return = $mongoCollectionObj->update($cond, $data, $params); } catch (MongoException $e) { @@ -735,7 +752,7 @@ public function update(&$Model, $fields = null, $values = null, $conditions = nu ); } } else { - $params = $this->config['collection_options']; + $params = $this->collectionOptions['save']; try{ $return = $mongoCollectionObj->save($data, $params); } catch (MongoException $e) { @@ -811,7 +828,7 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { $this->_stripAlias($fields, $Model->alias, false, 'value'); $fields = $this->setMongoUpdateOperator($Model, $fields); - $params = array_merge($this->config['collection_options'], array("multiple" => true)); + $params = array_merge($this->collectionOptions['update'], array("multiple" => true)); $this->_prepareLogQuery($Model); // just sets a timer try{ From fc1745641c0e437ae2d5c3212cf19fcabd08d052 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 08:48:28 +1100 Subject: [PATCH 04/33] Library ported from using php-mongo extebsion to new php-mongodb extension. Tests updated. Composer added. --- Model/Behavior/SchemalessBehavior.php | 89 ++ Model/Behavior/SqlCompatibleBehavior.php | 295 ++++ Model/Datasource/MongodbSource.php | 1481 ++++++++++++++++++ README.markdown | 6 +- Test/Case/Datasource/MongodbSourceTest.php | 1622 ++++++++++++++++++++ composer.json | 23 + 6 files changed, 3513 insertions(+), 3 deletions(-) create mode 100644 Model/Behavior/SchemalessBehavior.php create mode 100644 Model/Behavior/SqlCompatibleBehavior.php create mode 100644 Model/Datasource/MongodbSource.php create mode 100644 Test/Case/Datasource/MongodbSourceTest.php create mode 100644 composer.json diff --git a/Model/Behavior/SchemalessBehavior.php b/Model/Behavior/SchemalessBehavior.php new file mode 100644 index 0000000..b721eb0 --- /dev/null +++ b/Model/Behavior/SchemalessBehavior.php @@ -0,0 +1,89 @@ +settings[$Model->alias] = array_merge($this->_defaultSettings, $config); + } + +/** + * beforeSave method + * + * Set the schema to allow saving whatever has been passed + * + * @param mixed $Model + * @return void + * @access public + */ + public function beforeSave(Model $Model, $options = array()) { + $Model->cacheSources = false; + $Model->schema(true); + return true; + } +} \ No newline at end of file diff --git a/Model/Behavior/SqlCompatibleBehavior.php b/Model/Behavior/SqlCompatibleBehavior.php new file mode 100644 index 0000000..6c499f7 --- /dev/null +++ b/Model/Behavior/SqlCompatibleBehavior.php @@ -0,0 +1,295 @@ + true, + 'operators' => array( + '!=' => '$ne', + '>' => '$gt', + '>=' => '$gte', + '<' => '$lt', + '<=' => '$lte', + 'IN' => '$in', + 'NOT' => '$not', + 'NOT IN' => '$nin' + ) + ); + +/** + * setup method + * + * Allow overriding the operator map + * + * @param mixed $Model + * @param array $config array() + * @return void + * @access public + */ + public function setup(Model $Model, $config = array()) { + $this->settings[$Model->alias] = array_merge($this->_defaultSettings, $config); + } + +/** + * If requested, convert dates from MongoDate objects to standard date strings + * + * @param mixed $Model + * @param mixed $results + * @param mixed $primary + * @return void + * @access public + */ + public function afterFind(Model $Model, $results, $primary=false) { + if ($this->settings[$Model->alias]['convertDates']) { + $this->convertDates($results); + } + return $results; + } + +/** + * beforeFind method + * + * If conditions are an array ensure they are mongified + * + * @param mixed $Model + * @param mixed $query + * @return void + * @access public + */ + public function beforeFind(Model $Model, $query) { + if (is_array($query['order'])) { + $this->_translateOrders($Model, $query['order']); + } + if (is_array($query['conditions']) && $this->_translateConditions($Model, $query['conditions'])) { + return $query; + } + return $query; + } + +/** + * Convert MongoDate objects to strings for the purpose of view simplicity + * + * @param mixed $results + * @return void + * @access protected + */ + protected function convertDates(&$results) { + if (is_array($results)) { + foreach($results as &$row) { + $this->convertDates($row); + } + } elseif (is_a($results, 'MongoDate')) { + $results = date('Y-M-d h:i:s', $results->sec); + } + } + + +/** + * translateOrders method + * change order syntax from SQL style to Mongo style + * + * @param mixed $Model + * @param mixed $orders + * @return void + * @access protected + */ + protected function _translateOrders(Model &$Model, &$orders) { + if(!empty($orders[0])) { + foreach($orders[0] as $key => $val) { + if(preg_match('/^(.+) (ASC|DESC)$/i', $val, $match)) { + $orders[0][$match[1]] = $match[2]; + unset($orders[0][$key]); + } + } + } + } + + +/** + * translateConditions method + * + * Loop on conditions and desqlify them + * + * @param mixed $Model + * @param mixed $conditions + * @return void + * @access protected + */ + protected function _translateConditions(Model &$Model, &$conditions) { + $return = false; + foreach($conditions as $key => &$value) { + $uKey = strtoupper($key); + if (substr($uKey, -5) === 'NOT IN') { + // 'Special' case because it has a space in it, and it's the whole key + $conditions[substr($key, 0, -5)]['$nin'] = $value; + unset($conditions[$key]); + $return = true; + continue; + } + if ($uKey === 'OR') { + unset($conditions[$key]); + foreach($value as $key => $part) { + $part = array($key => $part); + $this->_translateConditions($Model, $part); + $conditions['$or'][] = $part; + } + $return = true; + continue; + } + if ($key === $Model->primaryKey && is_array($value)) { + //_id=>array(1,2,3) pattern, set $in operator + $isMongoOperator = false; + foreach($value as $idKey => $idValue) { + //check a mongo operator exists + if(substr($idKey,0,1) === '$') { + $isMongoOperator = true; + continue; + } + } + unset($idKey, $idValue); + if($isMongoOperator === false) { + $conditions[$key] = array('$in' => $value); + } + $return = true; + continue; + } + if (substr($uKey, -3) === 'NOT') { + // 'Special' case because it's awkward + $childKey = key($value); + $childValue = current($value); + + if (in_array(substr($childKey, -1), array('>', '<', '='))) { + $parts = explode(' ', $childKey); + $operator = array_pop($parts); + if ($operator = $this->_translateOperator($Model, $operator)) { + $childKey = implode(' ', $parts); + } + } else { + $conditions[$childKey]['$nin'] = (array)$childValue; + unset($conditions['NOT']); + $return = true; + continue; + } + + $conditions[$childKey]['$not'][$operator] = $childValue; + unset($conditions['NOT']); + $return = true; + continue; + } + if (substr($uKey, -5) === ' LIKE') { + // 'Special' case because it's awkward + if ($value[0] === '%') { + $value = substr($value, 1); + } else { + $value = '^' . $value; + } + if (substr($value, -1) === '%') { + $value = substr($value, 0, -1); + } else { + $value .= '$'; + } + $value = str_replace('%', '.*', $value); + + $conditions[substr($key, 0, -5)] = new MongoRegex("/$value/i"); + unset($conditions[$key]); + $return = true; + continue; + } + + if (!in_array(substr($key, -1), array('>', '<', '='))) { + $return = true; + continue; + } + if (is_numeric($key && is_array($value))) { + if ($this->_translateConditions($Model, $value)) { + $return = true; + continue; + } + } + $parts = explode(' ', $key); + $operator = array_pop($parts); + if ($operator = $this->_translateOperator($Model, $operator)) { + $newKey = implode(' ', $parts); + $conditions[$newKey][$operator] = $value; + unset($conditions[$key]); + $return = true; + } + if (is_array($value)) { + if ($this->_translateConditions($Model, $value)) { + $return = true; + continue; + } + } + } + return $return; + } + +/** + * translateOperator method + * + * Use the operator map for the model and return what the db really wants to hear + * + * @param mixed $Model + * @param mixed $operator + * @return string + * @access protected + */ + protected function _translateOperator(Model $Model, $operator) { + if (!empty($this->settings[$Model->alias]['operators'][$operator])) { + return $this->settings[$Model->alias]['operators'][$operator]; + } + return ''; + } +} diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php new file mode 100644 index 0000000..7632d4f --- /dev/null +++ b/Model/Datasource/MongodbSource.php @@ -0,0 +1,1481 @@ + true, + 'persistent' => true, + 'host' => 'localhost', + 'database' => '', + 'port' => '27017', + 'login' => '', + 'password' => '', + 'replicaset' => '' + ); + +/** + * collection options + * + * set collection options for various mongo write operations. + * options can be found in the php manual + * http://www.php.net/manual/en/mongocollection.save.php + * http://www.php.net/manual/en/mongocollection.insert.php + * http://www.php.net/manual/en/mongocollection.batchinsert.php + * http://www.php.net/manual/en/mongocollection.update.php + * + * @var array + */ + + public $collectionOptions = array( + 'save' => array(), + 'insert' => array(), + 'batchInsert' => array(), + 'update' => array() + ); + +/** + * column definition + * + * @var array + */ + public $columns = array( + 'boolean' => array('name' => 'boolean'), + 'string' => array('name' => 'varchar'), + 'text' => array('name' => 'text'), + 'integer' => array('name' => 'integer', 'format' => null, 'formatter' => 'intval'), + 'float' => array('name' => 'float', 'format' => null, 'formatter' => 'floatval'), + 'datetime' => array('name' => 'datetime', 'format' => null, 'formatter' => 'MongodbDateFormatter'), + 'timestamp' => array('name' => 'timestamp', 'format' => null, 'formatter' => 'MongodbDateFormatter'), + 'time' => array('name' => 'time', 'format' => null, 'formatter' => 'MongodbDateFormatter'), + 'date' => array('name' => 'date', 'format' => null, 'formatter' => 'MongodbDateFormatter'), + ); + +/** + * Default schema for the mongo models + * + * @var array + * @access protected + */ + protected $_defaultSchema = array( + '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), + 'created' => array('type' => 'datetime', 'default' => null), + 'modified' => array('type' => 'datetime', 'default' => null) + ); + +/** + * Typemap used for most all find requests + * This best emulates the legacy query capabilities the plugin previously provided + * + * @var array + * @access protected + */ + protected $typeMap = array( + 'typeMap' => array( + 'root' => 'array', + 'document' => 'array', + 'array' => 'array' + ) + ); + +/** + * construct method + * + * By default don't try to connect until you need to + * + * @param array $config Configuration array + * @param bool $autoConnect false + * @return void + * @access public + */ + function __construct($config = array(), $autoConnect = false) { + return parent::__construct($config, $autoConnect); + } + +/** + * Destruct + * + * @access public + */ + public function __destruct() { + if ($this->connected) { + $this->disconnect(); + } + } + +/** + * commit method + * + * MongoDB doesn't support transactions + * + * @return void + * @access public + */ + public function commit() { + return false; + } + +/** + * Connect to the database + * + * @return boolean Connected + * @access public + */ + public function connect() { + $this->connected = false; + + try { + $connectionString = $this->createConnectionString($this->config); + + if (isset($this->config['replicaset']['host'])) { + $this->connection = new MongoDB\Client($this->config['replicaset']['host'], $this->config['replicaset']['options'], $this->typeMap); + } else { + $this->connection = new MongoDB\Client($connectionString, array(), $this->typeMap); + } + + if ($this->_db = $this->connection->selectDatabase($this->config['database'])) { + $this->connected = true; + } + } catch(MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + return $this->connected; + } + +/** + * Create connection string + * + * @param array $config + * @param string $version version of MongoDriver + */ + public function createConnectionString($config) { + $host = "mongodb://"; + $hostname = $config['host'] . ':' . $config['port']; + + if (! empty($config['login'])) { + $host .= $config['login'] .':'. $config['password'] . '@' . $hostname . '/'. $config['database']; + } else { + $host .= $hostname; + } + return $host; + } + +/** + * Inserts multiple values into a table + * + * @param string $table + * @param string $fields + * @param array $values + * @access public + */ + public function insertMulti($table, $fields, $values) { + if (!is_array($fields) || !is_array($values)) { + return false; + } + $data = array(); + foreach($values as $row) { + if (is_string($row)) { + $row = explode(', ', substr($row, 1, -1)); + } + $data[] = array_combine($fields, $row); + } + $this->_prepareLogQuery($Model->table); // just sets a timer + $params = array_merge($this->collectionOptions['batchInsert']); + try{ + $collection = $this->_db + ->selectCollection($Model->table) + ->insertMany($data, $params); + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.insertMulti( :data , :params )", compact('data','params')); + } + return $return; + } + +/** + * check connection to the database + * + * @return boolean Connected + * @access public + */ + public function isConnected() { + if ($this->connected === false) { + return false; + } + return $this->connect(); + } + +/** + * get MongoDB Object + * + * @return mixed MongoDB Object + * @access public + */ + public function getMongoDb() { + syslog(LOG_NOTICE, 'getMongoDb :'.print_r($this->_db, true)); + if ($this->connected === false) { + return false; + } + return $this->_db; + } + +/** + * get MongoDB Collection Object + * + * @return mixed MongoDB Collection Object + * @access public + */ + public function getMongoCollection(&$Model) { + if ($this->connected === false) { + return false; + } + return $this->_db->selectCollection($Model->table); + } + +/** + * isInterfaceSupported method + * + * listSources is infact supported, however: cake expects it to return a complete list of all + * possible sources in the selected db - the possible list of collections is infinte, so it's + * faster and simpler to tell cake that the interface is /not/ supported so it assumes that + * exist + * + * @param mixed $interface + * @return void + * @access public + */ + public function isInterfaceSupported($interface) { + if ($interface === 'listSources') { + return false; + } + return parent::isInterfaceSupported($interface); + } + +/** + * Close database connection + * + * @return boolean Connected + * @access public + */ + public function close() { + return $this->disconnect(); + } + +/** + * Disconnect from the database + * + * @return boolean Connected + * @access public + */ + public function disconnect() { + if ($this->connected !== false) { + $this->connected = false; + unset($this->_db, $this->connection); + } + return true; + } + +/** + * Set special options for the find command + * Options are in 'key' => 'value' format. Anything from the following URL should be fine : + * https://docs.mongodb.com/php-library/master/reference/method/MongoDBCollection-find/ + * + * @return void + * @access public + */ + public function setFindOptions($options) { + $this->_findOptions = $options; + } + +/** + * Set typeMap + * See this URL for a typemap discussion + * https://docs.mongodb.com/php-library/master/reference/bson/#type-maps + * + * @return void + * @access public + */ + public function setTypeMap($typeMap) { + $this->typeMap = $typeMap; + } + +/** + * Get list of available Collections + * Mongodb can create collections on the fly, so return true if connected. + * + * @param array $data + * @return array Collections + * @access public + */ + public function listSources($data = null) { + if (! $this->isConnected()) { + return false; + } + return true; + } + +/** + * Describe + * + * Automatically bind the schemaless behavior if there is no explicit mongo schema. + * When called, if there is model data it will be used to derive a schema. a row is plucked + * out of the db and the data obtained used to derive the schema. + * + * @param Model $Model + * @return array if model instance has mongoSchema, return it. + * @access public + */ + public function describe($Model) { + $Model->primaryKey = '_id'; + $schema = array(); + if (!empty($Model->mongoSchema) && is_array($Model->mongoSchema)) { + $schema = $Model->mongoSchema; + return $schema + $this->_defaultSchema; + } elseif ($this->isConnected() && is_a($Model, 'Model') && !empty($Model->Behaviors)) { + $Model->Behaviors->attach('MongoDBLib.Schemaless'); + if (! $Model->data) { + if ($this->_db->selectCollection($Model->table)->count()) { + return $this->deriveSchemaFromData($Model, $this->_db->selectCollection($Model->table)->findOne()); + } + } + } + return $this->deriveSchemaFromData($Model); + } + +/** + * begin method + * + * Mongo doesn't support transactions + * + * @return void + * @access public + */ + public function begin() { + return false; + } + +/** + * Calculate + * + * @param Model $Model + * @return array + * @access public + */ + public function calculate(Model $Model, $func, $params = array()) { + return array('count' => true); + } + +/** + * Quotes identifiers. + * + * MongoDb does not need identifiers quoted, so this method simply returns the identifier. + * + * @param string $name The identifier to quote. + * @return string The quoted identifier. + */ + public function name($name) { + return $name; + } + +/** + * Create Data + * + * @param Model $Model Model Instance + * @param array $fields Field data + * @param array $values Save data + * @return boolean Insert result + * @access public + */ + public function create(Model $Model, $fields = null, $values = null) { + if (! $this->isConnected()) { + return false; + } + + if ($fields !== null && $values !== null) { + $data = array_combine($fields, $values); + } else { + $data = $Model->data; + } + if (! empty($data['_id'])) { + $this->_convertId($data['_id']); + } + + $this->_prepareLogQuery($Model); // just sets a timer + $params = $this->collectionOptions['insert']; + try { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->insertOne($data, $params); + $return = true; + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.insert( :data , :params )", compact('data','params')); + } + + syslog(LOG_NOTICE, 'Create '); + + if (! empty($return)) { + syslog(LOG_NOTICE, 'Return '.json_encode($return)); + $id = $this->lastResult->getInsertedId(); + if ($this->config['set_string_id'] && is_object($id)) { + $id = $this->lastResult->getInsertedId()->__toString(); + } + syslog(LOG_NOTICE, 'Create _id '.json_encode($id)); + $Model->setInsertID($id); + $Model->id = $id; + return true; + } + return false; + } + +/** + * createSchema method + * + * Mongo no care for creating schema. Mongo work with no schema. + * + * @param mixed $schema + * @param mixed $tableName null + * @return void + * @access public + */ + public function createSchema($schema, $tableName = null) { + return true; + } + +/** + * dropSchema method + * + * Return a command to drop each table + * + * @param mixed $schema + * @param mixed $tableName null + * @return void + * @access public + */ + public function dropSchema(CakeSchema $schema, $tableName = null) { + if (!$this->isConnected()) { + return false; + } + + if (!is_a($schema, 'CakeSchema')) { + trigger_error(__('Invalid schema object', true), E_USER_WARNING); + return null; + } + if ($tableName) { + return "db.{$tableName}.drop();"; + } + + $toDrop = array(); + foreach ($schema->tables as $curTable => $columns) { + if ($tableName === $curTable) { + $toDrop[] = $curTable; + } + } + + if (count($toDrop) === 1) { + return "db.{$toDrop[0]}.drop();"; + } + + $return = "toDrop = :tables;\nfor( i = 0; i < toDrop.length; i++ ) {\n\tdb[toDrop[i]].drop();\n}"; + $tables = '["' . implode($toDrop, '", "') . '"]'; + + return String::insert($return, compact('tables')); + } + +/** + * distinct method + * + * @param mixed $Model + * @param array $keys array() + * @param array $params array() + * @return void + * @access public + */ + public function distinct(&$Model, $keys = array(), $params = array()) { + if (! $this->isConnected()) { + return false; + } + + $this->_prepareLogQuery($Model); // just sets a timer + + if (array_key_exists('conditions', $params)) { + $params = $params['conditions']; + } + try{ + $return = $this->_db + ->selectCollection($Model->table) + ->distinct($keys, $params); + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.distinct( :keys, :params )", compact('keys', 'params')); + } + + return $return; + } + +/** + * group method + * + * Note : https://docs.mongodb.com/php-library/master/upgrade/#old-and-new-methods + * As of 2016-10-28 Mongo advises "Not yet implemented. See PHPLIB-177. Use MongoDB\Database::command. + * + * @param mixed $Model + * @param array $params array() + * Set params same as MongoCollection::group() + * key,initial, reduce, options(conditions, finalize) + * + * Ex. $params = array( + * 'key' => array('field' => true), + * 'initial' => array('csum' => 0), + * 'reduce' => 'function(obj, prev){prev.csum += 1;}', + * 'options' => array( + * 'condition' => array('age' => array('$gt' => 20)), + * 'finalize' => array(), + * ), + * ); + * @return void + * @access public + */ + public function group($params, Model $Model = null) { + if (! $this->isConnected() || count($params) === 0 ) { + return false; + } + + $this->_prepareLogQuery($Model); + $key = empty($params['key']) ? array() : $params['key']; + $initial = empty($params['initial']) ? array() : $params['initial']; + $reduce = empty($params['reduce']) ? array() : $params['reduce']; + $cond = empty($params['conditions']) ? array() : $params['conditions']; + $options = empty($params['options']) ? array() : $params['options']; + + try { + $tmp = $this->_db + ->command( + array( + 'group' => array( + 'ns' => $Model->table, + 'key' => $key, + 'initial' => $initial, + 'cond' => $cond, + '$reduce' => $reduce + ) + ), + $options + ); + $return = $tmp->toArray()[0]; + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.group( :key, :initial, :reduce, :options )", $params); + } + return $return; + } + + +/** + * ensureIndex method + * + * @param mixed $Model + * @param array $keys array() + * @param array $params array() + * @return void + * @access public + */ + public function ensureIndex(&$Model, $keys = array(), $params = array()) { + if (! $this->isConnected()) { + return false; + } + + $this->_prepareLogQuery($Model); // just sets a timer + + try { + $return = $this->_db + ->selectCollection($Model->table) + ->createIndex($keys, $params); + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.ensureIndex( :keys, :params )", compact('keys', 'params')); + } + + return $return; + } + +/** + * Update Data + * + * This method uses $set operator automatically with MongoCollection::update(). + * If you don't want to use $set operator, you can chose any one as follw. + * 1. Set TRUE in Model::mongoNoSetOperator property. + * 2. Set a mongodb operator in a key of save data as follow. + * Model->save(array('_id' => $id, '$inc' => array('count' => 1))); + * Don't use Model::mongoSchema property, + * CakePHP delete '$inc' data in Model::Save(). + * 3. Set a Mongo operator in Model::mongoNoSetOperator property. + * Model->mongoNoSetOperator = '$inc'; + * Model->save(array('_id' => $id, array('count' => 1))); + * + * @param Model $Model Model Instance + * @param array $fields Field data + * @param array $values Save data + * @return boolean Update result + * @access public + */ + public function update(Model $Model, $fields = null, $values = null, $conditions = null) { + if (! $this->isConnected()) { + return false; + } + + if ($fields !== null && $values !== null) { + $data = array_combine($fields, $values); + } elseif ($fields !== null && $conditions !== null) { + return $this->updateAll($Model, $fields, $conditions); + } else{ + $data = $Model->data; + } + + if (empty($data['_id'])) { + $data['_id'] = $Model->id; + } + $this->_convertId($data['_id']); + + try { + $mongoCollectionObj = $this->_db + ->selectCollection($Model->table); + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + return false; + } + + $this->_prepareLogQuery($Model); // just sets a timer + $return = false; + if (! empty($data['_id'])) { + syslog(LOG_NOTICE, 'Update A'); + + $this->_convertId($data['_id']); + $cond = array('_id' => $data['_id']); + unset($data['_id']); + + $data = $this->setMongoUpdateOperator($Model, $data); + $params = $this->collectionOptions['update']; + try { + syslog(LOG_NOTICE, 'Cond '.json_encode($cond)); + syslog(LOG_NOTICE, 'Data '.json_encode($data)); + if ($Model->mongoNoSetOperator === true) { + $this->lastResult = $mongoCollectionObj->replaceOne($cond, $data, $params); + } else { + $this->lastResult = $mongoCollectionObj->updateOne($cond, $data, $params); + } + $return = true; + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.update( :conditions, :data, :params )", + array('conditions' => $cond, 'data' => $data, 'params' => $params) + ); + } + } else { + // Not sure this block ever executes. + // If $data['_id'] is empty does the Model call $this->create() instead ?? + syslog(LOG_NOTICE, 'Update B'); + $params = $this->collectionOptions['save']; + try{ + $this->lastResult = $mongoCollectionObj->insertOne($data, $params); + $return = true; + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->useTable}.save( :data, :params )", compact('data', 'params')); + } + } + syslog(LOG_NOTICE, 'Update '.json_encode($this->lastResult)); + + return $return; + } + +/** + * setMongoUpdateOperator + * + * Set Mongo update operator following saving data. + * This method is for update() and updateAll. + * + * @param Model $Model Model Instance + * @param array $values Save data + * @return array $data + * @access public + */ + public function setMongoUpdateOperator(&$Model, $data) { + if (isset($data['updated'])) { + $updateField = 'updated'; + } else { + $updateField = 'modified'; + } + + syslog(LOG_NOTICE, 'setMongoUpdateOperator '.$updateField); + syslog(LOG_NOTICE, "mongoNoSetOperator ".$Model->mongoNoSetOperator); + //setting Mongo operator + if (empty($Model->mongoNoSetOperator)) { + if(!preg_grep('/^\$/', array_keys($data))) { + $data = array('$set' => $data); + syslog(LOG_NOTICE, 'SMUO A'); + } else { + if(!empty($data[$updateField])) { + $modified = $data[$updateField]; + unset($data[$updateField]); + $data['$set'] = array($updateField => $modified); + } + syslog(LOG_NOTICE, 'SMUO B'); + } + } elseif (substr($Model->mongoNoSetOperator,0,1) === '$') { + if(!empty($data[$updateField])) { + $modified = $data[$updateField]; + unset($data[$updateField]); + $data = array($Model->mongoNoSetOperator => $data, '$set' => array($updateField => $modified)); + syslog(LOG_NOTICE, 'SMUO C'); + } else { + $data = array($Model->mongoNoSetOperator => $data); + syslog(LOG_NOTICE, 'SMUO D'); + } + } + syslog(LOG_NOTICE, 'SMUO E'); + + return $data; + } + +/** + * Update multiple documents + * + * @param Model $Model Model Instance + * @param array $fields Field data + * @param array $conditions + * @return boolean Update result + * @access public + */ + public function updateAll(&$Model, $fields = null, $conditions = null) { + if (! $this->isConnected()) { + return false; + } + + $this->_stripAlias($conditions, $Model->alias); + $this->_stripAlias($fields, $Model->alias, false, 'value'); + $fields = $this->setMongoUpdateOperator($Model, $fields); + $this->_prepareLogQuery($Model); + + try { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->updateMany($conditions, $fields); + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + + var_dump($this->lastResult); + syslog(LOG_NOTICE, 'updateAll ' . (! empty($this->lastResult))); + + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.update( :conditions, :fields, :params )", + array('conditions' => $conditions, 'fields' => $fields, 'params' => $this->collectionOptions['update']) + ); + } + return ! empty($this->lastResult); + } + +/** + * deriveSchemaFromData method + * + * @param mixed $Model + * @param array $data array() + * @return void + * @access public + */ + public function deriveSchemaFromData($Model, $data = array()) { + if (!$data) { + $data = $Model->data; + if ($data && array_key_exists($Model->alias, $data)) { + $data = $data[$Model->alias]; + } + } + + $return = $this->_defaultSchema; + + if ($data) { + $fields = array_keys($data); + foreach($fields as $field) { + if (in_array($field, array('created', 'modified', 'updated'))) { + $return[$field] = array('type' => 'datetime', 'null' => true); + } else { + $return[$field] = array('type' => 'string', 'length' => 2000); + } + } + } + + return $return; + } + +/** + * Delete Data + * + * For deleteAll(true, false) calls - conditions will arrive here as true - account for that and + * convert to an empty array + * For deleteAll(array('some conditions')) calls - conditions will arrive here as: + * array( + * Alias._id => array(1, 2, 3, ...) + * ) + * + * This format won't be understood by mongodb, it'll find 0 rows. convert to: + * + * array( + * Alias._id => array('$in' => array(1, 2, 3, ...)) + * ) + * + * @TODO bench remove() v drop. if it's faster to drop - just drop the collection taking into + * account existing indexes (recreate just the indexes) + * @param Model $Model Model Instance + * @param array $conditions + * @return boolean Update result + * @access public + */ + public function delete(Model $Model, $conditions = null) { + if (! $this->isConnected()) { + return false; + } + + $id = null; + $this->_stripAlias($conditions, $Model->alias); + + if ($conditions === true) { + $conditions = array(); + } elseif (empty($conditions)) { + $id = $Model->id; + } elseif (! empty($conditions) && !is_array($conditions)) { + $id = $conditions; + $conditions = array(); + } + + $this->_stripAlias($conditions, $Model->alias); + if (! empty($id)) { + $conditions['_id'] = $id; + } + if (! empty($conditions['_id'])) { + $this->_convertId($conditions['_id'], true); + } + + $return = false; + try { + $this->_prepareLogQuery($Model); + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->deleteMany($conditions); + $count = $this->lastResult->getDeletedCount(); + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.remove( :conditions )", compact('conditions', 'count')); + } + $return = true; + } catch (MongoException $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + return $return; + } + +/** + * Read Data + * + * For deleteAll(true) calls - the conditions will arrive here as true - account for that and switch to an empty array + * + * @param Model $Model Model Instance + * @param array $query Query data + * @return array Results + * @access public + */ + public function read(Model $Model, $query = array(), $recursive = null) { + if (! $this->isConnected()) { + return false; + } + + $this->_setEmptyValues($query); + extract($query); + + if (! empty($order[0])) { + $order = array_shift($order); + } + $this->_stripAlias($conditions, $Model->alias); + $this->_stripAlias($fields, $Model->alias, false, 'value'); + $this->_stripAlias($order, $Model->alias, false, 'both'); + + if (! empty($conditions['_id'])) { + $this->_convertId($conditions['_id']); + } + + $fields = (is_array($fields)) ? $fields : array($fields => 1); + // Check for string keys in $fields array. + // New mongodb driver not happy using field names as array values for projection, + // it wants field names as keys eg. array(field1 => 1 , field2 => 1, field3 => 1) + // So clean that up here. + if (count(array_filter(array_keys($fields), 'is_string')) == 0) { + // No string keys found .. assuming sequential array + $tmp = array(); + foreach($fields as $field) { + $tmp[$field] = 1; + } + $fields = $tmp; + } + + if ($conditions === true) { + $conditions = array(); + } elseif (!is_array($conditions)) { + $conditions = array($conditions); + } + + // TODO : Janky ! Rework. + $order = (is_array($order)) ? $order : array($order); + if (is_array($order)) { + foreach($order as $field => &$dir) { + if (is_numeric($field) || is_null($dir)) { + unset ($order[$field]); + continue; + } + if ($dir && strtoupper($dir) === 'ASC') { + $dir = 1; + continue; + } elseif (!$dir || strtoupper($dir) === 'DESC') { + $dir = -1; + continue; + } + $dir = (int)$dir; + } + } + + if (empty($offset) && $page && $limit) + $offset = ($page - 1) * $limit; + + $return = array(); + $this->_prepareLogQuery($Model); + + $queryType = isset($Model->findQueryType) ? $Model->findQueryType : 'all'; + + if ($queryType === 'count') { + $count = $this->_db + ->selectCollection($Model->table) + ->count($conditions); + + if ($this->fullDebug) + $this->logQuery("db.{$Model->useTable}.count( :conditions )", compact('conditions', 'count')); + + return array(array($Model->alias => array('count' => $count))); + } + + if ($queryType === 'all' || $queryType === 'first') { + $options = array( + 'projection' => $fields, + 'sort' => $order, + 'limit' => $limit, + 'skip' => $offset + ); + if (! empty($this->_findOptions)) + $options = array_merge($options, $this->_findOptions); + + $cursor = $this->_db + ->selectCollection($Model->table) + ->find($conditions, $options); + + $count = 0; + // Iterate over cursor + foreach($cursor as $mongodata) { + if ($this->config['set_string_id'] && ! empty($mongodata['_id']) && is_object($mongodata['_id'])) { + $mongodata['_id'] = $mongodata['_id']->__toString(); + } + $return[][$Model->alias] = $mongodata; + $count++; + } + + if ($this->fullDebug) + $this->logQuery("db.{$Model->table}.find( :conditions, :options )", compact('conditions', 'options', 'count')); + + return $return; + } + + // There was code in the previous version to allow setting a 'modify' flag to execute a findAndModify .. + // I've moved that into a query type. + if ($Model->findQueryType === 'modify') { + $options = array( + 'projection' => $fields, + 'sort' => $order, + 'limit' => $limit, + 'skip' => $offset, + 'returnDocument' => ! empty($new), + 'upsert' => ! empty($upsert), + ); + + // Merge preset options + if (! empty($this->_findOptions)) + $options = array_merge($options, $this->_findOptions); + + // If remove is set then replace the document otherwise update. + if (! empty($remove)) { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->findOneAndReplace($conditions, array('$set' => $modify), $options); + + if ($this->fullDebug) + $logQuery = "db.{$Model->table}.findOneAndReplace( :conditions, :options )"; + } else { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->findOneAndUpdate($conditions, array('$set' => $modify), $options); + + if ($this->fullDebug) + $logQuery = "db.{$Model->table}.findOneAndUpdate( :conditions, :options )"; + } + + $result = MongoDB\BSON\toPHP($this->lastResult); + + $count = 0; + if (! empty($result)) { + $count = 1; + if ($this->config['set_string_id'] && ! empty($result['_id']) && is_object($result['_id'])) { + $result['_id'] = $result['_id']->__toString(); + } + $return[][$Model->alias] = $result; + } + + if ($this->fullDebug) + $this->logQuery($logQuery, compact($conditions, $options, $count)); + + return $return; + } + return $return; + } + +/** + * rollback method + * + * MongoDB doesn't support transactions + * + * @return void + * @access public + */ + public function rollback() { + return false; + } + +/** + * Deletes all the records in a table + * + * @param mixed $table A string or model class representing the table to be truncated + * @return boolean + * @access public + */ + public function truncate($table) { + if (! $this->isConnected()) { + return false; + } + return $this->_db + ->selectCollection($table) + ->deleteMany(); + } + +/** + * query method + * If call getMongoDb() from model, this method call getMongoDb(). + * + * @param mixed $query + * @param array $params array() + * @return void + * @access public + */ + public function query() { + $args = func_get_args(); + $query = $args[0]; + $params = array(); + if (count($args) > 1) { + $params = $args[1]; + } + + if (! $this->isConnected()) + return false; + + if ($query === 'getMongoDb') + return $this->getMongoDb(); + + // Compatibility with previous plugin + if ($query == 'db.version()') { + $doc = $this->query(array('serverStatus' => 1)); + return $doc['version']; + } + + $this->_prepareLogQuery($Model); + $return = array(); + $cursor = $this->_db + ->command($query); + + if (! is_object($cursor)) { + if ($this->fullDebug) { + $this->logQuery("Failed : db.command( :query )", compact('query')); + } + return false; + } + + $count = 0; + // Its a cursor - but is it always only a one document cursor ? + foreach($cursor as $doc) { + syslog(LOG_NOTICE, 'Query : '.json_encode($doc)); + $return = $doc; + $count++; + } + if ($this->fullDebug) { + $this->logQuery("db.command( :query )", compact('query', 'count')); + } + return $return; + } + +/** + * mapReduce + * + * Method maintained for backwards compatibility + * + * @param mixed $query + * @param integer $timeout (milli second) NOTE: Currently ignored. + * @return mixed false or array + * @access public + */ + public function mapReduce($query, $timeout = null) { + return $this->query($query); + } + +/** + * Prepares a value, or an array of values for database queries by quoting and escaping them. + * + * @param mixed $data A value or an array of values to prepare. + * @param string $column The column into which this data will be inserted + * @param boolean $read Value to be used in READ or WRITE context + * @return mixed Prepared value or array of values. + * @access public + */ + public function value($data, $column = null, $read = true) { + $return = parent::value($data, $column, $read); + if ($return === null && $data !== null) { + return $data; + } + return $return; + } + +/** + * execute method + * + * If there is no query or the query is true, execute has probably been called as part of a + * db-agnostic process which does not have a mongo equivalent, don't do anything. + * + * @param mixed $query + * @param array $params array() + * @return void + * @access public + */ + public function execute($query, $options = array(), $params = array()) { + if (! $this->isConnected()) + return false; + + if (! $query || $query === true) + return; + + return $this->query($query, $params); + } + +/** + * Set empty values, arrays or integers, for the variables Mongo uses + * + * @param mixed $data + * @param array $integers array('limit', 'offset') + * @return void + * @access protected + */ + protected function _setEmptyValues(&$data, $integers = array('limit', 'offset')) { + if (! is_array($data)) + return; + foreach($data as $key => $value) { + if (empty($value)) { + $data[$key] = (in_array($key, $integers)) ? 0 : array(); + } + } + } + +/** + * prepareLogQuery method + * + * Any prep work to log a query + * + * @param mixed $Model + * @return void + * @access protected + */ + protected function _prepareLogQuery(&$Model) { + if (! $this->fullDebug) { + return false; + } + $this->_startTime = microtime(true); + $this->took = null; + $this->affected = null; + $this->error = null; + $this->numRows = null; + return true; + } + +/** + * logQuery method + * + * Set timers, errors and refer to the parent + * If there are arguments passed - inject them into the query + * Show MongoIds in a copy-and-paste-into-mongo format + * + * + * @param mixed $query + * @param array $args array() + * @return void + * @access public + */ + public function logQuery($query, $args = array()) { + if ($args) { + $this->_stringify($args); + $query = CakeText::insert($query, $args); + } + $this->took = round((microtime(true) - $this->_startTime) * 1000, 0); + $this->affected = null; + if (empty($this->error['err'])) { + //$this->error = $this->_db->lastError(); + if (!is_scalar($this->error)) { + $this->error = json_encode($this->error); + } + } + $this->numRows = ! empty($args['count']) ? $args['count'] : null; + $query = preg_replace('@"ObjectId\((.*?)\)"@', 'ObjectId ("\1")', $query); + return parent::logQuery($query); + } + +/** + * convertId method + * + * $conditions is used to determine if it should try to auto correct _id => array() queries + * it only appies to conditions, hence the param name + * + * @param mixed $mixed + * @param bool $conditions false + * @return void + * @access protected + */ + protected function _convertId(&$mixed, $conditions = false) { + if (is_string($mixed)) { + if (strlen($mixed) !== 24) { + return; + } + $mixed = new MongoDB\BSON\ObjectID($mixed); + } + if (is_array($mixed)) { + foreach($mixed as &$row) { + $this->_convertId($row, false); + } + if (! empty($mixed[0]) && $conditions) { + $mixed = array('$in' => $mixed); + } + } + } + +/** + * stringify method + * + * Takes an array of args as an input and returns an array of json-encoded strings. Takes care of + * any objects the arrays might be holding (MongoID); + * + * @param array $args array() + * @param int $level 0 internal recursion counter + * @return array + * @access protected + */ + protected function _stringify(&$args = array(), $level = 0) { + foreach($args as &$arg) { + if (is_array($arg)) { + $this->_stringify($arg, $level + 1); + } elseif (is_object($arg) && is_callable(array($arg, '__toString'))) { + $class = get_class($arg); + if ($class === 'MongoId') { + $arg = 'ObjectId(' . $arg->__toString() . ')'; + } elseif ($class === 'MongoRegex') { + $arg = '_regexstart_' . $arg->__toString() . '_regexend_'; + } else { + $arg = $class . '(' . $arg->__toString() . ')'; + } + } + if ($level === 0) { + $arg = json_encode($arg); + if (strpos($arg, '_regexstart_')) { + preg_match_all('@"_regexstart_(.*?)_regexend_"@', $arg, $matches); + foreach($matches[0] as $i => $whole) { + $replace = stripslashes($matches[1][$i]); + $arg = str_replace($whole, $replace, $arg); + } + } + } + } + } + +/** + * Convert automatically array('Model.field' => 'foo') to array('field' => 'foo') + * + * This introduces the limitation that you can't have a (nested) field with the same name as the model + * But it's a small price to pay to be able to use other behaviors/functionality with mongoDB + * + * @param array $args array() + * @param string $alias 'Model' + * @param bool $recurse true + * @param string $check 'key', 'value' or 'both' + * @return void + * @access protected + */ + protected function _stripAlias(&$args = array(), $alias = 'Model', $recurse = true, $check = 'key') { + if (!is_array($args)) { + return; + } + $checkKey = ($check === 'key' || $check === 'both'); + $checkValue = ($check === 'value' || $check === 'both'); + + foreach($args as $key => &$val) { + if ($checkKey) { + if (strpos($key, $alias . '.') === 0) { + unset($args[$key]); + $key = substr($key, strlen($alias) + 1); + $args[$key] = $val; + } + } + if ($checkValue) { + if (is_string($val) && strpos($val, $alias . '.') === 0) { + $val = substr($val, strlen($alias) + 1); + } + } + if ($recurse && is_array($val)) { + $this->_stripAlias($val, $alias, true, $check); + } + } + } +} + +/** + * MongoDbDateFormatter method + * + * This function cannot be in the class because of the way model save is written + * + * @param mixed $date null + * @return void + * @access public + */ +function MongoDbDateFormatter($date = null) { + return ($date) ? new MongoDB\BSON\UTCDateTime($date) : new MongoDB\BSON\UTCDateTime(time()); +} \ No newline at end of file diff --git a/README.markdown b/README.markdown index 18047de..c612b4d 100644 --- a/README.markdown +++ b/README.markdown @@ -1,8 +1,8 @@ -# mongoDB datasource for CakePHP +# MongoDB datasource for CakePHP 2.8+ ## Requirements -PHP5, -pecl mongo (http://php.net/mongo) +PHP5.6, PHP7+ +pecl mongodb (http://php.net/mongo) ## Installation diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php new file mode 100644 index 0000000..a9a5647 --- /dev/null +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -0,0 +1,1622 @@ + array( + 'rule' => 'isUnique', + 'required' => false + ), + 'uniquefield2' => array( + 'rule' => 'manualUniqueValidation', + 'required' => false + ), + ); + + public $mongoSchema = array( + 'title' => array('type' => 'string'), + 'body' => array('type' => 'string'), + 'text' => array('type' => 'text'), + 'uniquefield1' => array('type' => 'text'), + 'uniquefield2' => array('type' => 'text'), + 'count' => array('type' => 'integer'), + 'created' => array('type' => 'datetime'), + 'modified' => array('type' => 'datetime'), + ); + + function manualUniqueValidation($check) { + $c = $this->find('count', array( + 'conditions' => array( + 'uniquefield2' => $check['uniquefield2'] + ) + )); + if ($c === 0) { + return true; + } + return false; + } +} + +/** + * MongoArticle class + * + * @uses AppModel + * @package mongodb + * @subpackage mongodb.tests.cases.datasources + */ +class MongoArticle extends AppModel { + public $useDbConfig = 'mongo_test'; +} + +/** + * MongoDB Source test class + * + * @package app + * @subpackage app.model.datasources + */ +class MongodbSourceTest extends CakeTestCase { + +/** + * Database Instance + * + * @var resource + * @access public + */ + public $mongodb; + +/** + * Base Config + * + * @var array + * @access public + * + */ + protected $_config = array( + 'datasource' => 'MongoDBLib.MongodbSource', + 'host' => 'localhost', + 'login' => '', + 'password' => '', + 'database' => 'test_mongo', + 'port' => 27017, + 'prefix' => '', + 'persistent' => true, + ); + +/** + * Sets up the environment for each test method + * + * @return void + * @access public + */ + public function setUp() { + $connections = ConnectionManager::enumConnectionObjects(); + + if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'mongodbSource') { + $config = new DATABASE_CONFIG(); + $this->_config = $config->test; + } + + ConnectionManager::create('mongo_test', $this->_config); + $this->Mongo = new MongodbSource($this->_config); + + $this->Post = ClassRegistry::init('Post'); + $this->Post->setDataSource('mongo_test'); + + $this->mongodb = ConnectionManager::getDataSource('mongo_test'); + $this->mongodb->connect(); + + $this->dropData(); + + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->setDataSource('mongo_test'); + + syslog(LOG_NOTICE, 'setUp '.$this->getName()); + } + +/** + * Destroys the environment after each test method is run + * + * @return void + * @access public + */ + public function tearDown() { + //$this->dropData(); + //unset($this->Post); + //unset($this->Mongo); + //unset($this->mongodb); + //ClassRegistry::flush(); + } + + +/** + * get Mongod server version + * + * @return numeric + * @access public + */ + public function getMongodVersion() { + $mongo = $this->Post->getDataSource(); + return $mongo->execute('db.version()'); + } + +/** + * Insert data method for mongodb. + * + * @param array insert data + * @return void + * @access public + */ + public function insertData($data) { + try { + $this->mongodb + ->connection + ->selectDatabase($this->_config['database']) + ->selectCollection($this->Post->table) + ->insertOne($data); + } catch (MongoException $e) { + trigger_error($e->getMessage()); + } + } + +/** + * Drop database + * + * @return void + * @access public + */ + public function dropData() { + try { + $db = $this->mongodb + ->connection + ->selectDatabase($this->_config['database']); + + foreach($db->listCollections() as $collection) { + $result = $this->mongodb + ->connection + ->selectDatabase($this->_config['database']) + ->selectCollection($collection->getName()) + ->drop(); + } + } catch (MongoException $e) { + trigger_error($e->getMessage()); + } + } + + +/** + * testCreateConnectionName + * + * @return void + * @access public + */ + public function testCreateConnectionName() { + $config = array( + 'datasource' => 'mongodb', + 'host' => 'localhost', + 'login' => '', + 'password' => '', + 'database' => 'test_mongo', + 'port' => 27017, + 'prefix' => '', + 'persistent' => false, + ); + $expect = 'mongodb://localhost:27017'; + $host = $this->mongodb->createConnectionString($config); + $this->assertIdentical($expect, $host); + + $config = array( + 'datasource' => 'mongodb', + 'host' => 'localhost', + 'login' => 'user', + 'password' => 'pass', + 'database' => 'test_mongo', + 'port' => 27017, + 'prefix' => '', + 'persistent' => false, + ); + $expect = 'mongodb://user:pass@localhost:27017/test_mongo'; + $host = $this->mongodb->createConnectionString($config); + $this->assertIdentical($expect, $host); + } + +/** + * Tests connection + * + * @return void + * @access public + */ + public function testConnect() { + $result = $this->Mongo->connect(); + $this->assertTrue($result); + $this->assertTrue($this->Mongo->connected); + $this->assertTrue($this->Mongo->isConnected()); + } + +/** + * Tests the disconnect method of the Mongodb DataSource + * + * @return void + * @access public + */ + public function testDisconnect() { + $result = $this->Mongo->disconnect(); + $this->assertTrue($result); + $this->assertFalse($this->Mongo->connected); + } + +/** + * Tests the listSources method of the Mongodb DataSource + * + * @return void + * @access public + */ + public function testListSources() { + $this->assertTrue($this->mongodb->listSources()); + } + +/** + * Tests the getMongoDb method of the Mongodb DataSource + * + * @return void + * @access public + */ + public function testGetMongoDb() { + $obj = $this->mongodb->getMongoDb(); + $this->assertTrue(is_object($obj)); + $objName = get_class($obj); + $this->assertEqual('MongoDB\Database', $objName); + } + +/** + * Tests the Model::getMongoDb() call Mongodb::getMongoDb + * + * @return void + * @access public + */ + public function testGetMongoDbFromModel() { + $obj = $this->Post->getMongoDb(); + syslog(LOG_NOTICE, 'testGetMongoDbFromModel :'.print_r($obj, true)); + $this->assertTrue(is_object($obj)); + $objName = get_class($obj); + $this->assertEqual('MongoDB\Database', $objName); + } + +/** + * Tests the getMongoCollection method of the Mongodb DataSource + * + * @return void + * @access public + */ + public function testGetMongoCollection() { + $obj = $this->mongodb->getMongoCollection($this->Post); + $this->assertTrue(is_object($obj)); + $objName = get_class($obj); + $this->assertEqual('MongoDB\Collection', $objName); + } + +/** + * Tests the describe method of the Mongodb DataSource + * + * @return void + * @access public + */ + public function testDescribe() { + $mockObj = $this->getMock('AppModel'); + + $result = $this->mongodb->describe($mockObj); + $expected = array( + '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), + 'created' => array('type' => 'datetime', 'default' => null), + 'modified' => array('type' => 'datetime', 'default' => null), + ); + $this->assertEqual($expected, $result); + + $result = $this->mongodb->describe($this->Post); + $expect = array( + '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), + 'title' => array('type' => 'string'), + 'body' => array('type' => 'string'), + 'text' => array('type' => 'text'), + 'uniquefield1' => array('type' => 'text'), + 'uniquefield2' => array('type' => 'text'), + 'count' => array('type' => 'integer'), + 'created' => array('type' => 'datetime'), + 'modified' => array('type' => 'datetime'), + ); + ksort($result); + ksort($expect); + $this->assertEqual($expect, $result); + } + +/** + * Tests find method. + * + * @return void + * @access public + */ + public function testFind() { + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb' + ); + $this->insertData($data); + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + } + +/** + * Tests save method. + * + * @return void + * @access public + */ + public function testSave() { + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb' + ); + $saveData['Post'] = $data; + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $result = $this->Post->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['Post']; + $this->assertEqual(6, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $this->assertEqual($this->Post->id, $resultData['_id']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + + $this->assertTrue(is_a($resultData['created'], 'MongoDB\BSON\UTCDateTime')); + $this->assertTrue(is_a($resultData['modified'], 'MongoDB\BSON\UTCDateTime')); + } + +/** + * Tests insertId after saving + * + * @return void + * @access public + */ + public function testCheckInsertIdAfterSaving() { + $saveData['Post'] = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $this->assertEqual($this->Post->id, $this->Post->getInsertId()); + $this->assertTrue(is_string($this->Post->id)); + $this->assertTrue(is_string($this->Post->getInsertId())); + + //set Numeric _id + $saveData['Post'] = array( + '_id' => 123456789, + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $this->assertEqual($saveData['Post']['_id'] ,$this->Post->id); + $this->assertEqual($this->Post->id, $this->Post->getInsertId()); + $this->assertTrue(is_numeric($this->Post->id)); + $this->assertTrue(is_numeric($this->Post->getInsertId())); + + $readArray1 = $this->Post->read(); + $readArray2 = $this->Post->read(null, $saveData['Post']['_id']); + $this->assertEqual($readArray1, $readArray2); + $this->assertEqual($saveData['Post']['_id'], $readArray2['Post']['_id']); + + } + +/** + * Tests saveAll method. + * + * @return void + * @access public + */ + public function testSaveAll() { + $saveData[0]['Post'] = array( + 'title' => 'test1', + 'body' => 'aaaa1', + 'text' => 'bbbb1' + ); + + $saveData[1]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2' + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($saveData); + $result = $this->Post->find('all'); + + $this->assertEqual(2, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(6, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $data = $saveData[0]['Post']; + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + + $this->assertTrue(is_a($resultData['created'], 'MongoDB\BSON\UTCDateTime')); + $this->assertTrue(is_a($resultData['modified'], 'MongoDB\BSON\UTCDateTime')); + + $resultData = $result[1]['Post']; + $this->assertEqual(6, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $data = $saveData[1]['Post']; + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + + $this->assertTrue(is_a($resultData['created'], 'MongoDB\BSON\UTCDateTime')); + $this->assertTrue(is_a($resultData['modified'], 'MongoDB\BSON\UTCDateTime')); + } + +/** + * Tests update method. + * + * @return void + * @access public + */ + public function testUpdate() { + $count0 = $this->Post->find('count'); + + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb', + 'count' => 0 + ); + $saveData['Post'] = $data; + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $postId = $this->Post->id; + + $count1 = $this->Post->find('count'); + $this->assertIdentical($count1 - $count0, 1, 'Save failed to create one row'); + + $this->assertNotEmpty($saveResult); + $this->assertNotEmpty($postId); + $findresult = $this->Post->find('all'); + $this->assertEqual(0, $findresult[0]['Post']['count']); + + $updatedata = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2' + ); + $saveData['Post'] = $updatedata; + + $saveResult = $this->Post->save($saveData); + + $count2 = $this->Post->find('count'); + $this->assertIdentical($count2 - $count1, 0, 'Save test 2 created another row, it did not update the existing row'); + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->Post->id, $postId); + + $this->Post->create(); + $updatedata = array( + '_id' => $postId, + 'title' => 'test3', + 'body' => 'aaaa3', + 'text' => 'bbbb3' + ); + $saveData['Post'] = $updatedata; + $saveResult = $this->Post->save($saveData); + + $count3 = $this->Post->find('count'); + $this->assertIdentical($count3 - $count2, 0, 'Saving with the id in the data created another row'); + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->Post->id, $postId); + + $this->Post->create(); + $this->Post->id = $postId; + $updatedata = array( + 'title' => 'test4', + 'body' => 'aaaa4', + 'text' => 'bbbb4' + ); + $saveData['Post'] = $updatedata; + $saveResult = $this->Post->save($saveData); + + $count4 = $this->Post->find('count'); + $this->assertIdentical($count4 - $count3, 0, 'Saving with $Model->id set and no id in the data created another row'); + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->Post->id, $postId); + + $result = $this->Post->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['Post']; + $this->assertEqual(7, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $this->assertEqual($this->Post->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); + $this->assertEqual($updatedata['body'], $resultData['body']); + $this->assertEqual($updatedata['text'], $resultData['text']); + $this->assertEqual(0, $resultData['count']); + + // using $inc operator + $this->Post->mongoNoSetOperator = '$inc'; + $this->Post->create(); + $updatedataIncrement = array( + '_id' => $postId, + 'count' => 1, + ); + $saveData['Post'] = $updatedataIncrement; + $saveResult = $this->Post->save($saveData); + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->Post->id, $postId); + + $result = $this->Post->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['Post']; + $this->assertEqual(7, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $this->assertEqual($this->Post->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); //not update + $this->assertEqual($updatedata['body'], $resultData['body']); //not update + $this->assertEqual($updatedata['text'], $resultData['text']); //not update + $this->assertEqual(1, $resultData['count']); //increment + unset($this->Post->mongoNoSetOperator); + } + + +/** + * Tests updateAll method. + * + * @return void + * @access public + */ + public function testUpdateAll() { + $saveData[0]['Post'] = array( + 'title' => 'test', + 'name' => 'ichi', + 'body' => 'aaaa1', + 'text' => 'bbbb1' + ); + + $saveData[1]['Post'] = array( + 'title' => 'test', + 'name' => 'ichi', + 'body' => 'aaaa2', + 'text' => 'bbbb2' + ); + + $this->Post->create(); + $this->Post->saveAll($saveData); + + $updateData = array('name' => 'ichikawa'); + $conditions = array('title' => 'test'); + + $resultUpdateAll = $this->Post->updateAll($updateData, $conditions); + $this->assertTrue($resultUpdateAll); + + $result = $this->Post->find('all'); + $this->assertEqual(2, count($result)); + $resultData = $result[0]['Post']; + $this->assertEqual(7, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $data = $saveData[0]['Post']; + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual('ichikawa', $resultData['name']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + $this->assertTrue(is_a($resultData['created'], 'MongoDB\BSON\UTCDateTime')); + $this->assertTrue(is_a($resultData['modified'], 'MongoDB\BSON\UTCDateTime')); + + + $resultData = $result[1]['Post']; + $this->assertEqual(7, count($resultData)); + $this->assertTrue(!empty($resultData['_id'])); + $data = $saveData[1]['Post']; + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual('ichikawa', $resultData['name']); + $this->assertEqual($data['body'], $resultData['body']); + $this->assertEqual($data['text'], $resultData['text']); + $this->assertTrue(is_a($resultData['created'], 'MongoDB\BSON\UTCDateTime')); + $this->assertTrue(is_a($resultData['modified'], 'MongoDB\BSON\UTCDateTime')); + } + +/** + * Tests updateAll method. + * + * @return void + * @access public + */ + public function testSetMongoUpdateOperator() { + + $ds = $this->Post->getDataSource(); + + //normal + $data = array('title' => 'test1', 'name' => 'ichikawa'); + $expect = array('$set' => array('title' => 'test1', 'name' => 'ichikawa')); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //using $inc + $data = array('title' => 'test1', 'name' => 'ichikawa', '$inc' => array('count' => 1)); + $expect = array('title' => 'test1', 'name' => 'ichikawa', '$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //using $inc and modified + $data = array('modified' => '2011/8/1', '$inc' => array('count' => 1)); + $expect = array('$set' => array('modified' => '2011/8/1'), '$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //using $inc and updated + $data = array('updated' => '2011/8/1', '$inc' => array('count' => 1)); + $expect = array('$set' => array('updated' => '2011/8/1'), '$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //using $inc, $push and modified + $data = array('$push' => array('tag' => 'tag1'), 'modified' => '2011/8/1', '$inc' => array('count' => 1)); + $expect = array('$push' => array('tag' => 'tag1'),'$set' => array('modified' => '2011/8/1'), '$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //mongoNoSetOperator is true, + // using $inc, $push and modified + $this->Post->mongoNoSetOperator = true; + $data = array('$push' => array('tag' => 'tag1'), 'modified' => '2011/8/1', '$inc' => array('count' => 1)); + $expect = array('$push' => array('tag' => 'tag1'),'modified' => '2011/8/1', '$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + + //mongoNoSetOperator is $inc, + $this->Post->mongoNoSetOperator = '$inc'; + $data = array('count' => 1); + $expect = array('$inc' => array('count' => 1)); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + + //mongoNoSetOperator is $inc, + // with modified field + $this->Post->mongoNoSetOperator = '$inc'; + $data = array('count' => 1, 'modified' => '2011/8/1'); + $expect = array('$inc' => array('count' => 1),'$set' => array('modified' => '2011/8/1')); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + //mongoNoSetOperator is $inc, + // with updated field + $this->Post->mongoNoSetOperator = '$inc'; + $data = array('count' => 1, 'updated' => '2011/8/1'); + $expect = array('$inc' => array('count' => 1),'$set' => array('updated' => '2011/8/1')); + $result = $ds->setMongoUpdateOperator($this->Post, $data); + $this->assertEqual($expect, $result); + + $this->Post->mongoNoSetOperator = null; + } + +/** + * Tests update method without $set operator. + * + * @return void + * @access public + */ + public function testUpdateWithoutMongoSchemaProperty() { + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + + syslog(LOG_NOTICE, "MongoArticle ".json_encode($this->MongoArticle)); + + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb', + 'count' => 0, + 'created' => new mongoDate(), + 'modified' => new mongoDate(), + ); + $saveData['MongoArticle'] = $data; + + $this->MongoArticle->create(); + $saveResult = $this->MongoArticle->save($saveData); + $postId = $this->MongoArticle->id; + + //using $set operator + $this->MongoArticle->create(); + $updatedata = array( + '_id' => $postId, + 'title' => 'test3', + 'body' => 'aaaa3', + ); + $saveData['MongoArticle'] = $updatedata; + $saveResult = $this->MongoArticle->save($saveData); // using $set operator + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->MongoArticle->id, $postId); + + $result = null; + $result = $this->MongoArticle->find('all'); + + print_r($result); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual($this->MongoArticle->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); //update + $this->assertEqual($updatedata['body'], $resultData['body']); //update + $this->assertEqual($data['text'], $resultData['text']); //not update + $this->assertEqual($data['count'], $resultData['count']); //not update + + //using $inc operator insted of $set operator + $this->MongoArticle->create(); + $updatedataInc = array( + '_id' => $postId, + '$inc' => array('count' => 1), + ); + $saveData['MongoArticle'] = $updatedataInc; + $saveResult = $this->MongoArticle->save($saveData); // using $set operator + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->MongoArticle->id, $postId); + $result = null; + $result = $this->MongoArticle->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual($this->MongoArticle->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); //not update + $this->assertEqual($updatedata['body'], $resultData['body']); //not update + $this->assertEqual($data['text'], $resultData['text']); //not update + $this->assertEqual(1, $resultData['count']); //increment + + //using $inc and $push + $this->MongoArticle->create(); + $updatedataInc = array( + '_id' => $postId, + '$push' => array( + 'comments' => array( + '_id' => new MongoDB\BSON\ObjectID(), + 'created' => new MongoDB\BSON\UTCDateTime(time()), + 'vote_count' => 0, + 'user' => 'user1', + 'body' => 'comment', + ) + ), + '$inc' => array('count' => 1), + ); + $saveData['MongoArticle'] = $updatedataInc; + $saveResult = $this->MongoArticle->save($saveData); // using $set operator + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->MongoArticle->id, $postId); + $result = null; + $result = $this->MongoArticle->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual($this->MongoArticle->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); //not update + $this->assertEqual($updatedata['body'], $resultData['body']); //not update + $this->assertEqual($data['text'], $resultData['text']); //not update + $this->assertEqual(2, $resultData['count']); //increment + $this->assertEqual('user1', $resultData['comments'][0]['user']); //push + $this->assertEqual('comment', $resultData['comments'][0]['body']); //push + $this->assertEqual(1, count($resultData['comments'])); //push + $this->assertTrue(!empty($resultData['created'])); + $this->assertTrue(!empty($resultData['modified'])); + + //no $set operator + $this->MongoArticle->mongoNoSetOperator = true; + + $this->MongoArticle->create(); + $updatedata = array( + '_id' => $postId, + 'title' => 'test4', + 'body' => 'aaaa4', + 'count' => '1', + ); + $saveData['MongoArticle'] = $updatedata; + $saveResult = $this->MongoArticle->save($saveData); + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->MongoArticle->id, $postId); + + $result = null; + $result = $this->MongoArticle->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual($this->MongoArticle->id, $resultData['_id']); + $this->assertEqual($updatedata['title'], $resultData['title']); //update + $this->assertEqual($updatedata['body'], $resultData['body']); //update + $this->assertTrue(empty($resultData['text'])); + $this->assertEqual(1, $resultData['count']); + + $this->MongoArticle->mongoNoSetOperator = null; + + //use $push + $this->MongoArticle->create(); + $updatedata = array( + '_id' => $postId, + 'push_column' => array('push1'), + ); + $saveData['MongoArticle'] = $updatedata; + $saveResult = $this->MongoArticle->save($saveData); //use $set + + $result = $this->MongoArticle->find('all'); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual('test4', $resultData['title']); // no update + $this->assertEqual(array('push1'), $resultData['push_column']); + + $this->MongoArticle->mongoNoSetOperator = '$push'; + $this->MongoArticle->create(); + $updatedata = array( + '_id' => $postId, + 'push_column' => 'push2', + ); + $saveData['MongoArticle'] = $updatedata; + $saveResult = $this->MongoArticle->save($saveData); //use $push + + $this->assertNotEmpty($saveResult); + $this->assertIdentical($this->MongoArticle->id, $postId); + + $result = null; + $result = $this->MongoArticle->find('all'); + + $this->assertEqual(1, count($result)); + $resultData = $result[0]['MongoArticle']; + $this->assertEqual($this->MongoArticle->id, $resultData['_id']); + $this->assertEqual('test4', $resultData['title']); // no update + $this->assertEqual(array('push1','push2'), $resultData['push_column']); //update + + $this->MongoArticle->mongoNoSetOperator = null; + + unset($this->MongoArticle); + } + + +/** + * Tests groupBy + * + * @return void + * @access public + */ + public function testGroupBy() { + for ($i = 0 ; $i < 30 ; $i++) { + $saveData[$i]['Post'] = array( + 'title' => 'test'.$i, + 'body' => 'aaaa'.$i, + 'text' => 'bbbb'.$i, + 'count' => $i, + ); + } + + $saveData[30]['Post'] = array( + 'title' => 'test1', + 'body' => 'aaaa1', + 'text' => 'bbbb1', + 'count' => 1, + ); + $saveData[31]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2', + 'count' => 2, + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($saveData); + + $cond_count = 5; + $query = array( + 'key' => array('title' => true ), + 'initial' => array('csum' => 0), + 'reduce' => 'function(obj, result){result.csum += 1;}', + 'conditions' => array('count' => array('$lt' => $cond_count)), + ); + + $mongo = $this->Post->getDataSource(); + $result = $mongo->group($query, $this->Post); + + $this->assertTrue($result['ok'] == 1 && count($result['retval']) > 0); + $this->assertEqual($cond_count, count($result['retval'])); + $this->assertEqual('test0', $result['retval'][0]['title']); + $this->assertEqual('test1', $result['retval'][1]['title']); + $this->assertEqual('test2', $result['retval'][2]['title']); + $this->assertEqual(1, $result['retval'][0]['csum']); + $this->assertEqual(2, $result['retval'][1]['csum']); + $this->assertEqual(2, $result['retval'][2]['csum']); + } + +/** + * Tests query + * Distinct, Group + * + * @return void + * @access public + */ + public function testQuery() { + for($i = 0 ; $i < 30 ; $i++) { + $saveData[$i]['Post'] = array( + 'title' => 'test'.$i, + 'body' => 'aaaa'.$i, + 'text' => 'bbbb'.$i, + 'count' => $i, + ); + } + + $saveData[30]['Post'] = array( + 'title' => 'test1', + 'body' => 'aaaa1', + 'text' => 'bbbb1', + 'count' => 1, + ); + $saveData[31]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2', + 'count' => 2, + ); + + $saveData[32]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2', + 'count' => 32, + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($saveData); + + //using query() Distinct + $params = array( + 'distinct' => 'posts', + 'key' => 'count', + ); + $result = $this->Post->query( $params ); + $this->assertEqual(1, $result['values'][1]); + $this->assertEqual(2, $result['values'][2]); + $this->assertEqual(32, $result['values'][30]); + + //using query() group + $cond_count = 5; + $reduce = "function(obj,prev){prev.csum++;}"; + $params = array( + 'group' => array( + 'ns' => 'posts', + 'cond' => array('count' => array('$lt' => $cond_count)), + 'key' => array('title' => true), + 'initial' => array('csum' => 0), + '$reduce' => $reduce + ) + ); + + $result = $this->Post->query( $params ); + + $this->assertTrue($result['ok'] == 1 && count($result['retval']) > 0); + $this->assertEqual($cond_count, count($result['retval'])); + $this->assertEqual('test0', $result['retval'][0]['title']); + $this->assertEqual('test1', $result['retval'][1]['title']); + $this->assertEqual('test2', $result['retval'][2]['title']); + $this->assertEqual(1, $result['retval'][0]['csum']); + $this->assertEqual(2, $result['retval'][1]['csum']); + $this->assertEqual(2, $result['retval'][2]['csum']); + } + +/** + * Tests MapReduce + * + * @return void + * @access public + */ + public function testMapReduce() { + for ($i = 0 ; $i < 30 ; $i++) { + $saveData[$i]['Post'] = array( + 'title' => 'test'.$i, + 'body' => 'aaaa'.$i, + 'text' => 'bbbb'.$i, + 'count' => $i, + ); + } + + $saveData[30]['Post'] = array( + 'title' => 'test1', + 'body' => 'aaaa1', + 'text' => 'bbbb1', + 'count' => 1, + ); + + $saveData[31]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2', + 'count' => 2, + ); + + $saveData[32]['Post'] = array( + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2', + 'count' => 32, + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($saveData); + + $map = new MongoDB\BSON\Javascript("function() { emit(this.title,1); }"); + $reduce = new MongoDB\BSON\Javascript("function(k, vals) { ". + "var sum = 0;". + "for (var i in vals) {". + "sum += vals[i];". + "}". + "return sum; }" + ); + + // Results output to collection. + $params = array( + "mapReduce" => "posts", + "map" => $map, + "reduce" => $reduce, + "query" => array( + "count" => array('$gt' => -2), + ), + 'out' => 'test_mapreduce_posts', + ); + + $mongo = $this->Post->getDataSource(); + $reply = $mongo->mapReduce($params); + $this->assertEqual($reply['result'], 'test_mapreduce_posts'); + $this->assertEqual($reply['ok'], 1); + + // Results inline + $params = array( + "mapReduce" => "posts", + "map" => $map, + "reduce" => $reduce, + "query" => array( + "count" => array('$gt' => -2), + ), + 'out' => array('inline' => 1), + ); + + $reply = $this->Post->query($params); + $this->assertTrue(is_array($reply['results'])); + $this->assertEqual($reply['ok'], 1); + + $posts = array(); + foreach ($reply['results'] as $post) { + $posts[$post['_id']] = $post['value']; + } + + $this->assertEqual(30, count($posts)); + $this->assertEqual(1, $posts['test0']); + $this->assertEqual(2, $posts['test1']); + $this->assertEqual(3, $posts['test2']); + $this->assertEqual(1, $posts['test3']); + } + +/** + * testSort method + * + * @return void + * @access public + */ + public function testSort() { + $data = array( + 'title' => 'AAA', + 'body' => 'aaaa', + 'text' => 'aaaa' + ); + $saveData['Post'] = $data; + $this->Post->create(); + $this->Post->save($saveData); + + $data = array( + 'title' => 'CCC', + 'body' => 'cccc', + 'text' => 'cccc' + ); + $saveData['Post'] = $data; + $this->Post->create(); + $this->Post->save($saveData); + + $this->Post->create(); + $data = array( + 'title' => 'BBB', + 'body' => 'bbbb', + 'text' => 'bbbb' + ); + $saveData['Post'] = $data; + $this->Post->create(); + $this->Post->save($saveData); + + $expected = array('AAA', 'BBB', 'CCC'); + $result = $this->Post->find('all', array( + 'fields' => array('_id', 'title'), + 'order' => array('title' => 1) + )); + $result = Set::extract($result, '/Post/title'); + + $this->assertEqual($expected, $result); + $result = $this->Post->find('all', array( + 'fields' => array('_id', 'title'), + 'order' => array('title' => 'ASC') + )); + $result = Set::extract($result, '/Post/title'); + + $expected = array_reverse($expected); + $result = $this->Post->find('all', array( + 'fields' => array('_id', 'title'), + 'order' => array('title' => '-1') + )); + $result = Set::extract($result, '/Post/title'); + $this->assertEqual($expected, $result); + + $result = $this->Post->find('all', array( + 'fields' => array('_id', 'title'), + 'order' => array('title' => 'DESC') + )); + $result = Set::extract($result, '/Post/title'); + $this->assertEqual($expected, $result); + } + +/** + * testSchemaless method + * + * Test you can save to a model without specifying mongodb. + * + * @return void + * @access public + */ + public function testSchemaless() { + $toSave = array( + 'title' => 'A test article', + 'body' => str_repeat('Lorum ipsum ', 100), + 'tags' => array( + 'one', + 'two', + 'three' + ), + 'modified' => null, + 'created' => null + ); + + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->create(); + $this->assertNotEmpty($this->MongoArticle->save($toSave), 'Saving with no defined schema failed'); + + $expected = array_intersect_key($toSave, array_flip(array('title', 'body', 'tags'))); + $result = $this->MongoArticle->read(array('title', 'body', 'tags')); + syslog(LOG_NOTICE, 'Schemaless read '.json_encode(array_keys($result['MongoArticle']))); + unset ($result['MongoArticle']['_id']); // prevent auto added field from screwing things up + $this->assertEqual($expected, $result['MongoArticle']); + + $toSave = array( + 'title' => 'Another test article', + 'body' => str_repeat('Lorum pipsum ', 100), + 'tags' => array( + 'four', + 'five', + 'six' + ), + 'starts' => date('Y-M-d H:i:s'), + 'modified' => null, + 'created' => null + ); + $this->MongoArticle->create(); + $this->assertNotEmpty($this->MongoArticle->save($toSave), 'Saving with no defined schema failed'); + $starts = $this->MongoArticle->field('starts'); + $this->assertEqual($toSave['starts'], $starts); + } + +/** + * testSpecificId method + * + * Test you can save specifying your own _id values - and update by _id + * + * @return void + * @access public + */ + public function testSpecificId() { + $data = array( + '_id' => 123, + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb' + ); + $saveData['Post'] = $data; + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $found = $this->Post->find('first', array( + 'fields' => array('_id', 'title', 'body', 'text'), + 'conditions' => array('_id' => 123) + )); + + $this->assertEqual($found, $saveData); + + $data = array( + '_id' => 123, + 'title' => 'test2', + 'body' => 'aaaa2', + 'text' => 'bbbb2' + ); + $saveData['Post'] = $data; + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $found = $this->Post->find('first', array( + 'fields' => array('_id', 'title', 'body', 'text'), + 'conditions' => array('_id' => 123) + )); + $this->assertEqual($found, $saveData); + } + +/** + * testOr method + * + * @return void + * @access public + */ + public function testOr() { + $mongoVersion = $this->mongodb->execute('db.version()'); + $shouldSkip = version_compare($mongoVersion, '1.5.3', '<'); + if ($this->skipIf($shouldSkip, '$or tests require at least version mongo version 1.5.3, currently using ' . $mongoVersion . ' %s')) { + return; + } + + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->create(); + + for ($i = 1; $i <= 20; $i++) { + $data = array( + 'title' => "Article $i", + 'subtitle' => "Sub Article $i", + ); + $saveData['MongoArticle'] = $data; + $this->MongoArticle->create(); + $this->MongoArticle->save($saveData); + } + $expected = $this->MongoArticle->find('all', array( + 'conditions' => array( + 'title' => array('$in' => array('Article 1', 'Article 10')) + ), + 'order' => array('number' => 'ASC') + )); + $this->assertEqual(count($expected), 2); + + $result = $this->MongoArticle->find('all', array( + 'conditions' => array( + '$or' => array( + array('title' => 'Article 1'), + array('title' => 'Article 10'), + ) + ), + 'order' => array('number' => 'ASC') + )); + $this->assertEqual($result, $expected); + } + +/** + * testDeleteAll method + * + * @return void + * @access public + */ + function testDeleteAll($cascade = true) { + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + + $this->MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); + $this->MongoArticle->save(); + $this->MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); + $this->MongoArticle->save(); + $this->MongoArticle->create(array('title' => 'Article 3', 'cat' => 2)); + $this->MongoArticle->save(); + $this->MongoArticle->create(array('title' => 'Article 4', 'cat' => 2)); + $this->MongoArticle->save(); + + $count = $this->MongoArticle->find('count'); + $this->assertEqual($count, 4); + + $this->MongoArticle->deleteAll(array('cat' => 2), $cascade); + $count = $this->MongoArticle->find('count'); + $this->assertEqual($count, 2); + + $this->MongoArticle->deleteAll(true, $cascade); + $count = $this->MongoArticle->find('count'); + $this->assertEqual($count, 0); + } + +/** + * testDeleteAllNoCascade method + * + * @return void + * @access public + */ + function testDeleteAllNoCascade() { + $this->testDeleteAll(false); + } + +/** + * testRegexSearch method + * + * @return void + * @access public + */ + public function testRegexSearch() { + + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); + $this->MongoArticle->save(); + + $this->MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); + $this->MongoArticle->save(); + + $this->MongoArticle->create(array('title' => 'Article 3', 'cat' => 2)); + $this->MongoArticle->save(); + + $count = $this->MongoArticle->find('count', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertEqual($count, 1); + + $count = $this->MongoArticle->find('count', array( + 'conditions' => array( + 'title' => new MongoDB\BSON\Regex('^Article', '') + ) + )); + $this->assertEqual($count, 3); + } + +/** + * testEmptyReturn method + * inserts article into table. searches for a different non existing article. should return an empty array in the same that that it does from other datasources + * @return void + * @access public + */ + public function testEmptyReturn() { + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); + $this->MongoArticle->save(); + $articles = $this->MongoArticle->find('all', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($articles)); + + $articles = $this->MongoArticle->find('first', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + // http://www.codeproject.com/Articles/987758/Changes-to-Model-find-first-in-CakePHP + // find('first') with not matching conditions resutns array(). + $this->assertTrue(is_array($articles)); + $this->assertEmpty($articles); + } + +/** + * Tests isUnique validation. + * + * @return void + * @access public + */ + public function testSaveUniques() { + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb', + 'uniquefield1'=>'uniquenameforthistest' + ); + $saveData['Post'] = $data; + + $this->Post->Behaviors->attach('MongoDBLib.SqlCompatible'); + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + + $data = array( + 'title' => 'test', + 'body' => 'asdf', + 'text' => 'bbbb', + 'uniquefield1'=>'uniquenameforthistest' + ); + $saveData['Post'] = $data; + + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertFalse($saveResult); + } + +/** + * Tests isUnique validation with custom validation. + * + * @return void + * @access public + */ + public function testSaveUniquesCustom() { + $data = array( + 'title' => 'test', + 'body' => 'aaaa', + 'text' => 'bbbb', + 'uniquefield2' => 'someunqiuename' + ); + $saveData['Post'] = $data; + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertNotEmpty($saveResult); + $data = array( + 'title' => 'test', + 'body' => 'asdf', + 'text' => 'bbbb', + 'uniquefield2' => 'someunqiuename' + ); + $saveData['Post'] = $data; + $this->Post->create(); + $saveResult = $this->Post->save($saveData); + $this->assertFalse($saveResult); + } + + public function testReturn() { + $this->MongoArticle = ClassRegistry::init('MongoArticle'); + $this->MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); + $this->MongoArticle->save(); + $this->MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); + $this->MongoArticle->save(); + + $return = $this->MongoArticle->find('all', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('first', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('first', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('count', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_int($return)); + + $return = $this->MongoArticle->find('neighbors', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('list', array( + 'conditions' => array( + 'title' => 'Article 2' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('all', array( + 'conditions' => array( + 'title' => 'Doesn\'t exist' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('first', array( + 'conditions' => array( + 'title' => 'Doesn\'t exist' + ) + )); + $this->assertTrue(is_array($return)); + $this->assertEmpty($return); + + $return = $this->MongoArticle->find('count', array( + 'conditions' => array( + 'title' => 'Doesn\'t exist' + ) + )); + $this->assertTrue(is_int($return)); + $this->assertEqual(0, $return); + + $return = $this->MongoArticle->find('neighbors', array( + 'conditions' => array( + 'title' => 'Doesn\'t exist' + ) + )); + $this->assertTrue(is_array($return)); + + $return = $this->MongoArticle->find('list', array( + 'conditions' => array( + 'title' => 'Doesn\'t exist' + ) + )); + $this->assertTrue(is_array($return)); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6bc1b31 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "HandsetDetection/MongoDBLib", + "description": "A CakePHP Driver from MongoDB using the new mongodb extension. For CakePHP 2.8+ (Not CakePHP 3.x)", + "homepage": "https://github.com/HandsetDetection/MongoDBLib", + "keywords": ["cakephp", "mongodb", "php", "mongo"], + "minimum-stability": "dev", + "license": "MIT", + "type": "cakephp-plugin", + "require": { + "php": ">=5.6", + "mongodb/mongodb": ">=1.0.0", + }, + "require-dev": { + "phpunit/phpunit": "4.0.*" + }, + "authors": [ + { + "name": "Richard Uren", + "email": "elricho66@gmail.com", + "homepage": "http://www.handsetdetection.com/" + } + ] +} \ No newline at end of file From c1a6c8b67efe8c19c6fd5e94625a7e7aa7247ac0 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 08:55:22 +1100 Subject: [PATCH 05/33] Remove outdated directories. --- models/behaviors/schemaless.php | 89 - models/behaviors/sql_compatible.php | 270 --- models/datasources/mongodb_source.php | 1444 -------------- tests/cases/1st/first_mongodb_source.test.php | 171 -- tests/cases/behaviors/sql_compatible.test.php | 314 ---- .../cases/datasources/mongodb_source.test.php | 1673 ----------------- 6 files changed, 3961 deletions(-) delete mode 100644 models/behaviors/schemaless.php delete mode 100644 models/behaviors/sql_compatible.php delete mode 100644 models/datasources/mongodb_source.php delete mode 100644 tests/cases/1st/first_mongodb_source.test.php delete mode 100644 tests/cases/behaviors/sql_compatible.test.php delete mode 100644 tests/cases/datasources/mongodb_source.test.php diff --git a/models/behaviors/schemaless.php b/models/behaviors/schemaless.php deleted file mode 100644 index 03fb696..0000000 --- a/models/behaviors/schemaless.php +++ /dev/null @@ -1,89 +0,0 @@ -settings[$Model->alias] = array_merge($this->_defaultSettings, $config); - } - -/** - * beforeSave method - * - * Set the schema to allow saving whatever has been passed - * - * @param mixed $Model - * @return void - * @access public - */ - public function beforeSave(&$Model) { - $Model->cacheSources = false; - $Model->schema(true); - return true; - } -} \ No newline at end of file diff --git a/models/behaviors/sql_compatible.php b/models/behaviors/sql_compatible.php deleted file mode 100644 index 61e23ec..0000000 --- a/models/behaviors/sql_compatible.php +++ /dev/null @@ -1,270 +0,0 @@ - true, - 'operators' => array( - '!=' => '$ne', - '>' => '$gt', - '>=' => '$gte', - '<' => '$lt', - '<=' => '$lte', - 'IN' => '$in', - 'NOT' => '$not', - 'NOT IN' => '$nin' - ) - ); - -/** - * setup method - * - * Allow overriding the operator map - * - * @param mixed $Model - * @param array $config array() - * @return void - * @access public - */ - public function setup(&$Model, $config = array()) { - $this->settings[$Model->alias] = array_merge($this->_defaultSettings, $config); - } - -/** - * If requested, convert dates from MongoDate objects to standard date strings - * - * @param mixed $Model - * @param mixed $results - * @param mixed $primary - * @return void - * @access public - */ - public function afterFind(&$Model, $results, $primary) { - if ($this->settings[$Model->alias]['convertDates']) { - $this->convertDates($results); - } - return $results; - } - -/** - * beforeFind method - * - * If conditions are an array ensure they are mongified - * - * @param mixed $Model - * @param mixed $query - * @return void - * @access public - */ - public function beforeFind(&$Model, $query) { - if (is_array($query['conditions']) && $this->_translateConditions($Model, $query['conditions'])) { - return $query; - } - return true; - } - -/** - * Convert MongoDate objects to strings for the purpose of view simplicity - * - * @param mixed $results - * @return void - * @access public - */ - public function convertDates(&$results) { - if (is_array($results)) { - foreach($results as &$row) { - $this->convertDates($row); - } - } elseif (is_a($results, 'MongoDate')) { - $results = date('Y-M-d h:i:s', $results->sec); - } - } - -/** - * translateConditions method - * - * Loop on conditions and desqlify them - * - * @param mixed $Model - * @param mixed $conditions - * @return void - * @access protected - */ - protected function _translateConditions(&$Model, &$conditions) { - $return = false; - foreach($conditions as $key => &$value) { - $uKey = strtoupper($key); - if (substr($uKey, -5) === 'NOT IN') { - // 'Special' case because it has a space in it, and it's the whole key - $conditions[substr($key, 0, -5)]['$nin'] = $value; - unset($conditions[$key]); - $return = true; - continue; - } - if ($uKey === 'OR') { - unset($conditions[$key]); - foreach($value as $key => $part) { - $part = array($key => $part); - $this->_translateConditions($Model, $part); - $conditions['$or'][] = $part; - } - $return = true; - continue; - } - if ($key === $Model->primaryKey && is_array($value)) { - //_id=>array(1,2,3) pattern, set $in operator - $isMongoOperator = false; - foreach($value as $idKey => $idValue) { - //check a mongo operator exists - if(substr($idKey,0,1) === '$') { - $isMongoOperator = true; - continue; - } - } - unset($idKey, $idValue); - if($isMongoOperator === false) { - $conditions[$key] = array('$in' => $value); - } - $return = true; - continue; - } - if (substr($uKey, -3) === 'NOT') { - // 'Special' case because it's awkward - $childKey = key($value); - $childValue = current($value); - - if (in_array(substr($childKey, -1), array('>', '<', '='))) { - $parts = explode(' ', $childKey); - $operator = array_pop($parts); - if ($operator = $this->_translateOperator($Model, $operator)) { - $childKey = implode(' ', $parts); - } - } else { - $conditions[$childKey]['$nin'] = (array)$childValue; - unset($conditions['NOT']); - $return = true; - continue; - } - - $conditions[$childKey]['$not'][$operator] = $childValue; - unset($conditions['NOT']); - $return = true; - continue; - } - if (substr($uKey, -5) === ' LIKE') { - // 'Special' case because it's awkward - if ($value[0] === '%') { - $value = substr($value, 1); - } else { - $value = '^' . $value; - } - if (substr($value, -1) === '%') { - $value = substr($value, 0, -1); - } else { - $value .= '$'; - } - $value = str_replace('%', '.*', $value); - - $conditions[substr($key, 0, -5)] = new MongoRegex("/$value/i"); - unset($conditions[$key]); - $return = true; - continue; - } - - if (!in_array(substr($key, -1), array('>', '<', '='))) { - $return = true; - continue; - } - if (is_numeric($key && is_array($value))) { - if ($this->_translateConditions($Model, $value)) { - $return = true; - continue; - } - } - $parts = explode(' ', $key); - $operator = array_pop($parts); - if ($operator = $this->_translateOperator($Model, $operator)) { - $newKey = implode(' ', $parts); - $conditions[$newKey][$operator] = $value; - unset($conditions[$key]); - $return = true; - } - if (is_array($value)) { - if ($this->_translateConditions($Model, $value)) { - $return = true; - continue; - } - } - } - return $return; - } - -/** - * translateOperator method - * - * Use the operator map for the model and return what the db really wants to hear - * - * @param mixed $Model - * @param mixed $operator - * @return string - * @access protected - */ - protected function _translateOperator($Model, $operator) { - if (!empty($this->settings[$Model->alias]['operators'][$operator])) { - return $this->settings[$Model->alias]['operators'][$operator]; - } - return ''; - } -} diff --git a/models/datasources/mongodb_source.php b/models/datasources/mongodb_source.php deleted file mode 100644 index e21c4d3..0000000 --- a/models/datasources/mongodb_source.php +++ /dev/null @@ -1,1444 +0,0 @@ - true, - 'persistent' => true, - 'host' => 'localhost', - 'database' => '', - 'port' => '27017', - 'login' => '', - 'password' => '', - 'replicaset' => '' - ); - -/** - * collection options - * - * set collection options for various mongo write operations. - * options can be found in the php manual - * http://www.php.net/manual/en/mongocollection.save.php - * http://www.php.net/manual/en/mongocollection.insert.php - * http://www.php.net/manual/en/mongocollection.batchinsert.php - * http://www.php.net/manual/en/mongocollection.update.php - * - * @var array - */ - - public $collectionOptions = array( - 'save' => array(), - 'insert' => array(), - 'batchInsert' => array(), - 'update' => array() - ); - -/** - * column definition - * - * @var array - */ - public $columns = array( - 'boolean' => array('name' => 'boolean'), - 'string' => array('name' => 'varchar'), - 'text' => array('name' => 'text'), - 'integer' => array('name' => 'integer', 'format' => null, 'formatter' => 'intval'), - 'float' => array('name' => 'float', 'format' => null, 'formatter' => 'floatval'), - 'datetime' => array('name' => 'datetime', 'format' => null, 'formatter' => 'MongodbDateFormatter'), - 'timestamp' => array('name' => 'timestamp', 'format' => null, 'formatter' => 'MongodbDateFormatter'), - 'time' => array('name' => 'time', 'format' => null, 'formatter' => 'MongodbDateFormatter'), - 'date' => array('name' => 'date', 'format' => null, 'formatter' => 'MongodbDateFormatter'), - ); - -/** - * Default schema for the mongo models - * - * @var array - * @access protected - */ - protected $_defaultSchema = array( - '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), - 'created' => array('type' => 'datetime', 'default' => null), - 'modified' => array('type' => 'datetime', 'default' => null) - ); - -/** - * construct method - * - * By default don't try to connect until you need to - * - * @param array $config Configuration array - * @param bool $autoConnect false - * @return void - * @access public - */ - function __construct($config = array(), $autoConnect = false) { - return parent::__construct($config, $autoConnect); - } - -/** - * Destruct - * - * @access public - */ - public function __destruct() { - if ($this->connected) { - $this->disconnect(); - } - } - -/** - * commit method - * - * MongoDB doesn't support transactions - * - * @return void - * @access public - */ - public function commit() { - return false; - } - -/** - * Connect to the database - * - * If using 1.0.2 or above use the mongodb:// format to connect - * The connect syntax changed in version 1.0.2 - so check for that too - * - * If authentication information in present then authenticate the connection - * - * @return boolean Connected - * @access public - */ - public function connect() { - $this->connected = false; - - try{ - - $host = $this->createConnectionName($this->config, $this->_driverVersion); - - if (isset($this->config['replicaset']) && count($this->config['replicaset']) === 2) { - $this->connection = new Mongo($this->config['replicaset']['host'], $this->config['replicaset']['options']); - if (isset($this->config['slaveok'])) { - $this->connection->setSlaveOkay($this->config['slaveok']); - } - } else if ($this->_driverVersion >= '1.2.0') { - $this->connection = new Mongo($host, array("persist" => $this->config['persistent'])); - } else { - $this->connection = new Mongo($host, true, $this->config['persistent']); - } - - if ($this->_db = $this->connection->selectDB($this->config['database'])) { - if (!empty($this->config['login']) && $this->_driverVersion < '1.2.0') { - $return = $this->_db->authenticate($this->config['login'], $this->config['password']); - if (!$return || !$return['ok']) { - trigger_error('MongodbSource::connect ' . $return['errmsg']); - return false; - } - } - - $this->connected = true; - } - - } catch(MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - return $this->connected; - } - -/** - * create connection name. - * - * @param array $config - * @param string $version version of MongoDriver - */ - public function createConnectionName($config, $version) { - $host = null; - - if ($version >= '1.0.2') { - $host = "mongodb://"; - } else { - $host = ''; - } - $hostname = $config['host'] . ':' . $config['port']; - - if(!empty($config['login'])){ - $host .= $config['login'] .':'. $config['password'] . '@' . $hostname . '/'. $config['database']; - } else { - $host .= $hostname; - } - - return $host; - } - - -/** - * Inserts multiple values into a table - * - * @param string $table - * @param string $fields - * @param array $values - * @access public - */ - public function insertMulti($table, $fields, $values) { - $table = $this->fullTableName($table); - - if (!is_array($fields) || !is_array($values)) { - return false; - } - $data = array(); - foreach($values as $row) { - if (is_string($row)) { - $row = explode(', ', substr($row, 1, -1)); - } - $data[] = array_combine($fields, $row); - } - $this->_prepareLogQuery($table); // just sets a timer - $params = array_merge($this->collectionOptions['batchInsert'], array('safe' => true)); - try{ - $return = $this->_db - ->selectCollection($table) - ->batchInsert($data, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$table}.insertMulti( :data , :params )", compact('data','params')); - } - } - -/** - * check connection to the database - * - * @return boolean Connected - * @access public - */ - public function isConnected() { - if ($this->connected === false) { - return false; - } - return $this->connect(); - } - -/** - * get MongoDB Object - * - * @return mixed MongoDB Object - * @access public - */ - public function getMongoDb() { - if ($this->connected === false) { - return false; - } - return $this->_db; - } - -/** - * get MongoDB Collection Object - * - * @return mixed MongoDB Collection Object - * @access public - */ - public function getMongoCollection(&$Model) { - if ($this->connected === false) { - return false; - } - - $collection = $this->_db - ->selectCollection($Model->table); - return $collection; - } - -/** - * isInterfaceSupported method - * - * listSources is infact supported, however: cake expects it to return a complete list of all - * possible sources in the selected db - the possible list of collections is infinte, so it's - * faster and simpler to tell cake that the interface is /not/ supported so it assumes that - * exist - * - * @param mixed $interface - * @return void - * @access public - */ - public function isInterfaceSupported($interface) { - if ($interface === 'listSources') { - return false; - } - return parent::isInterfaceSupported($interface); - } - -/** - * Close database connection - * - * @return boolean Connected - * @access public - */ - public function close() { - return $this->disconnect(); - } - -/** - * Disconnect from the database - * - * @return boolean Connected - * @access public - */ - public function disconnect() { - if ($this->connected) { - $this->connected = !$this->connection->close(); - unset($this->_db, $this->connection); - return !$this->connected; - } - return true; - } - -/** - * Get list of available Collections - * - * @param array $data - * @return array Collections - * @access public - */ - public function listSources($data = null) { - if (!$this->isConnected()) { - return false; - } - - $collections = $this->_db->listCollections(); - $sources = array(); - - if(!empty($collections)){ - foreach($collections as $collection){ - $sources[] = $collection->getName(); - } - } - - return $sources; - } - -/** - * Describe - * - * Automatically bind the schemaless behavior if there is no explicit mongo schema. - * When called, if there is model data it will be used to derive a schema. a row is plucked - * out of the db and the data obtained used to derive the schema. - * - * @param Model $Model - * @return array if model instance has mongoSchema, return it. - * @access public - */ - public function describe(&$Model, $field = null) { - $Model->primaryKey = '_id'; - $schema = array(); - if (!empty($Model->mongoSchema) && is_array($Model->mongoSchema)) { - $schema = $Model->mongoSchema; - return $schema + $this->_defaultSchema; - } elseif ($this->isConnected() && is_a($Model, 'Model') && !empty($Model->Behaviors)) { - $Model->Behaviors->attach('Mongodb.Schemaless'); - if (!$Model->data) { - if ($this->_db->selectCollection($Model->table)->count()) { - return $this->deriveSchemaFromData($Model, $this->_db->selectCollection($Model->table)->findOne()); - } - } - } - return $this->deriveSchemaFromData($Model); - } - -/** - * begin method - * - * Mongo doesn't support transactions - * - * @return void - * @access public - */ - public function begin() { - return false; - } - -/** - * Calculate - * - * @param Model $Model - * @return array - * @access public - */ - public function calculate(&$Model) { - return array('count' => true); - } - -/** - * Quotes identifiers. - * - * MongoDb does not need identifiers quoted, so this method simply returns the identifier. - * - * @param string $name The identifier to quote. - * @return string The quoted identifier. - */ - public function name($name) { - return $name; - } - -/** - * Create Data - * - * @param Model $Model Model Instance - * @param array $fields Field data - * @param array $values Save data - * @return boolean Insert result - * @access public - */ - public function create(&$Model, $fields = null, $values = null) { - if (!$this->isConnected()) { - return false; - } - - if ($fields !== null && $values !== null) { - $data = array_combine($fields, $values); - } else { - $data = $Model->data; - } - if (!empty($data['_id'])) { - $this->_convertId($data['_id']); - } - - $this->_prepareLogQuery($Model); // just sets a timer - $params = $this->collectionOptions['insert']; - try{ - $return = $this->_db - ->selectCollection($Model->table) - ->insert($data, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.insert( :data , :params )", compact('data','params')); - } - - if (!empty($return) && $return['ok']) { - - $id = $data['_id']; - if($this->config['set_string_id'] && is_object($data['_id'])) { - $id = $data['_id']->__toString(); - } - $Model->setInsertID($id); - $Model->id = $id; - return true; - } - return false; - } - -/** - * createSchema method - * - * Mongo no care for creating schema. Mongo work with no schema. - * - * @param mixed $schema - * @param mixed $tableName null - * @return void - * @access public - */ - public function createSchema($schema, $tableName = null) { - return true; - } - -/** - * dropSchema method - * - * Return a command to drop each table - * - * @param mixed $schema - * @param mixed $tableName null - * @return void - * @access public - */ - public function dropSchema($schema, $tableName = null) { - if (!$this->isConnected()) { - return false; - } - - if (!is_a($schema, 'CakeSchema')) { - trigger_error(__('Invalid schema object', true), E_USER_WARNING); - return null; - } - if ($tableName) { - return "db.{$tableName}.drop();"; - } - - $toDrop = array(); - foreach ($schema->tables as $curTable => $columns) { - if ($tableName === $curTable) { - $toDrop[] = $curTable; - } - } - - if (count($toDrop) === 1) { - return "db.{$toDrop[0]}.drop();"; - } - - $return = "toDrop = :tables;\nfor( i = 0; i < toDrop.length; i++ ) {\n\tdb[toDrop[i]].drop();\n}"; - $tables = '["' . implode($toDrop, '", "') . '"]'; - - return String::insert($return, compact('tables')); - } - -/** - * distinct method - * - * @param mixed $Model - * @param array $keys array() - * @param array $params array() - * @return void - * @access public - */ - public function distinct(&$Model, $keys = array(), $params = array()) { - if (!$this->isConnected()) { - return false; - } - - $this->_prepareLogQuery($Model); // just sets a timer - - if (array_key_exists('conditions', $params)) { - $params = $params['conditions']; - } - try{ - $return = $this->_db - ->selectCollection($Model->table) - ->distinct($keys, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.distinct( :keys, :params )", compact('keys', 'params')); - } - - return $return; - } - - -/** - * group method - * - * @param mixed $Model - * @param array $params array() - * Set params same as MongoCollection::group() - * key,initial, reduce, options(conditions, finalize) - * - * Ex. $params = array( - * 'key' => array('field' => true), - * 'initial' => array('csum' => 0), - * 'reduce' => 'function(obj, prev){prev.csum += 1;}', - * 'options' => array( - * 'condition' => array('age' => array('$gt' => 20)), - * 'finalize' => array(), - * ), - * ); - * @return void - * @access public - */ - public function group(&$Model, $params = array()) { - - if (!$this->isConnected() || count($params) === 0 ) { - return false; - } - - $this->_prepareLogQuery($Model); // just sets a timer - - $key = (empty($params['key'])) ? array() : $params['key']; - $initial = (empty($params['initial'])) ? array() : $params['initial']; - $reduce = (empty($params['reduce'])) ? array() : $params['reduce']; - $options = (empty($params['options'])) ? array() : $params['options']; - - try{ - $return = $this->_db - ->selectCollection($Model->table) - ->group($key, $initial, $reduce, $options); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.group( :key, :initial, :reduce, :options )", $params); - } - - - return $return; - } - - -/** - * ensureIndex method - * - * @param mixed $Model - * @param array $keys array() - * @param array $params array() - * @return void - * @access public - */ - public function ensureIndex(&$Model, $keys = array(), $params = array()) { - if (!$this->isConnected()) { - return false; - } - - $this->_prepareLogQuery($Model); // just sets a timer - - try{ - $return = $this->_db - ->selectCollection($Model->table) - ->ensureIndex($keys, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.ensureIndex( :keys, :params )", compact('keys', 'params')); - } - - return $return; - } - -/** - * Update Data - * - * This method uses $set operator automatically with MongoCollection::update(). - * If you don't want to use $set operator, you can chose any one as follw. - * 1. Set TRUE in Model::mongoNoSetOperator property. - * 2. Set a mongodb operator in a key of save data as follow. - * Model->save(array('_id' => $id, '$inc' => array('count' => 1))); - * Don't use Model::mongoSchema property, - * CakePHP delete '$inc' data in Model::Save(). - * 3. Set a Mongo operator in Model::mongoNoSetOperator property. - * Model->mongoNoSetOperator = '$inc'; - * Model->save(array('_id' => $id, array('count' => 1))); - * - * @param Model $Model Model Instance - * @param array $fields Field data - * @param array $values Save data - * @return boolean Update result - * @access public - */ - public function update(&$Model, $fields = null, $values = null, $conditions = null) { - - if (!$this->isConnected()) { - return false; - } - - if ($fields !== null && $values !== null) { - $data = array_combine($fields, $values); - } elseif($fields !== null && $conditions !== null) { - return $this->updateAll($Model, $fields, $conditions); - } else{ - $data = $Model->data; - } - - if (empty($data['_id'])) { - $data['_id'] = $Model->id; - } - $this->_convertId($data['_id']); - - try{ - $mongoCollectionObj = $this->_db - ->selectCollection($Model->table); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - return false; - } - - $this->_prepareLogQuery($Model); // just sets a timer - $return = false; - if (!empty($data['_id'])) { - $this->_convertId($data['_id']); - $cond = array('_id' => $data['_id']); - unset($data['_id']); - - $data = $this->setMongoUpdateOperator($Model, $data); - $params = array_merge($this->collectionOptions['update'], array("multiple" => true)); - try{ - $return = $mongoCollectionObj->update($cond, $data, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.update( :conditions, :data, :params )", - array('conditions' => $cond, 'data' => $data, 'params' => $params) - ); - } - } else { - $params = $this->collectionOptions['save']; - try{ - $return = $mongoCollectionObj->save($data, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.save( :data, :params )", compact('data', 'params')); - } - } - return $return; - } - - -/** - * setMongoUpdateOperator - * - * Set Mongo update operator following saving data. - * This method is for update() and updateAll. - * - * @param Model $Model Model Instance - * @param array $values Save data - * @return array $data - * @access public - */ - public function setMongoUpdateOperator(&$Model, $data) { - if(isset($data['updated'])) { - $updateField = 'updated'; - } else { - $updateField = 'modified'; - } - - //setting Mongo operator - if(empty($Model->mongoNoSetOperator)) { - if(!preg_grep('/^\$/', array_keys($data))) { - $data = array('$set' => $data); - } else { - if(!empty($data[$updateField])) { - $modified = $data[$updateField]; - unset($data[$updateField]); - $data['$set'] = array($updateField => $modified); - } - } - } elseif(substr($Model->mongoNoSetOperator,0,1) === '$') { - if(!empty($data[$updateField])) { - $modified = $data[$updateField]; - unset($data[$updateField]); - $data = array($Model->mongoNoSetOperator => $data, '$set' => array($updateField => $modified)); - } else { - $data = array($Model->mongoNoSetOperator => $data); - - } - } - - return $data; - } - -/** - * Update multiple Record - * - * @param Model $Model Model Instance - * @param array $fields Field data - * @param array $conditions - * @return boolean Update result - * @access public - */ - public function updateAll(&$Model, $fields = null, $conditions = null) { - if (!$this->isConnected()) { - return false; - } - - $this->_stripAlias($conditions, $Model->alias); - $this->_stripAlias($fields, $Model->alias, false, 'value'); - - $fields = $this->setMongoUpdateOperator($Model, $fields); - $params = array_merge($this->collectionOptions['update'], array("multiple" => true)); - - $this->_prepareLogQuery($Model); // just sets a timer - try{ - $return = $this->_db - ->selectCollection($Model->table) - ->update($conditions, $fields, $params); - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.update( :conditions, :fields, :params )", - array('conditions' => $conditions, 'fields' => $fields, 'params' => $params) - ); - } - return $return; - } - -/** - * deriveSchemaFromData method - * - * @param mixed $Model - * @param array $data array() - * @return void - * @access public - */ - public function deriveSchemaFromData($Model, $data = array()) { - if (!$data) { - $data = $Model->data; - if ($data && array_key_exists($Model->alias, $data)) { - $data = $data[$Model->alias]; - } - } - - $return = $this->_defaultSchema; - - if ($data) { - $fields = array_keys($data); - foreach($fields as $field) { - if (in_array($field, array('created', 'modified', 'updated'))) { - $return[$field] = array('type' => 'datetime', 'null' => true); - } else { - $return[$field] = array('type' => 'string', 'length' => 2000); - } - } - } - - return $return; - } - -/** - * Delete Data - * - * For deleteAll(true, false) calls - conditions will arrive here as true - account for that and - * convert to an empty array - * For deleteAll(array('some conditions')) calls - conditions will arrive here as: - * array( - * Alias._id => array(1, 2, 3, ...) - * ) - * - * This format won't be understood by mongodb, it'll find 0 rows. convert to: - * - * array( - * Alias._id => array('$in' => array(1, 2, 3, ...)) - * ) - * - * @TODO bench remove() v drop. if it's faster to drop - just drop the collection taking into - * account existing indexes (recreate just the indexes) - * @param Model $Model Model Instance - * @param array $conditions - * @return boolean Update result - * @access public - */ - public function delete(&$Model, $conditions = null) { - if (!$this->isConnected()) { - return false; - } - - $id = null; - - $this->_stripAlias($conditions, $Model->alias); - - if ($conditions === true) { - $conditions = array(); - } elseif (empty($conditions)) { - $id = $Model->id; - } elseif (!empty($conditions) && !is_array($conditions)) { - $id = $conditions; - $conditions = array(); - } - - $mongoCollectionObj = $this->_db - ->selectCollection($Model->table); - - $this->_stripAlias($conditions, $Model->alias); - if (!empty($id)) { - $conditions['_id'] = $id; - } - if (!empty($conditions['_id'])) { - $this->_convertId($conditions['_id'], true); - } - - $return = false; - $r = false; - try{ - $this->_prepareLogQuery($Model); // just sets a timer - $return = $mongoCollectionObj->remove($conditions); - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.remove( :conditions )", - compact('conditions') - ); - } - $return = true; - } catch (MongoException $e) { - $this->error = $e->getMessage(); - trigger_error($this->error); - } - return $return; - } - -/** - * Read Data - * - * For deleteAll(true) calls - the conditions will arrive here as true - account for that and switch to an empty array - * - * @param Model $Model Model Instance - * @param array $query Query data - * @return array Results - * @access public - */ - public function read(&$Model, $query = array()) { - if (!$this->isConnected()) { - return false; - } - - $this->_setEmptyValues($query); - extract($query); - - if (!empty($order[0])) { - $order = array_shift($order); - } - $this->_stripAlias($conditions, $Model->alias); - $this->_stripAlias($fields, $Model->alias, false, 'value'); - $this->_stripAlias($order, $Model->alias, false, 'both'); - - if (!empty($conditions['_id'])) { - $this->_convertId($conditions['_id']); - } - - $fields = (is_array($fields)) ? $fields : array($fields => 1); - if ($conditions === true) { - $conditions = array(); - } elseif (!is_array($conditions)) { - $conditions = array($conditions); - } - $order = (is_array($order)) ? $order : array($order); - - if (is_array($order)) { - foreach($order as $field => &$dir) { - if (is_numeric($field) || is_null($dir)) { - unset ($order[$field]); - continue; - } - if ($dir && strtoupper($dir) === 'ASC') { - $dir = 1; - continue; - } elseif (!$dir || strtoupper($dir) === 'DESC') { - $dir = -1; - continue; - } - $dir = (int)$dir; - } - } - - if (empty($offset) && $page && $limit) { - $offset = ($page - 1) * $limit; - } - - $return = array(); - - $this->_prepareLogQuery($Model); // just sets a timer - if (empty($modify)) { - if ($Model->findQueryType === 'count' && $fields == array('count' => true)) { - $count = $this->_db - ->selectCollection($Model->table) - ->count($conditions); - if ($this->fullDebug) { - $this->logQuery("db.{$Model->useTable}.count( :conditions )", - compact('conditions', 'count') - ); - } - return array(array($Model->alias => array('count' => $count))); - } - - $return = $this->_db - ->selectCollection($Model->table) - ->find($conditions, $fields) - ->sort($order) - ->limit($limit) - ->skip($offset); - if ($this->fullDebug) { - $count = $return->count(true); - $this->logQuery("db.{$Model->useTable}.find( :conditions, :fields ).sort( :order ).limit( :limit ).skip( :offset )", - compact('conditions', 'fields', 'order', 'limit', 'offset', 'count') - ); - } - } else { - $options = array_filter(array( - 'findandmodify' => $Model->table, - 'query' => $conditions, - 'sort' => $order, - 'remove' => !empty($remove), - 'update' => array('$set' => $modify), - 'new' => !empty($new), - 'fields' => $fields, - 'upsert' => !empty($upsert) - )); - $return = $this->_db - ->command($options); - if ($this->fullDebug) { - if ($return['ok']) { - $count = 1; - if ($this->config['set_string_id'] && !empty($return['value']['_id']) && is_object($return['value']['_id'])) { - $return['value']['_id'] = $return['value']['_id']->__toString(); - } - $return[][$Model->alias] = $return['value']; - } else { - $count = 0; - } - $this->logQuery("db.runCommand( :options )", - array('options' => array_filter($options), 'count' => $count) - ); - } - } - - if ($Model->findQueryType === 'count') { - return array(array($Model->alias => array('count' => $return->count()))); - } - - if (is_object($return)) { - $_return = array(); - while ($return->hasNext()) { - $mongodata = $return->getNext(); - if ($this->config['set_string_id'] && !empty($mongodata['_id']) && is_object($mongodata['_id'])) { - $mongodata['_id'] = $mongodata['_id']->__toString(); - } - $_return[][$Model->alias] = $mongodata; - } - return $_return; - } - return $return; - } - -/** - * rollback method - * - * MongoDB doesn't support transactions - * - * @return void - * @access public - */ - public function rollback() { - return false; - } - -/** - * Deletes all the records in a table - * - * @param mixed $table A string or model class representing the table to be truncated - * @return boolean - * @access public - */ - public function truncate($table) { - if (!$this->isConnected()) { - return false; - } - - return $this->execute('db.' . $this->fullTableName($table) . '.remove();'); - } - -/** - * query method - * If call getMongoDb() from model, this method call getMongoDb(). - * - * @param mixed $query - * @param array $params array() - * @return void - * @access public - */ - public function query($query, $params = array()) { - if (!$this->isConnected()) { - return false; - } - - if($query === 'getMongoDb') { - return $this->getMongoDb(); - } - - $this->_prepareLogQuery($Model); // just sets a timer - $return = $this->_db - ->command($query); - if ($this->fullDebug) { - $this->logQuery("db.runCommand( :query )", compact('query')); - } - - return $return; - } - -/** - * mapReduce - * - * @param mixed $query - * @param integer $timeout (milli second) - * @return mixed false or array - * @access public - */ - public function mapReduce($query, $timeout = null) { - - //above MongoDB1.8, query must object. - if(isset($query['query']) && !is_object($query['query'])) { - $query['query'] = (object)$query['query']; - } - - $result = $this->query($query); - - if($result['ok']) { - if (isset($query['out']['inline']) && $query['out']['inline'] === 1) { - if (is_array($result['results'])) { - $data = $result['results']; - }else{ - $data = false; - } - }else { - $data = $this->_db->selectCollection($result['result'])->find(); - if(!empty($timeout)) { - $data->timeout($timeout); - } - } - return $data; - } - return false; - } - - - -/** - * Prepares a value, or an array of values for database queries by quoting and escaping them. - * - * @param mixed $data A value or an array of values to prepare. - * @param string $column The column into which this data will be inserted - * @param boolean $read Value to be used in READ or WRITE context - * @return mixed Prepared value or array of values. - * @access public - */ - public function value($data, $column = null, $read = true) { - $return = parent::value($data, $column, $read); - if ($return === null && $data !== null) { - return $data; - } - return $return; - } - -/** - * execute method - * - * If there is no query or the query is true, execute has probably been called as part of a - * db-agnostic process which does not have a mongo equivalent, don't do anything. - * - * @param mixed $query - * @param array $params array() - * @return void - * @access public - */ - public function execute($query, $params = array()) { - if (!$this->isConnected()) { - return false; - } - - if (!$query || $query === true) { - return; - } - $this->_prepareLogQuery($Model); // just sets a timer - $return = $this->_db - ->execute($query, $params); - if ($this->fullDebug) { - if ($params) { - $this->logQuery(":query, :params", - compact('query', 'params') - ); - } else { - $this->logQuery($query); - } - } - if ($return['ok']) { - return $return['retval']; - } - return $return; - } - -/** - * Set empty values, arrays or integers, for the variables Mongo uses - * - * @param mixed $data - * @param array $integers array('limit', 'offset') - * @return void - * @access protected - */ - protected function _setEmptyValues(&$data, $integers = array('limit', 'offset')) { - if (!is_array($data)) { - return; - } - foreach($data as $key => $value) { - if (empty($value)) { - if (in_array($key, $integers)) { - $data[$key] = 0; - } else { - $data[$key] = array(); - } - } - } - } - -/** - * prepareLogQuery method - * - * Any prep work to log a query - * - * @param mixed $Model - * @return void - * @access protected - */ - protected function _prepareLogQuery(&$Model) { - if (!$this->fullDebug) { - return false; - } - $this->_startTime = microtime(true); - $this->took = null; - $this->affected = null; - $this->error = null; - $this->numRows = null; - return true; - } - -/** - * setTimeout Method - * - * Sets the MongoCursor timeout so long queries (like map / reduce) can run at will. - * Expressed in milliseconds, for an infinite timeout, set to -1 - * - * @param int $ms - * @return boolean - * @access public - */ - public function setTimeout($ms){ - MongoCursor::$timeout = $ms; - - return true; - } - -/** - * logQuery method - * - * Set timers, errors and refer to the parent - * If there are arguments passed - inject them into the query - * Show MongoIds in a copy-and-paste-into-mongo format - * - * - * @param mixed $query - * @param array $args array() - * @return void - * @access public - */ - public function logQuery($query, $args = array()) { - if ($args) { - $this->_stringify($args); - $query = String::insert($query, $args); - } - $this->took = round((microtime(true) - $this->_startTime) * 1000, 0); - $this->affected = null; - if (empty($this->error['err'])) { - $this->error = $this->_db->lastError(); - if (!is_scalar($this->error)) { - $this->error = json_encode($this->error); - } - } - $this->numRows = !empty($args['count'])?$args['count']:null; - - $query = preg_replace('@"ObjectId\((.*?)\)"@', 'ObjectId ("\1")', $query); - return parent::logQuery($query); - } - -/** - * convertId method - * - * $conditions is used to determine if it should try to auto correct _id => array() queries - * it only appies to conditions, hence the param name - * - * @param mixed $mixed - * @param bool $conditions false - * @return void - * @access protected - */ - protected function _convertId(&$mixed, $conditions = false) { - if (is_string($mixed)) { - if (strlen($mixed) !== 24) { - return; - } - $mixed = new MongoId($mixed); - } - if (is_array($mixed)) { - foreach($mixed as &$row) { - $this->_convertId($row, false); - } - if (!empty($mixed[0]) && $conditions) { - $mixed = array('$in' => $mixed); - } - } - } - -/** - * stringify method - * - * Takes an array of args as an input and returns an array of json-encoded strings. Takes care of - * any objects the arrays might be holding (MongoID); - * - * @param array $args array() - * @param int $level 0 internal recursion counter - * @return array - * @access protected - */ - protected function _stringify(&$args = array(), $level = 0) { - foreach($args as &$arg) { - if (is_array($arg)) { - $this->_stringify($arg, $level + 1); - } elseif (is_object($arg) && is_callable(array($arg, '__toString'))) { - $class = get_class($arg); - if ($class === 'MongoId') { - $arg = 'ObjectId(' . $arg->__toString() . ')'; - } elseif ($class === 'MongoRegex') { - $arg = '_regexstart_' . $arg->__toString() . '_regexend_'; - } else { - $arg = $class . '(' . $arg->__toString() . ')'; - } - } - if ($level === 0) { - $arg = json_encode($arg); - if (strpos($arg, '_regexstart_')) { - preg_match_all('@"_regexstart_(.*?)_regexend_"@', $arg, $matches); - foreach($matches[0] as $i => $whole) { - $replace = stripslashes($matches[1][$i]); - $arg = str_replace($whole, $replace, $arg); - } - } - } - } - } - -/** - * Convert automatically array('Model.field' => 'foo') to array('field' => 'foo') - * - * This introduces the limitation that you can't have a (nested) field with the same name as the model - * But it's a small price to pay to be able to use other behaviors/functionality with mongoDB - * - * @param array $args array() - * @param string $alias 'Model' - * @param bool $recurse true - * @param string $check 'key', 'value' or 'both' - * @return void - * @access protected - */ - protected function _stripAlias(&$args = array(), $alias = 'Model', $recurse = true, $check = 'key') { - if (!is_array($args)) { - return; - } - $checkKey = ($check === 'key' || $check === 'both'); - $checkValue = ($check === 'value' || $check === 'both'); - - foreach($args as $key => &$val) { - if ($checkKey) { - if (strpos($key, $alias . '.') === 0) { - unset($args[$key]); - $key = substr($key, strlen($alias) + 1); - $args[$key] = $val; - } - } - if ($checkValue) { - if (is_string($val) && strpos($val, $alias . '.') === 0) { - $val = substr($val, strlen($alias) + 1); - } - } - if ($recurse && is_array($val)) { - $this->_stripAlias($val, $alias, true, $check); - } - } - } -} - -/** - * MongoDbDateFormatter method - * - * This function cannot be in the class because of the way model save is written - * - * @param mixed $date null - * @return void - * @access public - */ -function MongoDbDateFormatter($date = null) { - if ($date) { - return new MongoDate($date); - } - return new MongoDate(); -} \ No newline at end of file diff --git a/tests/cases/1st/first_mongodb_source.test.php b/tests/cases/1st/first_mongodb_source.test.php deleted file mode 100644 index 68a35df..0000000 --- a/tests/cases/1st/first_mongodb_source.test.php +++ /dev/null @@ -1,171 +0,0 @@ - 'mongodb', - 'host' => 'localhost', - 'login' => '', - 'password' => '', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => false, - ); - -/** - * Sets up the environment for each test method - * - * @return void - * @access public - */ - public function startTest() { - $connections = ConnectionManager::enumConnectionObjects(); - - if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'mongodbSource') { - $config = new DATABASE_CONFIG(); - $this->_config = $config->test; - } - - ConnectionManager::create('mongo_test', $this->_config); - - } - -/** - * Destroys the environment after each test method is run - * - * @return void - * @access public - */ - public function endTest() { - $this->mongodb = new MongodbSource($this->_config); - $this->mongodb->connect(); - $this->dropData(); - unset($this->mongodb); - } - - -/** - * Drop database - * - * @return void - * @access public - */ - public function dropData() { - try { - $db = $this->mongodb - ->connection - ->selectDB($this->_config['database']); - - foreach($db->listCollections() as $collection) { - $collection->drop(); - } - } catch (MongoException $e) { - trigger_error($e->getMessage()); - } - } - - -/** - * testSchemaless method - * - * Test you can save to a model without specifying mongodb. - * - * @return void - * @access public - */ - public function testSchemaless() { - $toSave = array( - 'title' => 'A test article', - 'body' => str_repeat('Lorum ipsum ', 100), - 'tags' => array( - 'one', - 'two', - 'three' - ), - 'modified' => null, - 'created' => null - ); - - $MongoArticle = ClassRegistry::init('MongoArticleSchemafree'); - $MongoArticle->create(); - $this->assertTrue($MongoArticle->save($toSave), 'Saving with no defined schema failed'); - - $expected = array_intersect_key($toSave, array_flip(array('title', 'body', 'tags'))); - $result = $MongoArticle->read(array('title', 'body', 'tags')); - unset ($result['MongoArticleSchemafree']['_id']); // prevent auto added field from screwing things up - $this->assertEqual($expected, $result['MongoArticleSchemafree']); - - $toSave = array( - 'title' => 'Another test article', - 'body' => str_repeat('Lorum pipsum ', 100), - 'tags' => array( - 'four', - 'five', - 'six' - ), - 'starts' => date('Y-M-d H:i:s'), - 'modified' => null, - 'created' => null - ); - $MongoArticle->create(); - $this->assertTrue($MongoArticle->save($toSave), 'Saving with no defined schema failed'); - $starts = $MongoArticle->field('starts'); - $this->assertEqual($toSave['starts'], $starts); - } - -} diff --git a/tests/cases/behaviors/sql_compatible.test.php b/tests/cases/behaviors/sql_compatible.test.php deleted file mode 100644 index 4874716..0000000 --- a/tests/cases/behaviors/sql_compatible.test.php +++ /dev/null @@ -1,314 +0,0 @@ -lastQuery = $query; - return $query; - } -} - -/** - * SqlCompatibleTest class - * - * @uses CakeTestCase - * @package mongodb - * @subpackage mongodb.tests.cases.behaviors - */ -class SqlCompatibleTest extends CakeTestCase { - -/** - * Default db config. overriden by test db connection if present - * - * @var array - * @access protected - */ - protected $_config = array( - 'datasource' => 'mongodb', - 'host' => 'localhost', - 'login' => '', - 'password' => '', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => false, - ); - -/** - * Sets up the environment for each test method - * - * @return void - * @access public - */ - public function startTest() { - $connections = ConnectionManager::enumConnectionObjects(); - - if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'mongodbSource') { - $config = new DATABASE_CONFIG(); - $this->_config = $config->test; - } - - ConnectionManager::create('mongo_test', $this->_config); - $this->Mongo = new MongodbSource($this->_config); - - $this->Post = ClassRegistry::init(array('class' => 'SqlCompatiblePost', 'alias' => 'Post', 'ds' => 'mongo_test')); - - $this->_setupData(); - } - -/** - * Destroys the environment after each test method is run - * - * @return void - * @access public - */ - public function endTest() { - $this->Post->deleteAll(true); - unset($this->Post); - } - -/** - * testNOT method - * - * @return void - * @access public - */ - public function testNOT() { - $expected = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); - $result = $this->Post->find('all', array( - 'conditions' => array( - 'title !=' => 10, - ), - 'fields' => array('_id', 'title', 'number'), - 'order' => array('number' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $conditions = array( - 'title' => array('$ne' => 10) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - - $result = $this->Post->find('all', array( - 'conditions' => array( - 'NOT' => array( - 'title' => 10 - ), - ), - 'fields' => array('_id', 'title', 'number'), - 'order' => array('number' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $conditions = array( - 'title' => array('$nin' => array(10)) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - } - -/** - * testGTLT method - * - * @return void - * @access public - */ - public function testGTLT() { - $expected = array(8, 9, 10, 11, 12, 13); - $result = $this->Post->find('all', array( - 'conditions' => array( - 'title >' => 7, - 'title <' => 14, - ), - 'fields' => array('_id', 'title', 'number'), - 'order' => array('number' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $conditions = array( - 'title' => array( - '$gt' => 7, - '$lt' => 14 - ) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - } - -/** - * testGTE method - * - * @return void - * @access public - */ - public function testGTE() { - $expected = array(19, 20); - $result = $this->Post->find('all', array( - 'conditions' => array( - 'title >=' => 19, - ), - 'fields' => array('_id', 'title', 'number'), - 'order' => array('number' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $conditions = array( - 'title' => array('$gte' => 19) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - } - -/** - * testOR method - * - * @return void - * @access public - */ - public function testOR() { - $expected = array(1, 2, 19, 20); - $result = $this->Post->find('all', array( - 'conditions' => array( - 'OR' => array( - 'title <=' => 2, - 'title >=' => 19, - ) - ), - 'fields' => array('_id', 'title', 'number'), - 'order' => array('number' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $conditions = array( - '$or' => array( - array('title' => array('$lte' => 2)), - array('title' => array('$gte' => 19)) - ) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - } - - - /** - * Tests find method with conditions _id=>array() - * - * @return void - * @access public - */ - public function testFindConditionIn() { - - for ($i = 1; $i <= 5; $i++) { - $data = array( - '_id' => 'A1' . $i, - 'title' => $i, - ); - $saveData['Post'] = $data; - $this->Post->create(); - $this->Post->save($saveData); - } - - $params = array('conditions' => array('_id' => array('A11', 'A12'))); - $result = $this->Post->find('all', $params); - - $expected = array('A11','A12'); - $result = Set::extract($result, '/Post/_id'); - $this->assertEqual($expected, $result); - $this->assertEqual(2, count($result)); - - $conditions = array( - '_id' => array( - '$in' => array('A11', 'A12') - ) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - - - $params = array('conditions' => array('_id' => array('$nin' => array('A11', 'A12')))); - $result = $this->Post->find('all', $params); - //$expected = array('A13','A14'); - $result = Set::extract($result, '/Post/_id'); - $this->assertTrue(in_array('A13', $result)); - $this->assertFalse(in_array('A11', $result)); - $this->assertFalse(in_array('A12', $result)); - $this->assertEqual(23, count($result)); - - - $conditions = array( - '_id' => array( - '$nin' => array('A11', 'A12') - ) - ); - $this->assertEqual($conditions, $this->Post->lastQuery['conditions']); - } - - -/** - * setupData method - * - * @return void - * @access protected - */ - protected function _setupData() { - $this->Post->deleteAll(true); - for ($i = 1; $i <= 20; $i++) { - $data = array( - 'title' => $i, - ); - $saveData['Post'] = $data; - $this->Post->create(); - $this->Post->save($saveData); - } - } -} diff --git a/tests/cases/datasources/mongodb_source.test.php b/tests/cases/datasources/mongodb_source.test.php deleted file mode 100644 index 9a8ec4e..0000000 --- a/tests/cases/datasources/mongodb_source.test.php +++ /dev/null @@ -1,1673 +0,0 @@ - array( - 'rule' => 'isUnique', - 'required' => false - ), - 'uniquefield2' => array( - 'rule' => 'manualUniqueValidation', - 'required' => false - ), - ); - - public $mongoSchema = array( - 'title' => array('type' => 'string'), - 'body' => array('type' => 'string'), - 'text' => array('type' => 'text'), - 'uniquefield1' => array('type' => 'text'), - 'uniquefield2' => array('type' => 'text'), - 'count' => array('type' => 'integer'), - 'created' => array('type' => 'datetime'), - 'modified' => array('type' => 'datetime'), - ); - - function manualUniqueValidation($check) { - $c = $this->find('count', array( - 'conditions' => array( - 'uniquefield2' => $check['uniquefield2'] - ) - )); - if ($c === 0) { - return true; - } - return false; - } -} - -/** - * MongoArticle class - * - * @uses AppModel - * @package mongodb - * @subpackage mongodb.tests.cases.datasources - */ -class MongoArticle extends AppModel { - - public $useDbConfig = 'mongo_test'; -} - -/** - * MongoDB Source test class - * - * @package app - * @subpackage app.model.datasources - */ -class MongodbSourceTest extends CakeTestCase { - -/** - * Database Instance - * - * @var resource - * @access public - */ - public $mongodb; - -/** - * Base Config - * - * @var array - * @access public - * - */ - protected $_config = array( - 'datasource' => 'mongodb', - 'host' => 'localhost', - 'login' => '', - 'password' => '', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => true, - ); - -/** - * Sets up the environment for each test method - * - * @return void - * @access public - */ - public function startTest() { - $connections = ConnectionManager::enumConnectionObjects(); - - if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'mongodbSource') { - $config = new DATABASE_CONFIG(); - $this->_config = $config->test; - } - - ConnectionManager::create('mongo_test', $this->_config); - $this->Mongo = new MongodbSource($this->_config); - - $this->Post = ClassRegistry::init('Post'); - $this->Post->setDataSource('mongo_test'); - - $this->mongodb =& ConnectionManager::getDataSource($this->Post->useDbConfig); - $this->mongodb->connect(); - - $this->dropData(); - } - -/** - * Destroys the environment after each test method is run - * - * @return void - * @access public - */ - public function endTest() { - $this->dropData(); - unset($this->Post); - unset($this->Mongo); - unset($this->mongodb); - ClassRegistry::flush(); - } - - -/** - * get Mongod server version - * - * @return numeric - * @access public - */ - public function getMongodVersion() { - $mongo = $this->Post->getDataSource(); - return $mongo->execute('db.version()'); - } - -/** - * Insert data method for mongodb. - * - * @param array insert data - * @return void - * @access public - */ - public function insertData($data) { - try { - $this->mongodb - ->connection - ->selectDB($this->_config['database']) - ->selectCollection($this->Post->table) - ->insert($data, true); - } catch (MongoException $e) { - trigger_error($e->getMessage()); - } - } - -/** - * Drop database - * - * @return void - * @access public - */ - public function dropData() { - try { - $db = $this->mongodb - ->connection - ->selectDB($this->_config['database']); - - foreach($db->listCollections() as $collection) { - $collection->drop(); - } - } catch (MongoException $e) { - trigger_error($e->getMessage()); - } - } - - -/** - * testCreateConnectionName - * - * @return void - * @access public - */ - public function testCreateConnectionName() { - $config = array( - 'datasource' => 'mongodb', - 'host' => 'localhost', - 'login' => '', - 'password' => '', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => false, - ); - $version = '1.2.2'; - $expect = 'mongodb://localhost:27017'; - $host = $this->mongodb->createConnectionName($config, $version); - $this->assertIdentical($expect, $host); - - - $config = array( - 'datasource' => 'mongodb', - 'host' => 'localhost', - 'login' => 'user', - 'password' => 'pass', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => false, - ); - $version = '1.2.2'; - $expect = 'mongodb://user:pass@localhost:27017/test_mongo'; - $host = $this->mongodb->createConnectionName($config, $version); - $this->assertIdentical($expect, $host); - - - $config = array( - 'datasource' => 'mongodb', - 'host' => 'localhost', - 'login' => 'user', - 'password' => 'pass', - 'database' => 'test_mongo', - 'port' => 27017, - 'prefix' => '', - 'persistent' => false, - ); - $version = '1.0.0'; - $expect = 'user:pass@localhost:27017/test_mongo'; - $host = $this->mongodb->createConnectionName($config, $version); - $this->assertIdentical($expect, $host); - } - -/** - * Tests connection - * - * @return void - * @access public - */ - public function testConnect() { - $result = $this->Mongo->connect(); - $this->assertTrue($result); - - $this->assertTrue($this->Mongo->connected); - $this->assertTrue($this->Mongo->isConnected()); - } - -/** - * Tests the disconnect method of the Mongodb DataSource - * - * @return void - * @access public - */ - public function testDisconnect() { - $result = $this->Mongo->disconnect(); - $this->assertTrue($result); - $this->assertFalse($this->Mongo->connected); - } - -/** - * Tests the listSources method of the Mongodb DataSource - * - * @return void - * @access public - */ - public function testListSources() { - $this->assertTrue(is_array($this->mongodb->listSources())); - } - -/** - * Tests the getMongoDb method of the Mongodb DataSource - * - * @return void - * @access public - */ - public function testGetMongoDb() { - $obj = $this->mongodb->getMongoDb(); - $this->assertTrue(is_object($obj)); - $objName = get_class($obj); - $this->assertEqual('MongoDB', $objName); - } - -/** - * Tests the Model::getMongoDb() call MongodbSource::getMongoDb - * - * @return void - * @access public - */ - public function testGetMongoDbFromModel() { - $obj = $this->Post->getMongoDb(); - $this->assertTrue(is_object($obj)); - $objName = get_class($obj); - $this->assertEqual('MongoDB', $objName); - } - -/** - * Tests the getMongoCollection method of the Mongodb DataSource - * - * @return void - * @access public - */ - public function testGetMongoCollection() { - $obj = $this->mongodb->getMongoCollection($this->Post); - $this->assertTrue(is_object($obj)); - $objName = get_class($obj); - $this->assertEqual('MongoCollection', $objName); - } - -/** - * Tests the describe method of the Mongodb DataSource - * - * @return void - * @access public - */ - public function testDescribe() { - $mockObj = new MockPost(); - - $result = $this->mongodb->describe($mockObj); - $expected = array( - '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), - 'created' => array('type' => 'datetime', 'default' => null), - 'modified' => array('type' => 'datetime', 'default' => null), - ); - $this->assertEqual($expected, $result); - - $result = $this->mongodb->describe($this->Post); - $expect = array( - '_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'), - 'title' => array('type' => 'string'), - 'body' => array('type' => 'string'), - 'text' => array('type' => 'text'), - 'uniquefield1' => array('type' => 'text'), - 'uniquefield2' => array('type' => 'text'), - 'count' => array('type' => 'integer'), - 'created' => array('type' => 'datetime'), - 'modified' => array('type' => 'datetime'), - ); - ksort($result); - ksort($expect); - $this->assertEqual($expect, $result); - } - -/** - * Tests find method. - * - * @return void - * @access public - */ - public function testFind() { - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb' - ); - $this->insertData($data); - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - } - -/** - * Tests save method. - * - * @return void - * @access public - */ - public function testSave() { - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb' - ); - $saveData['Post'] = $data; - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $result = $this->Post->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['Post']; - $this->assertEqual(6, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $this->assertEqual($this->Post->id, $resultData['_id']); - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - - $this->assertTrue(is_a($resultData['created'], 'MongoDate')); - $this->assertTrue(is_a($resultData['modified'], 'MongoDate')); - } - -/** - * Tests insertId after saving - * - * @return void - * @access public - */ - public function testCheckInsertIdAfterSaving() { - $saveData['Post'] = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $this->assertEqual($this->Post->id, $this->Post->getInsertId()); - $this->assertTrue(is_string($this->Post->id)); - $this->assertTrue(is_string($this->Post->getInsertId())); - - //set Numeric _id - $saveData['Post'] = array( - '_id' => 123456789, - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $this->assertEqual($saveData['Post']['_id'] ,$this->Post->id); - $this->assertEqual($this->Post->id, $this->Post->getInsertId()); - $this->assertTrue(is_numeric($this->Post->id)); - $this->assertTrue(is_numeric($this->Post->getInsertId())); - - $readArray1 = $this->Post->read(); - $readArray2 = $this->Post->read(null, $saveData['Post']['_id']); - $this->assertEqual($readArray1, $readArray2); - $this->assertEqual($saveData['Post']['_id'], $readArray2['Post']['_id']); - - } - - - -/** - * Tests saveAll method. - * - * @return void - * @access public - */ - public function testSaveAll() { - $saveData[0]['Post'] = array( - 'title' => 'test1', - 'body' => 'aaaa1', - 'text' => 'bbbb1' - ); - - $saveData[1]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2' - ); - - $this->Post->create(); - $saveResult = $this->Post->saveAll($saveData); - $result = $this->Post->find('all'); - - $this->assertEqual(2, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(6, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $data = $saveData[0]['Post']; - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - - $this->assertTrue(is_a($resultData['created'], 'MongoDate')); - $this->assertTrue(is_a($resultData['modified'], 'MongoDate')); - - $resultData = $result[1]['Post']; - $this->assertEqual(6, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $data = $saveData[1]['Post']; - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - - $this->assertTrue(is_a($resultData['created'], 'MongoDate')); - $this->assertTrue(is_a($resultData['modified'], 'MongoDate')); - } - -/** - * Tests update method. - * - * @return void - * @access public - */ - public function testUpdate() { - $count0 = $this->Post->find('count'); - - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb', - 'count' => 0 - ); - $saveData['Post'] = $data; - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $postId = $this->Post->id; - - $count1 = $this->Post->find('count'); - $this->assertIdentical($count1 - $count0, 1, 'Save failed to create one row'); - - $this->assertTrue($saveResult); - $this->assertTrue($postId); - $findresult = $this->Post->find('all'); - $this->assertEqual(0, $findresult[0]['Post']['count']); - - $updatedata = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2' - ); - $saveData['Post'] = $updatedata; - - $saveResult = $this->Post->save($saveData); - - $count2 = $this->Post->find('count'); - $this->assertIdentical($count2 - $count1, 0, 'Save test 2 created another row, it did not update the existing row'); - - $this->assertTrue($saveResult); - $this->assertIdentical($this->Post->id, $postId); - - $this->Post->create(); - $updatedata = array( - '_id' => $postId, - 'title' => 'test3', - 'body' => 'aaaa3', - 'text' => 'bbbb3' - ); - $saveData['Post'] = $updatedata; - $saveResult = $this->Post->save($saveData); - - $count3 = $this->Post->find('count'); - $this->assertIdentical($count3 - $count2, 0, 'Saving with the id in the data created another row'); - - $this->assertTrue($saveResult); - $this->assertIdentical($this->Post->id, $postId); - - $this->Post->create(); - $this->Post->id = $postId; - $updatedata = array( - 'title' => 'test4', - 'body' => 'aaaa4', - 'text' => 'bbbb4' - ); - $saveData['Post'] = $updatedata; - $saveResult = $this->Post->save($saveData); - - $count4 = $this->Post->find('count'); - $this->assertIdentical($count4 - $count3, 0, 'Saving with $Model->id set and no id in the data created another row'); - - $this->assertTrue($saveResult); - $this->assertIdentical($this->Post->id, $postId); - - $result = $this->Post->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['Post']; - $this->assertEqual(7, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $this->assertEqual($this->Post->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); - $this->assertEqual($updatedata['body'], $resultData['body']); - $this->assertEqual($updatedata['text'], $resultData['text']); - $this->assertEqual(0, $resultData['count']); - - - // using $inc operator - $this->Post->mongoNoSetOperator = '$inc'; - $this->Post->create(); - $updatedataIncrement = array( - '_id' => $postId, - 'count' => 1, - ); - $saveData['Post'] = $updatedataIncrement; - $saveResult = $this->Post->save($saveData); - - $this->assertTrue($saveResult); - $this->assertIdentical($this->Post->id, $postId); - - $result = $this->Post->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['Post']; - $this->assertEqual(7, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $this->assertEqual($this->Post->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); //not update - $this->assertEqual($updatedata['body'], $resultData['body']); //not update - $this->assertEqual($updatedata['text'], $resultData['text']); //not update - $this->assertEqual(1, $resultData['count']); //increment - unset($this->Post->mongoNoSetOperator); - } - - -/** - * Tests updateAll method. - * - * @return void - * @access public - */ - public function testUpdateAll() { - $saveData[0]['Post'] = array( - 'title' => 'test', - 'name' => 'ichi', - 'body' => 'aaaa1', - 'text' => 'bbbb1' - ); - - $saveData[1]['Post'] = array( - 'title' => 'test', - 'name' => 'ichi', - 'body' => 'aaaa2', - 'text' => 'bbbb2' - ); - - $this->Post->create(); - $this->Post->saveAll($saveData); - - $updateData = array('name' => 'ichikawa'); - $conditions = array('title' => 'test'); - - $resultUpdateAll = $this->Post->updateAll($updateData, $conditions); - $this->assertTrue($resultUpdateAll); - - $result = $this->Post->find('all'); - $this->assertEqual(2, count($result)); - $resultData = $result[0]['Post']; - $this->assertEqual(7, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $data = $saveData[0]['Post']; - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual('ichikawa', $resultData['name']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - $this->assertTrue(is_a($resultData['created'], 'MongoDate')); - $this->assertTrue(is_a($resultData['modified'], 'MongoDate')); - - - $resultData = $result[1]['Post']; - $this->assertEqual(7, count($resultData)); - $this->assertTrue(!empty($resultData['_id'])); - $data = $saveData[1]['Post']; - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual('ichikawa', $resultData['name']); - $this->assertEqual($data['body'], $resultData['body']); - $this->assertEqual($data['text'], $resultData['text']); - $this->assertTrue(is_a($resultData['created'], 'MongoDate')); - $this->assertTrue(is_a($resultData['modified'], 'MongoDate')); - } - -/** - * Tests updateAll method. - * - * @return void - * @access public - */ - public function testSetMongoUpdateOperator() { - - $ds = $this->Post->getDataSource(); - - //normal - $data = array('title' => 'test1', 'name' => 'ichikawa'); - $expect = array('$set' => array('title' => 'test1', 'name' => 'ichikawa')); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //using $inc - $data = array('title' => 'test1', 'name' => 'ichikawa', '$inc' => array('count' => 1)); - $expect = array('title' => 'test1', 'name' => 'ichikawa', '$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //using $inc and modified - $data = array('modified' => '2011/8/1', '$inc' => array('count' => 1)); - $expect = array('$set' => array('modified' => '2011/8/1'), '$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //using $inc and updated - $data = array('updated' => '2011/8/1', '$inc' => array('count' => 1)); - $expect = array('$set' => array('updated' => '2011/8/1'), '$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //using $inc, $push and modified - $data = array('$push' => array('tag' => 'tag1'), 'modified' => '2011/8/1', '$inc' => array('count' => 1)); - $expect = array('$push' => array('tag' => 'tag1'),'$set' => array('modified' => '2011/8/1'), '$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //mongoNoSetOperator is true, - // using $inc, $push and modified - $this->Post->mongoNoSetOperator = true; - $data = array('$push' => array('tag' => 'tag1'), 'modified' => '2011/8/1', '$inc' => array('count' => 1)); - $expect = array('$push' => array('tag' => 'tag1'),'modified' => '2011/8/1', '$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - - //mongoNoSetOperator is $inc, - $this->Post->mongoNoSetOperator = '$inc'; - $data = array('count' => 1); - $expect = array('$inc' => array('count' => 1)); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - - //mongoNoSetOperator is $inc, - // with modified field - $this->Post->mongoNoSetOperator = '$inc'; - $data = array('count' => 1, 'modified' => '2011/8/1'); - $expect = array('$inc' => array('count' => 1),'$set' => array('modified' => '2011/8/1')); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - //mongoNoSetOperator is $inc, - // with updated field - $this->Post->mongoNoSetOperator = '$inc'; - $data = array('count' => 1, 'updated' => '2011/8/1'); - $expect = array('$inc' => array('count' => 1),'$set' => array('updated' => '2011/8/1')); - $result = $ds->setMongoUpdateOperator($this->Post, $data); - $this->assertEqual($expect, $result); - - - } - - -/** - * Tests update method without $set operator. - * - * @return void - * @access public - */ - public function testUpdateWithoutMongoSchemaProperty() { - $MongoArticle = ClassRegistry::init('MongoArticle'); - - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb', - 'count' => 0, - 'created' => new mongoDate(), - 'modified' => new mongoDate(), - ); - $saveData['MongoArticle'] = $data; - - $MongoArticle->create(); - $saveResult = $MongoArticle->save($saveData); - $postId = $MongoArticle->id; - - //using $set operator - $MongoArticle->create(); - $updatedata = array( - '_id' => $postId, - 'title' => 'test3', - 'body' => 'aaaa3', - ); - $saveData['MongoArticle'] = $updatedata; - $saveResult = $MongoArticle->save($saveData); // using $set operator - - $this->assertTrue($saveResult); - $this->assertIdentical($MongoArticle->id, $postId); - - $result = null; - $result = $MongoArticle->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual($MongoArticle->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); //update - $this->assertEqual($updatedata['body'], $resultData['body']); //update - $this->assertEqual($data['text'], $resultData['text']); //not update - $this->assertEqual($data['count'], $resultData['count']); //not update - - - - //using $inc operator insted of $set operator - $MongoArticle->create(); - $updatedataInc = array( - '_id' => $postId, - '$inc' => array('count' => 1), - ); - $saveData['MongoArticle'] = $updatedataInc; - $saveResult = $MongoArticle->save($saveData); // using $set operator - - $this->assertTrue($saveResult); - $this->assertIdentical($MongoArticle->id, $postId); - $result = null; - $result = $MongoArticle->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual($MongoArticle->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); //not update - $this->assertEqual($updatedata['body'], $resultData['body']); //not update - $this->assertEqual($data['text'], $resultData['text']); //not update - $this->assertEqual(1, $resultData['count']); //increment - - //using $inc and $push - $MongoArticle->create(); - $updatedataInc = array( - '_id' => $postId, - '$push' => array( - 'comments' => array( - '_id' => new MongoId(), - 'created' => new MongoDate(), - 'vote_count' => 0, - 'user' => 'user1', - 'body' => 'comment', - ) - ), - '$inc' => array('count' => 1), - ); - $saveData['MongoArticle'] = $updatedataInc; - $saveResult = $MongoArticle->save($saveData); // using $set operator - - $this->assertTrue($saveResult); - $this->assertIdentical($MongoArticle->id, $postId); - $result = null; - $result = $MongoArticle->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual($MongoArticle->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); //not update - $this->assertEqual($updatedata['body'], $resultData['body']); //not update - $this->assertEqual($data['text'], $resultData['text']); //not update - $this->assertEqual(2, $resultData['count']); //increment - $this->assertEqual('user1', $resultData['comments'][0]['user']); //push - $this->assertEqual('comment', $resultData['comments'][0]['body']); //push - $this->assertEqual(1, count($resultData['comments'])); //push - $this->assertTrue(!empty($resultData['created'])); - $this->assertTrue(!empty($resultData['modified'])); - - - //no $set operator - $MongoArticle->mongoNoSetOperator = true; - - $MongoArticle->create(); - $updatedata = array( - '_id' => $postId, - 'title' => 'test4', - 'body' => 'aaaa4', - 'count' => '1', - ); - $saveData['MongoArticle'] = $updatedata; - $saveResult = $MongoArticle->save($saveData); - - $this->assertTrue($saveResult); - $this->assertIdentical($MongoArticle->id, $postId); - - $result = null; - $result = $MongoArticle->find('all'); - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual($MongoArticle->id, $resultData['_id']); - $this->assertEqual($updatedata['title'], $resultData['title']); //update - $this->assertEqual($updatedata['body'], $resultData['body']); //update - $this->assertTrue(empty($resultData['text'])); - $this->assertEqual(1, $resultData['count']); - - $MongoArticle->mongoNoSetOperator = null; - - - //use $push - $MongoArticle->create(); - $updatedata = array( - '_id' => $postId, - 'push_column' => array('push1'), - ); - $saveData['MongoArticle'] = $updatedata; - $saveResult = $MongoArticle->save($saveData); //use $set - - $result = $MongoArticle->find('all'); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual('test4', $resultData['title']); // no update - $this->assertEqual(array('push1'), $resultData['push_column']); - - - $MongoArticle->mongoNoSetOperator = '$push'; - $MongoArticle->create(); - $updatedata = array( - '_id' => $postId, - 'push_column' => 'push2', - ); - $saveData['MongoArticle'] = $updatedata; - $saveResult = $MongoArticle->save($saveData); //use $push - - $this->assertTrue($saveResult); - $this->assertIdentical($MongoArticle->id, $postId); - - $result = null; - $result = $MongoArticle->find('all'); - - - $this->assertEqual(1, count($result)); - $resultData = $result[0]['MongoArticle']; - $this->assertEqual($MongoArticle->id, $resultData['_id']); - $this->assertEqual('test4', $resultData['title']); // no update - $this->assertEqual(array('push1','push2'), $resultData['push_column']); //update - - $MongoArticle->mongoNoSetOperator = null; - - - unset($MongoArticle); - - } - - -/** - * Tests groupBy - * - * @return void - * @access public - */ - public function testGroupBy() { - for($i = 0 ; $i < 30 ; $i++) { - $saveData[$i]['Post'] = array( - 'title' => 'test'.$i, - 'body' => 'aaaa'.$i, - 'text' => 'bbbb'.$i, - 'count' => $i, - ); - } - - $saveData[30]['Post'] = array( - 'title' => 'test1', - 'body' => 'aaaa1', - 'text' => 'bbbb1', - 'count' => 1, - ); - $saveData[31]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2', - 'count' => 2, - ); - - $this->Post->create(); - $saveResult = $this->Post->saveAll($saveData); - - $cond_count = 5; - $query = array( - 'key' => array('title' => true ), - 'initial' => array('csum' => 0), - 'reduce' => 'function(obj, prev){prev.csum += 1;}', - 'options' => array( - 'condition' => array('count' => array('$lt' => $cond_count)), - ), - ); - - $mongo = $this->Post->getDataSource(); - $result = $mongo->group($this->Post, $query); - - $this->assertTrue($result['ok'] == 1 && count($result['retval']) > 0); - $this->assertEqual($cond_count, count($result['retval'])); - $this->assertEqual('test0', $result['retval'][0]['title']); - $this->assertEqual('test1', $result['retval'][1]['title']); - $this->assertEqual('test2', $result['retval'][2]['title']); - $this->assertEqual(1, $result['retval'][0]['csum']); - $this->assertEqual(2, $result['retval'][1]['csum']); - $this->assertEqual(2, $result['retval'][2]['csum']); - -} - - - -/** - * Tests query - * Distinct, Group - * - * @return void - * @access public - */ - public function testQuery() { - for($i = 0 ; $i < 30 ; $i++) { - $saveData[$i]['Post'] = array( - 'title' => 'test'.$i, - 'body' => 'aaaa'.$i, - 'text' => 'bbbb'.$i, - 'count' => $i, - ); - } - - $saveData[30]['Post'] = array( - 'title' => 'test1', - 'body' => 'aaaa1', - 'text' => 'bbbb1', - 'count' => 1, - ); - $saveData[31]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2', - 'count' => 2, - ); - - $saveData[32]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2', - 'count' => 32, - ); - - $this->Post->create(); - $saveResult = $this->Post->saveAll($saveData); - - - //using query() Distinct - $params = array( - 'distinct' => 'posts', - 'key' => 'count', - ); - $result = $this->Post->query( $params ); - $this->assertEqual(1, $result['values'][1]); - $this->assertEqual(2, $result['values'][2]); - $this->assertEqual(32, $result['values'][30]); - - - //using query() group - $cond_count = 5; - $reduce = "function(obj,prev){prev.csum++;}"; - $params = array( - 'group'=>array( - 'ns'=>'posts', - 'cond'=>array('count' => array('$lt' => $cond_count)), - 'key'=>array('title'=>true), - 'initial'=>array('csum'=>0), - '$reduce'=>$reduce - ) - ); - - $result = $this->Post->query( $params ); - - $this->assertTrue($result['ok'] == 1 && count($result['retval']) > 0); - $this->assertEqual($cond_count, count($result['retval'])); - $this->assertEqual('test0', $result['retval'][0]['title']); - $this->assertEqual('test1', $result['retval'][1]['title']); - $this->assertEqual('test2', $result['retval'][2]['title']); - $this->assertEqual(1, $result['retval'][0]['csum']); - $this->assertEqual(2, $result['retval'][1]['csum']); - $this->assertEqual(2, $result['retval'][2]['csum']); - -} - -/** - * Tests MapReduce - * - * @return void - * @access public - */ -public function testMapReduce() { - for($i = 0 ; $i < 30 ; $i++) { - $saveData[$i]['Post'] = array( - 'title' => 'test'.$i, - 'body' => 'aaaa'.$i, - 'text' => 'bbbb'.$i, - 'count' => $i, - ); - } - - $saveData[30]['Post'] = array( - 'title' => 'test1', - 'body' => 'aaaa1', - 'text' => 'bbbb1', - 'count' => 1, - ); - $saveData[31]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2', - 'count' => 2, - ); - - $saveData[32]['Post'] = array( - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2', - 'count' => 32, - ); - - $this->Post->create(); - $saveResult = $this->Post->saveAll($saveData); - - $map = new MongoCode("function() { emit(this.title,1); }"); - $reduce = new MongoCode("function(k, vals) { ". - "var sum = 0;". - "for (var i in vals) {". - "sum += vals[i];". - "}". - "return sum; }" - ); - - $params = array( - "mapreduce" => "posts", - "map" => $map, - "reduce" => $reduce, - "query" => array( - "count" => array('$gt' => -2), - ), - 'out' => 'test_mapreduce_posts', - ); - - $mongo = $this->Post->getDataSource(); - $results = $mongo->mapReduce($params); - - $posts = array(); - foreach ($results as $post) { - $posts[$post['_id']] = $post['value']; - } - - $this->assertEqual(30, count($posts)); - $this->assertEqual(1, $posts['test0']); - $this->assertEqual(2, $posts['test1']); - $this->assertEqual(3, $posts['test2']); - $this->assertEqual(1, $posts['test3']); - - - //set timeout - $results = $mongo->mapReduce($params, 100); //set timeout 100msec - $posts = array(); - foreach ($results as $post) { - $posts[$post['_id']] = $post['value']; - } - - $this->assertEqual(30, count($posts)); - $this->assertEqual(1, $posts['test0']); - $this->assertEqual(2, $posts['test1']); - $this->assertEqual(3, $posts['test2']); - $this->assertEqual(1, $posts['test3']); - - - //get results as inline data - $version = $this->getMongodVersion(); - if( $version >= '1.7.4') { - $params = array( - "mapreduce" => "posts", - "map" => $map, - "reduce" => $reduce, - "query" => array( - "count" => array('$gt' => -2), - ), - 'out' => array('inline' => 1), - ); - - $results = $mongo->mapReduce($params); - - $posts = array(); - foreach ($results as $post) { - $posts[$post['_id']] = $post['value']; - } - - $this->assertEqual(30, count($posts)); - $this->assertEqual(1, $posts['test0']); - $this->assertEqual(2, $posts['test1']); - $this->assertEqual(3, $posts['test2']); - $this->assertEqual(1, $posts['test3']); - } - - -} - - - -/** - * testSort method - * - * @return void - * @access public - */ - public function testSort() { - $data = array( - 'title' => 'AAA', - 'body' => 'aaaa', - 'text' => 'aaaa' - ); - $saveData['Post'] = $data; - $this->Post->create(); - $this->Post->save($saveData); - - $data = array( - 'title' => 'CCC', - 'body' => 'cccc', - 'text' => 'cccc' - ); - $saveData['Post'] = $data; - $this->Post->create(); - $this->Post->save($saveData); - - $this->Post->create(); - $data = array( - 'title' => 'BBB', - 'body' => 'bbbb', - 'text' => 'bbbb' - ); - $saveData['Post'] = $data; - $this->Post->create(); - $this->Post->save($saveData); - - $expected = array('AAA', 'BBB', 'CCC'); - $result = $this->Post->find('all', array( - 'fields' => array('_id', 'title'), - 'order' => array('title' => 1) - )); - $result = Set::extract($result, '/Post/title'); - - $this->assertEqual($expected, $result); - $result = $this->Post->find('all', array( - 'fields' => array('_id', 'title'), - 'order' => array('title' => 'ASC') - )); - $result = Set::extract($result, '/Post/title'); - - $expected = array_reverse($expected); - $result = $this->Post->find('all', array( - 'fields' => array('_id', 'title'), - 'order' => array('title' => '-1') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - - $result = $this->Post->find('all', array( - 'fields' => array('_id', 'title'), - 'order' => array('title' => 'DESC') - )); - $result = Set::extract($result, '/Post/title'); - $this->assertEqual($expected, $result); - } - -/** - * testSchemaless method - * - * Test you can save to a model without specifying mongodb. - * - * @return void - * @access public - */ - public function testSchemaless() { - $toSave = array( - 'title' => 'A test article', - 'body' => str_repeat('Lorum ipsum ', 100), - 'tags' => array( - 'one', - 'two', - 'three' - ), - 'modified' => null, - 'created' => null - ); - - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(); - $this->assertTrue($MongoArticle->save($toSave), 'Saving with no defined schema failed'); - - $expected = array_intersect_key($toSave, array_flip(array('title', 'body', 'tags'))); - $result = $MongoArticle->read(array('title', 'body', 'tags')); - unset ($result['MongoArticle']['_id']); // prevent auto added field from screwing things up - $this->assertEqual($expected, $result['MongoArticle']); - - $toSave = array( - 'title' => 'Another test article', - 'body' => str_repeat('Lorum pipsum ', 100), - 'tags' => array( - 'four', - 'five', - 'six' - ), - 'starts' => date('Y-M-d H:i:s'), - 'modified' => null, - 'created' => null - ); - $MongoArticle->create(); - $this->assertTrue($MongoArticle->save($toSave), 'Saving with no defined schema failed'); - $starts = $MongoArticle->field('starts'); - $this->assertEqual($toSave['starts'], $starts); - } - -/** - * testSpecificId method - * - * Test you can save specifying your own _id values - and update by _id - * - * @return void - * @access public - */ - public function testSpecificId() { - $data = array( - '_id' => 123, - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb' - ); - $saveData['Post'] = $data; - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $found = $this->Post->find('first', array( - 'fields' => array('_id', 'title', 'body', 'text'), - 'conditions' => array('_id' => 123) - )); - - $this->assertEqual($found, $saveData); - - $data = array( - '_id' => 123, - 'title' => 'test2', - 'body' => 'aaaa2', - 'text' => 'bbbb2' - ); - $saveData['Post'] = $data; - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $found = $this->Post->find('first', array( - 'fields' => array('_id', 'title', 'body', 'text'), - 'conditions' => array('_id' => 123) - )); - $this->assertEqual($found, $saveData); - } - -/** - * testOr method - * - * @return void - * @access public - */ - public function testOr() { - $mongoVersion = $this->mongodb->execute('db.version()'); - $shouldSkip = version_compare($mongoVersion, '1.5.3', '<'); - if ($this->skipIf($shouldSkip, '$or tests require at least version mongo version 1.5.3, currently using ' . $mongoVersion . ' %s')) { - return; - } - - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(); - - for ($i = 1; $i <= 20; $i++) { - $data = array( - 'title' => "Article $i", - 'subtitle' => "Sub Article $i", - ); - $saveData['MongoArticle'] = $data; - $MongoArticle->create(); - $MongoArticle->save($saveData); - } - $expected = $MongoArticle->find('all', array( - 'conditions' => array( - 'title' => array('$in' => array('Article 1', 'Article 10')) - ), - 'order' => array('number' => 'ASC') - )); - $this->assertTrue(count($expected), 2); - - $result = $MongoArticle->find('all', array( - 'conditions' => array( - '$or' => array( - array('title' => 'Article 1'), - array('title' => 'Article 10'), - ) - ), - 'order' => array('number' => 'ASC') - )); - $this->assertEqual($result, $expected); - } - -/** - * testDeleteAll method - * - * @return void - * @access public - */ - function testDeleteAll($cascade = true) { - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); - $MongoArticle->save(); - - $MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); - $MongoArticle->save(); - - $MongoArticle->create(array('title' => 'Article 3', 'cat' => 2)); - $MongoArticle->save(); - - $MongoArticle->create(array('title' => 'Article 4', 'cat' => 2)); - $MongoArticle->save(); - - $count = $MongoArticle->find('count'); - $this->assertEqual($count, 4); - - $MongoArticle->deleteAll(array('cat' => 2), $cascade); - - $count = $MongoArticle->find('count'); - $this->assertEqual($count, 2); - - $MongoArticle->deleteAll(true, $cascade); - - $count = $MongoArticle->find('count'); - $this->assertFalse($count); - } - -/** - * testDeleteAllNoCascade method - * - * @return void - * @access public - */ - function testDeleteAllNoCascade() { - $this->testDeleteAll(false); - } - -/** - * testRegexSearch method - * - * @return void - * @access public - */ - public function testRegexSearch() { - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); - $MongoArticle->save(); - $MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); - $MongoArticle->save(); - $MongoArticle->create(array('title' => 'Article 3', 'cat' => 2)); - $MongoArticle->save(); - - $count=$MongoArticle->find('count',array( - 'conditions'=>array( - 'title'=>'Article 2' - ) - )); - $this->assertEqual($count, 1); - - $count = $MongoArticle->find('count',array( - 'conditions'=>array( - 'title'=> new MongoRegex('/^Article/') - ) - )); - $this->assertEqual($count, 3); - } - -/** - * testEmptyReturn method - * inserts article into table. searches for a different non existing article. should return an empty array in the same that that it does from other datasources - * @return void - * @access public - */ - public function testEmptyReturn() { - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); - $MongoArticle->save(); - $articles=$MongoArticle->find('all',array( - 'conditions'=>array( - 'title'=>'Article 2' - ) - )); - $this->assertTrue(is_array($articles)); - $articles=$MongoArticle->find('first',array( - 'conditions'=>array( - 'title'=>'Article 2' - ) - )); - $this->assertFalse(is_array($articles)); - } - -/** - * Tests isUnique validation. - * - * @return void - * @access public - */ - public function testSaveUniques() { - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb', - 'uniquefield1'=>'uniquenameforthistest' - ); - $saveData['Post'] = $data; - - $this->Post->Behaviors->attach('Mongodb.SqlCompatible'); - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - - $data = array( - 'title' => 'test', - 'body' => 'asdf', - 'text' => 'bbbb', - 'uniquefield1'=>'uniquenameforthistest' - ); - $saveData['Post'] = $data; - - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertFalse($saveResult); - } - -/** - * Tests isUnique validation with custom validation. - * - * @return void - * @access public - */ - public function testSaveUniquesCustom() { - $data = array( - 'title' => 'test', - 'body' => 'aaaa', - 'text' => 'bbbb', - 'uniquefield2' => 'someunqiuename' - ); - $saveData['Post'] = $data; - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertTrue($saveResult); - $data = array( - 'title' => 'test', - 'body' => 'asdf', - 'text' => 'bbbb', - 'uniquefield2' => 'someunqiuename' - ); - $saveData['Post'] = $data; - $this->Post->create(); - $saveResult = $this->Post->save($saveData); - $this->assertFalse($saveResult); - } - - public function testReturn() { - $MongoArticle = ClassRegistry::init('MongoArticle'); - $MongoArticle->create(array('title' => 'Article 1', 'cat' => 1)); - $MongoArticle->save(); - $MongoArticle->create(array('title' => 'Article 2', 'cat' => 1)); - $MongoArticle->save(); - - $return = $MongoArticle->find('all', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('first', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('first', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('count', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_int($return)); - - $return = $MongoArticle->find('neighbors', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('list', array( - 'conditions' => array( - 'title' => 'Article 2' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('all', array( - 'conditions' => array( - 'title' => 'Doesn\'t exist' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('first', array( - 'conditions' => array( - 'title' => 'Doesn\'t exist' - ) - )); - $this->assertFalse($return); - - $return = $MongoArticle->find('count', array( - 'conditions' => array( - 'title' => 'Doesn\'t exist' - ) - )); - $this->assertTrue(is_int($return)); - - $return = $MongoArticle->find('neighbors', array( - 'conditions' => array( - 'title' => 'Doesn\'t exist' - ) - )); - $this->assertTrue(is_array($return)); - - $return = $MongoArticle->find('list', array( - 'conditions' => array( - 'title' => 'Doesn\'t exist' - ) - )); - $this->assertTrue(is_array($return)); - - } -} \ No newline at end of file From 437a15e2c3a5037414e2c3e84dde7aee18f9e8c1 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 08:58:00 +1100 Subject: [PATCH 06/33] Remove outdated docs. --- ...els-behaviors_behaviorsschemaless.php.html | 109 - ...behaviors_behaviorssql_compatible.php.html | 290 --- docs/blank.html | 13 - docs/classtrees_mongodb.html | 28 - docs/elementindex.html | 674 ------ docs/elementindex_mongodb.html | 671 ------ docs/errors.html | 22 - docs/index.html | 24 - docs/li_mongodb.html | 60 - docs/media/banner.css | 32 - docs/media/stylesheet.css | 142 -- .../SchemalessBehavior.html | 273 --- .../SqlCompatibleBehavior.html | 443 ---- .../_behaviors---schemaless.php.html | 69 - .../_behaviors---sql_compatible.php.html | 69 - .../MongodbSource.html | 1875 ----------------- .../_datasources---mongodb_source.php.html | 109 - docs/packages.html | 29 - 18 files changed, 4932 deletions(-) delete mode 100644 docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorsschemaless.php.html delete mode 100644 docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorssql_compatible.php.html delete mode 100644 docs/blank.html delete mode 100644 docs/classtrees_mongodb.html delete mode 100644 docs/elementindex.html delete mode 100644 docs/elementindex_mongodb.html delete mode 100644 docs/errors.html delete mode 100644 docs/index.html delete mode 100644 docs/li_mongodb.html delete mode 100644 docs/media/banner.css delete mode 100644 docs/media/stylesheet.css delete mode 100644 docs/mongodb/mongodb-models-behaviors/SchemalessBehavior.html delete mode 100644 docs/mongodb/mongodb-models-behaviors/SqlCompatibleBehavior.html delete mode 100644 docs/mongodb/mongodb-models-behaviors/_behaviors---schemaless.php.html delete mode 100644 docs/mongodb/mongodb-models-behaviors/_behaviors---sql_compatible.php.html delete mode 100644 docs/mongodb/mongodb-models-datasources/MongodbSource.html delete mode 100644 docs/mongodb/mongodb-models-datasources/_datasources---mongodb_source.php.html delete mode 100644 docs/packages.html diff --git a/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorsschemaless.php.html b/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorsschemaless.php.html deleted file mode 100644 index 5f8ef30..0000000 --- a/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorsschemaless.php.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - File Source for schemaless.php - - - - -

Source for file schemaless.php

-

Documentation is available at schemaless.php

-
-
  1. <?php
  2. -
  3. /**
  4. -
  5.  * Schemaless behavior.
  6. -
  7.  *
  8. -
  9.  * Adds functionality specific to MongoDB/schemaless dbs
  10. -
  11.  * Allow /not/ specifying the model's schema, and derive it (for cake-compatibility) from the data
  12. -
  13.  * being saved. Note that used carelessly this is a pretty dangerous thing to allow - means a user
  14. -
  15.  * can modify input forms adding whatever fields they like (unless you'er using the security
  16. -
  17.  * component) and fill your db with their junk.
  18. -
  19.  *
  20. -
  21.  * PHP version 5
  22. -
  23.  *
  24. -
  25.  * Copyright (c) 2010, Andy Dawson
  26. -
  27.  *
  28. -
  29.  * Licensed under The MIT License
  30. -
  31.  * Redistributions of files must retain the above copyright notice.
  32. -
  33.  *
  34. -
  35.  * @filesource
  36. -
  37.  * @copyright     Copyright (c) 2010, Andy Dawson
  38. -
  39.  * @link          www.ad7six.com
  40. -
  41.  * @package       mongodb
  42. -
  43.  * @subpackage    mongodb.models.behaviors
  44. -
  45.  * @since         v 1.0 (24-May-2010)
  46. -
  47.  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
  48. -
  49.  */
  50. -
  51.  
  52. -
  53. /**
  54. -
  55.  * SchemalessBehavior class
  56. -
  57.  *
  58. -
  59.  * @uses          ModelBehavior
  60. -
  61.  * @package       mongodb
  62. -
  63.  * @subpackage    mongodb.models.behaviors
  64. -
  65.  */
  66. -
  67. class SchemalessBehavior extends ModelBehavior {
  68. -
  69.  
  70. -
  71. /**
  72. -
  73.  * name property
  74. -
  75.  *
  76. -
  77.  * @var string 'Schemaless'
  78. -
  79.  * @access public
  80. -
  81.  */
  82. -
  83.     public $name = 'Schemaless';
  84. -
  85.  
  86. -
  87. /**
  88. -
  89.  * settings property
  90. -
  91.  *
  92. -
  93.  * @var array 
  94. -
  95.  * @access public
  96. -
  97.  */
  98. -
  99.     public $settings = array();
  100. -
  101.  
  102. -
  103. /**
  104. -
  105.  * defaultSettings property
  106. -
  107.  *
  108. -
  109.  * @var array 
  110. -
  111.  * @access protected
  112. -
  113.  */
  114. -
  115.     protected $_defaultSettings = array(
  116. -
  117.     );
  118. -
  119.  
  120. -
  121. /**
  122. -
  123.  * setup method
  124. -
  125.  *
  126. -
  127.  * Don't currently have any settings at all - disabled
  128. -
  129.  *
  130. -
  131.  * @param mixed $Model 
  132. -
  133.  * @param array $config array()
  134. -
  135.  * @return void 
  136. -
  137.  * @access public
  138. -
  139.  */
  140. -
  141.     public function setup(&$Model$config array()) {
  142. -
  143.         //$this->settings[$Model->alias] = array_merge($this->_defaultSettings, $config);
  144. -
  145.     }
  146. -
  147.  
  148. -
  149. /**
  150. -
  151.  * beforeSave method
  152. -
  153.  *
  154. -
  155.  * Set the schema to allow saving whatever has been passed
  156. -
  157.  *
  158. -
  159.  * @param mixed $Model 
  160. -
  161.  * @return void 
  162. -
  163.  * @access public
  164. -
  165.  */
  166. -
  167.     public function beforeSave(&$Model{
  168. -
  169.         $Model->cacheSources false;
  170. -
  171.         $Model->schema(true);
  172. -
  173.         return true;
  174. -
  175.     }
  176. -
  177. }
  178. -
-
-

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

- - \ No newline at end of file diff --git a/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorssql_compatible.php.html b/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorssql_compatible.php.html deleted file mode 100644 index 8181790..0000000 --- a/docs/__filesource/fsource_mongodb_mongodb-models-behaviors_behaviorssql_compatible.php.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - File Source for sql_compatible.php - - - - -

Source for file sql_compatible.php

-

Documentation is available at sql_compatible.php

-
-
  1. <?php
  2. -
  3. /**
  4. -
  5.  * Sql Compatible.
  6. -
  7.  *
  8. -
  9.  * Attach this behavior to be able to query mongo DBs without using mongo specific syntax.
  10. -
  11.  * If you don't need this behavior don't attach it and save a few cycles
  12. -
  13.  *
  14. -
  15.  * PHP version 5
  16. -
  17.  *
  18. -
  19.  * Copyright (c) 2010, Andy Dawson
  20. -
  21.  *
  22. -
  23.  * Licensed under The MIT License
  24. -
  25.  * Redistributions of files must retain the above copyright notice.
  26. -
  27.  *
  28. -
  29.  * @filesource
  30. -
  31.  * @copyright     Copyright (c) 2010, Andy Dawson
  32. -
  33.  * @link          www.ad7six.com
  34. -
  35.  * @package       mongodb
  36. -
  37.  * @subpackage    mongodb.models.behaviors
  38. -
  39.  * @since         v 1.0 (24-May-2010)
  40. -
  41.  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
  42. -
  43.  */
  44. -
  45.  
  46. -
  47. /**
  48. -
  49.  * SqlCompatibleBehavior class
  50. -
  51.  *
  52. -
  53.  * @uses          ModelBehavior
  54. -
  55.  * @package       mongodb
  56. -
  57.  * @subpackage    mongodb.models.behaviors
  58. -
  59.  */
  60. -
  61. class SqlCompatibleBehavior extends ModelBehavior {
  62. -
  63.  
  64. -
  65. /**
  66. -
  67.  * name property
  68. -
  69.  *
  70. -
  71.  * @var string 'SqlCompatible'
  72. -
  73.  * @access public
  74. -
  75.  */
  76. -
  77.     public $name = 'SqlCompatible';
  78. -
  79.  
  80. -
  81. /**
  82. -
  83.  * Runtime settings
  84. -
  85.  *
  86. -
  87.  * Keyed on model alias
  88. -
  89.  *
  90. -
  91.  * @var array 
  92. -
  93.  * @access public
  94. -
  95.  */
  96. -
  97.     public $settings = array();
  98. -
  99.  
  100. -
  101. /**
  102. -
  103.  * defaultSettings property
  104. -
  105.  *
  106. -
  107.  * @var array 
  108. -
  109.  * @access protected
  110. -
  111.  */
  112. -
  113.     protected $_defaultSettings = array(
  114. -
  115.         'convertDates' => true,
  116. -
  117.         'operators' => array(
  118. -
  119.             '!=' => '$ne',
  120. -
  121.             '>' => '$gt',
  122. -
  123.             '>=' => '$gte',
  124. -
  125.             '<' => '$lt',
  126. -
  127.             '<=' => '$lte',
  128. -
  129.             'IN' => '$in',
  130. -
  131.             'NOT' => '$not',
  132. -
  133.             'NOT IN' => '$nin'
  134. -
  135.         )
  136. -
  137.     );
  138. -
  139.  
  140. -
  141. /**
  142. -
  143.  * setup method
  144. -
  145.  *
  146. -
  147.  * Allow overriding the operator map
  148. -
  149.  *
  150. -
  151.  * @param mixed $Model 
  152. -
  153.  * @param array $config array()
  154. -
  155.  * @return void 
  156. -
  157.  * @access public
  158. -
  159.  */
  160. -
  161.     public function setup(&$Model$config array()) {
  162. -
  163.         $this->settings[$Model->aliasarray_merge($this->_defaultSettings$config);
  164. -
  165.     }
  166. -
  167.  
  168. -
  169. /**
  170. -
  171.  * If requested, convert dates from MongoDate objects to standard date strings
  172. -
  173.  *
  174. -
  175.  * @param mixed $Model 
  176. -
  177.  * @param mixed $results 
  178. -
  179.  * @param mixed $primary 
  180. -
  181.  * @return void 
  182. -
  183.  * @access public
  184. -
  185.  */
  186. -
  187.     public function afterFind(&$Model$results$primary{
  188. -
  189.         if ($this->settings[$Model->alias]['convertDates']{
  190. -
  191.             $this->convertDates($results);
  192. -
  193.         }
  194. -
  195.         return $results;
  196. -
  197.     }
  198. -
  199.  
  200. -
  201. /**
  202. -
  203.  * beforeFind method
  204. -
  205.  *
  206. -
  207.  * If conditions are an array ensure they are mongified
  208. -
  209.  *
  210. -
  211.  * @param mixed $Model 
  212. -
  213.  * @param mixed $query 
  214. -
  215.  * @return void 
  216. -
  217.  * @access public
  218. -
  219.  */
  220. -
  221.     public function beforeFind(&$Model$query{
  222. -
  223.         if (is_array($query['conditions']&& $this->_translateConditions($Model$query['conditions'])) {
  224. -
  225.             return $query;
  226. -
  227.         }
  228. -
  229.         return true;
  230. -
  231.     }
  232. -
  233.  
  234. -
  235. /**
  236. -
  237.  * Convert MongoDate objects to strings for the purpose of view simplicity
  238. -
  239.  *
  240. -
  241.  * @param mixed $results 
  242. -
  243.  * @return void 
  244. -
  245.  * @access public
  246. -
  247.  */
  248. -
  249.     public function convertDates(&$results{
  250. -
  251.         if (is_array($results)) {
  252. -
  253.             foreach($results as &$row{
  254. -
  255.                 $this->convertDates($row);
  256. -
  257.             }
  258. -
  259.         elseif (is_a($results'MongoDate')) {
  260. -
  261.             $results date('Y-M-d h:i:s'$results->sec);
  262. -
  263.         }
  264. -
  265.     }
  266. -
  267.  
  268. -
  269. /**
  270. -
  271.  * translateConditions method
  272. -
  273.  *
  274. -
  275.  * Loop on conditions and desqlify them
  276. -
  277.  *
  278. -
  279.  * @param mixed $Model 
  280. -
  281.  * @param mixed $conditions 
  282. -
  283.  * @return void 
  284. -
  285.  * @access protected
  286. -
  287.  */
  288. -
  289.     protected function _translateConditions(&$Model&$conditions{
  290. -
  291.         $return false;
  292. -
  293.         foreach($conditions as $key => &$value{
  294. -
  295.             $uKey strtoupper($key);
  296. -
  297.             if (substr($uKey-5=== 'NOT IN'{
  298. -
  299.                 // 'Special' case because it has a space in it, and it's the whole key
  300. -
  301.                 $conditions[substr($key0-5)]['$nin'$value;
  302. -
  303.                 unset($conditions[$key]);
  304. -
  305.                 $return true;
  306. -
  307.                 continue;
  308. -
  309.             }
  310. -
  311.             if ($uKey === 'OR'{
  312. -
  313.                 unset($conditions[$key]);
  314. -
  315.                 foreach($value as $key => $part{
  316. -
  317.                     $part array($key => $part);
  318. -
  319.                     $this->_translateConditions($Model$part);
  320. -
  321.                     $conditions['$or'][$part;
  322. -
  323.                 }
  324. -
  325.                 $return true;
  326. -
  327.                 continue;
  328. -
  329.             }
  330. -
  331.             if ($key === $Model->primaryKey && is_array($value)) {
  332. -
  333.                 //_id=>array(1,2,3) pattern, set  $in operator
  334. -
  335.                 $isMongoOperator false;
  336. -
  337.                 foreach($value as $idKey => $idValue{
  338. -
  339.                     //check a mongo operator exists
  340. -
  341.                     if(substr($idKey,0,1=== '$'{
  342. -
  343.                         $isMongoOperator true;
  344. -
  345.                         continue;
  346. -
  347.                     }
  348. -
  349.                 }
  350. -
  351.                 unset($idKey$idValue);
  352. -
  353.                 if($isMongoOperator === false{
  354. -
  355.                     $conditions[$keyarray('$in' => $value);
  356. -
  357.                 }
  358. -
  359.                 $return true;
  360. -
  361.                 continue;
  362. -
  363.             }
  364. -
  365.             if (substr($uKey-3=== 'NOT'{
  366. -
  367.                 // 'Special' case because it's awkward
  368. -
  369.                 $childKey key($value);
  370. -
  371.                 $childValue current($value);
  372. -
  373.  
  374. -
  375.                 if (in_array(substr($childKey-1)array('>''<''='))) {
  376. -
  377.                     $parts explode(' '$childKey);
  378. -
  379.                     $operator array_pop($parts);
  380. -
  381.                     if ($operator $this->_translateOperator($Model$operator)) {
  382. -
  383.                         $childKey implode(' '$parts);
  384. -
  385.                     }
  386. -
  387.                 else {
  388. -
  389.                     $conditions[$childKey]['$nin'= (array)$childValue;
  390. -
  391.                     unset($conditions['NOT']);
  392. -
  393.                     $return true;
  394. -
  395.                     continue;
  396. -
  397.                 }
  398. -
  399.  
  400. -
  401.                 $conditions[$childKey]['$not'][$operator$childValue;
  402. -
  403.                 unset($conditions['NOT']);
  404. -
  405.                 $return true;
  406. -
  407.                 continue;
  408. -
  409.             }
  410. -
  411.             if (substr($uKey-5=== ' LIKE'{
  412. -
  413.                 // 'Special' case because it's awkward
  414. -
  415.                 if ($value[0=== '%'{
  416. -
  417.                     $value substr($value1);
  418. -
  419.                 else {
  420. -
  421.                     $value '^' $value;
  422. -
  423.                 }
  424. -
  425.                 if (substr($value-1=== '%'{
  426. -
  427.                     $value substr($value0-1);
  428. -
  429.                 else {
  430. -
  431.                     $value .= '$';
  432. -
  433.                 }
  434. -
  435.                 $value str_replace('%''.*'$value);
  436. -
  437.  
  438. -
  439.                 $conditions[substr($key0-5)new MongoRegex("/$value/i");
  440. -
  441.                 unset($conditions[$key]);
  442. -
  443.                 $return true;
  444. -
  445.                 continue;
  446. -
  447.             }
  448. -
  449.  
  450. -
  451.             if (!in_array(substr($key-1)array('>''<''='))) {
  452. -
  453.                 $return true;
  454. -
  455.                 continue;
  456. -
  457.             }
  458. -
  459.             if (is_numeric($key && is_array($value))) {
  460. -
  461.                 if ($this->_translateConditions($Model$value)) {
  462. -
  463.                     $return true;
  464. -
  465.                     continue;
  466. -
  467.                 }
  468. -
  469.             }
  470. -
  471.             $parts explode(' '$key);
  472. -
  473.             $operator array_pop($parts);
  474. -
  475.             if ($operator $this->_translateOperator($Model$operator)) {
  476. -
  477.                 $newKey implode(' '$parts);
  478. -
  479.                 $conditions[$newKey][$operator$value;
  480. -
  481.                 unset($conditions[$key]);
  482. -
  483.                 $return true;
  484. -
  485.             }
  486. -
  487.             if (is_array($value)) {
  488. -
  489.                 if ($this->_translateConditions($Model$value)) {
  490. -
  491.                     $return true;
  492. -
  493.                     continue;
  494. -
  495.                 }
  496. -
  497.             }
  498. -
  499.         }
  500. -
  501.         return $return;
  502. -
  503.     }
  504. -
  505.  
  506. -
  507. /**
  508. -
  509.  * translateOperator method
  510. -
  511.  *
  512. -
  513.  * Use the operator map for the model and return what the db really wants to hear
  514. -
  515.  *
  516. -
  517.  * @param mixed $Model 
  518. -
  519.  * @param mixed $operator 
  520. -
  521.  * @return string 
  522. -
  523.  * @access protected
  524. -
  525.  */
  526. -
  527.     protected function _translateOperator($Model$operator{
  528. -
  529.         if (!empty($this->settings[$Model->alias]['operators'][$operator])) {
  530. -
  531.             return $this->settings[$Model->alias]['operators'][$operator];
  532. -
  533.         }
  534. -
  535.         return '';
  536. -
  537.     }
  538. -
  539. }
  540. -
-
-

- Documentation generated on Tue, 26 Jul 2011 01:09:02 +0900 by phpDocumentor 1.4.3 -

- - \ No newline at end of file diff --git a/docs/blank.html b/docs/blank.html deleted file mode 100644 index 927a8fd..0000000 --- a/docs/blank.html +++ /dev/null @@ -1,13 +0,0 @@ - - - Generated Documentation - - - - -

Generated Documentation

-Welcome to mongodb!
-
-This documentation was generated by phpDocumentor v1.4.3
- - \ No newline at end of file diff --git a/docs/classtrees_mongodb.html b/docs/classtrees_mongodb.html deleted file mode 100644 index 722d39b..0000000 --- a/docs/classtrees_mongodb.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -

- -

-

Root class DboSource

- - -

Root class ModelBehavior

- - -

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

- - \ No newline at end of file diff --git a/docs/elementindex.html b/docs/elementindex.html deleted file mode 100644 index 7406437..0000000 --- a/docs/elementindex.html +++ /dev/null @@ -1,674 +0,0 @@ - - - - - - - - - - - -

Full index

-

Package indexes

- -
-
- a - b - c - d - e - g - i - l - m - n - q - r - s - t - u - v - _ -
- - -
-
a
- -
-
-
-
- afterFind -
-
- -
If requested, convert dates from MongoDate objects to standard date strings
-
-
- -
-
b
- -
-
-
-
- beforeFind -
-
- -
beforeFind method
-
-
- beforeSave -
-
- -
beforeSave method
-
-
- begin -
-
-
MongodbSource::begin() in mongodb_source.php
-
begin method
-
-
- -
-
c
- -
-
-
-
- $columns -
-
-
MongodbSource::$columns in mongodb_source.php
-
column definition
-
-
- $connected -
-
-
MongodbSource::$connected in mongodb_source.php
-
Are we connected to the DataSource?
-
-
- calculate -
-
-
MongodbSource::calculate() in mongodb_source.php
-
Calculate
-
-
- close -
-
-
MongodbSource::close() in mongodb_source.php
-
Close database connection
-
-
- commit -
-
-
MongodbSource::commit() in mongodb_source.php
-
commit method
-
-
- connect -
-
-
MongodbSource::connect() in mongodb_source.php
-
Connect to the database
-
-
- convertDates -
-
- -
Convert MongoDate objects to strings for the purpose of view simplicity
-
-
- create -
-
-
MongodbSource::create() in mongodb_source.php
-
Create Data
-
-
- createConnectionName -
-
- -
create connection name.
-
-
- createSchema -
-
-
MongodbSource::createSchema() in mongodb_source.php
-
createSchema method
-
-
- -
-
d
- -
-
-
-
- delete -
-
-
MongodbSource::delete() in mongodb_source.php
-
Delete Data
-
-
- deriveSchemaFromData -
-
- -
deriveSchemaFromData method
-
-
- describe -
-
-
MongodbSource::describe() in mongodb_source.php
-
Describe
-
-
- disconnect -
-
-
MongodbSource::disconnect() in mongodb_source.php
-
Disconnect from the database
-
-
- distinct -
-
-
MongodbSource::distinct() in mongodb_source.php
-
distinct method
-
-
- dropSchema -
-
-
MongodbSource::dropSchema() in mongodb_source.php
-
dropSchema method
-
-
- -
-
e
- -
-
-
-
- ensureIndex -
-
-
MongodbSource::ensureIndex() in mongodb_source.php
-
ensureIndex method
-
-
- execute -
-
-
MongodbSource::execute() in mongodb_source.php
-
execute method
-
-
- -
-
g
- -
-
-
-
- getMongoCollection -
-
- -
get MongoDB Collection Object
-
-
- getMongoDb -
-
-
MongodbSource::getMongoDb() in mongodb_source.php
-
get MongoDB Object
-
-
- group -
-
-
MongodbSource::group() in mongodb_source.php
-
group method
-
-
- -
-
i
- -
-
-
-
- insertMulti -
-
-
MongodbSource::insertMulti() in mongodb_source.php
-
Inserts multiple values into a table
-
-
- isConnected -
-
-
MongodbSource::isConnected() in mongodb_source.php
-
check connection to the database
-
-
- isInterfaceSupported -
-
- -
isInterfaceSupported method
-
-
- -
-
l
- -
-
-
-
- listSources -
-
-
MongodbSource::listSources() in mongodb_source.php
-
Get list of available Collections
-
-
- logQuery -
-
-
MongodbSource::logQuery() in mongodb_source.php
-
logQuery method
-
-
- -
-
m
- -
-
-
-
- mongodb_source.php -
-
-
mongodb_source.php in mongodb_source.php
-
-
- mapReduce -
-
-
MongodbSource::mapReduce() in mongodb_source.php
-
mapReduce
-
-
- MongoDbDateFormatter -
-
-
MongoDbDateFormatter() in mongodb_source.php
-
MongoDbDateFormatter method
-
-
- MongodbSource -
-
-
MongodbSource in mongodb_source.php
-
MongoDB Source
-
-
- -
-
n
- -
-
-
-
- $name -
-
-
SqlCompatibleBehavior::$name in sql_compatible.php
-
name property
-
-
- $name -
-
-
SchemalessBehavior::$name in schemaless.php
-
name property
-
-
- name -
-
-
MongodbSource::name() in mongodb_source.php
-
Quotes identifiers.
-
-
- -
-
q
- -
-
-
-
- query -
-
-
MongodbSource::query() in mongodb_source.php
-
query method If call getMongoDb() from model, this method call getMongoDb().
-
-
- -
-
r
- -
-
-
-
- read -
-
-
MongodbSource::read() in mongodb_source.php
-
Read Data
-
-
- rollback -
-
-
MongodbSource::rollback() in mongodb_source.php
-
rollback method
-
-
- -
-
s
- -
-
-
-
- $settings -
-
-
SqlCompatibleBehavior::$settings in sql_compatible.php
-
Runtime settings
-
-
- $settings -
-
- -
settings property
-
-
- schemaless.php -
-
-
schemaless.php in schemaless.php
-
-
- sql_compatible.php -
-
-
sql_compatible.php in sql_compatible.php
-
-
- SchemalessBehavior -
-
-
SchemalessBehavior in schemaless.php
-
SchemalessBehavior class
-
-
- setTimeout -
-
-
MongodbSource::setTimeout() in mongodb_source.php
-
setTimeout Method
-
-
- setup -
-
-
SqlCompatibleBehavior::setup() in sql_compatible.php
-
setup method
-
-
- setup -
-
-
SchemalessBehavior::setup() in schemaless.php
-
setup method
-
-
- SqlCompatibleBehavior -
-
-
SqlCompatibleBehavior in sql_compatible.php
-
SqlCompatibleBehavior class
-
-
- -
-
t
- -
-
-
-
- truncate -
-
-
MongodbSource::truncate() in mongodb_source.php
-
Deletes all the records in a table
-
-
- -
-
u
- -
-
-
-
- update -
-
-
MongodbSource::update() in mongodb_source.php
-
Update Data
-
-
- updateAll -
-
-
MongodbSource::updateAll() in mongodb_source.php
-
Update multiple Record
-
-
- -
-
v
- -
-
-
-
- value -
-
-
MongodbSource::value() in mongodb_source.php
-
Prepares a value, or an array of values for database queries by quoting and escaping them.
-
-
- -
-
_
- -
-
-
-
- $_baseConfig -
-
-
MongodbSource::$_baseConfig in mongodb_source.php
-
Base Config
-
-
- $_db -
-
-
MongodbSource::$_db in mongodb_source.php
-
Database Instance
-
-
- $_defaultSchema -
-
-
MongodbSource::$_defaultSchema in mongodb_source.php
-
Default schema for the mongo models
-
-
- $_defaultSettings -
-
- -
defaultSettings property
-
-
- $_defaultSettings -
-
- -
defaultSettings property
-
-
- $_driverVersion -
-
-
MongodbSource::$_driverVersion in mongodb_source.php
-
Mongo Driver Version
-
-
- $_startTime -
-
-
MongodbSource::$_startTime in mongodb_source.php
-
startTime property
-
-
- _convertId -
-
-
MongodbSource::_convertId() in mongodb_source.php
-
convertId method
-
-
- _prepareLogQuery -
-
-
MongodbSource::_prepareLogQuery() in mongodb_source.php
-
prepareLogQuery method
-
-
- _setEmptyValues -
-
-
MongodbSource::_setEmptyValues() in mongodb_source.php
-
Set empty values, arrays or integers, for the variables Mongo uses
-
-
- _stringify -
-
-
MongodbSource::_stringify() in mongodb_source.php
-
stringify method
-
-
- _stripAlias -
-
-
MongodbSource::_stripAlias() in mongodb_source.php
-
Convert automatically array('Model.field' => 'foo') to array('field' => 'foo')
-
-
- _translateConditions -
-
- -
translateConditions method
-
-
- _translateOperator -
-
- -
translateOperator method
-
-
- __construct -
-
-
MongodbSource::__construct() in mongodb_source.php
-
construct method
-
-
- __destruct -
-
-
MongodbSource::__destruct() in mongodb_source.php
-
Destruct
-
-
- -
- a - b - c - d - e - g - i - l - m - n - q - r - s - t - u - v - _ -
- \ No newline at end of file diff --git a/docs/elementindex_mongodb.html b/docs/elementindex_mongodb.html deleted file mode 100644 index 89e5bc7..0000000 --- a/docs/elementindex_mongodb.html +++ /dev/null @@ -1,671 +0,0 @@ - - - - - - - - - - - -

[mongodb] element index

-All elements -
-
- a - b - c - d - e - g - i - l - m - n - q - r - s - t - u - v - _ -
- - -
-
_
- -
-
-
-
- $_defaultSettings -
-
- -
defaultSettings property
-
-
- $_defaultSettings -
-
- -
defaultSettings property
-
-
- _translateConditions -
-
- -
translateConditions method
-
-
- _translateOperator -
-
- -
translateOperator method
-
-
- $_baseConfig -
-
-
MongodbSource::$_baseConfig in mongodb_source.php
-
Base Config
-
-
- $_db -
-
-
MongodbSource::$_db in mongodb_source.php
-
Database Instance
-
-
- $_defaultSchema -
-
-
MongodbSource::$_defaultSchema in mongodb_source.php
-
Default schema for the mongo models
-
-
- $_driverVersion -
-
-
MongodbSource::$_driverVersion in mongodb_source.php
-
Mongo Driver Version
-
-
- $_startTime -
-
-
MongodbSource::$_startTime in mongodb_source.php
-
startTime property
-
-
- _convertId -
-
-
MongodbSource::_convertId() in mongodb_source.php
-
convertId method
-
-
- _prepareLogQuery -
-
-
MongodbSource::_prepareLogQuery() in mongodb_source.php
-
prepareLogQuery method
-
-
- _setEmptyValues -
-
-
MongodbSource::_setEmptyValues() in mongodb_source.php
-
Set empty values, arrays or integers, for the variables Mongo uses
-
-
- _stringify -
-
-
MongodbSource::_stringify() in mongodb_source.php
-
stringify method
-
-
- _stripAlias -
-
-
MongodbSource::_stripAlias() in mongodb_source.php
-
Convert automatically array('Model.field' => 'foo') to array('field' => 'foo')
-
-
- __construct -
-
-
MongodbSource::__construct() in mongodb_source.php
-
construct method
-
-
- __destruct -
-
-
MongodbSource::__destruct() in mongodb_source.php
-
Destruct
-
-
- -
-
a
- -
-
-
-
- afterFind -
-
- -
If requested, convert dates from MongoDate objects to standard date strings
-
-
- -
-
b
- -
-
-
-
- beforeFind -
-
- -
beforeFind method
-
-
- beforeSave -
-
- -
beforeSave method
-
-
- begin -
-
-
MongodbSource::begin() in mongodb_source.php
-
begin method
-
-
- -
-
c
- -
-
-
-
- convertDates -
-
- -
Convert MongoDate objects to strings for the purpose of view simplicity
-
-
- $columns -
-
-
MongodbSource::$columns in mongodb_source.php
-
column definition
-
-
- $connected -
-
-
MongodbSource::$connected in mongodb_source.php
-
Are we connected to the DataSource?
-
-
- calculate -
-
-
MongodbSource::calculate() in mongodb_source.php
-
Calculate
-
-
- close -
-
-
MongodbSource::close() in mongodb_source.php
-
Close database connection
-
-
- commit -
-
-
MongodbSource::commit() in mongodb_source.php
-
commit method
-
-
- connect -
-
-
MongodbSource::connect() in mongodb_source.php
-
Connect to the database
-
-
- create -
-
-
MongodbSource::create() in mongodb_source.php
-
Create Data
-
-
- createConnectionName -
-
- -
create connection name.
-
-
- createSchema -
-
-
MongodbSource::createSchema() in mongodb_source.php
-
createSchema method
-
-
- -
-
d
- -
-
-
-
- delete -
-
-
MongodbSource::delete() in mongodb_source.php
-
Delete Data
-
-
- deriveSchemaFromData -
-
- -
deriveSchemaFromData method
-
-
- describe -
-
-
MongodbSource::describe() in mongodb_source.php
-
Describe
-
-
- disconnect -
-
-
MongodbSource::disconnect() in mongodb_source.php
-
Disconnect from the database
-
-
- distinct -
-
-
MongodbSource::distinct() in mongodb_source.php
-
distinct method
-
-
- dropSchema -
-
-
MongodbSource::dropSchema() in mongodb_source.php
-
dropSchema method
-
-
- -
-
e
- -
-
-
-
- ensureIndex -
-
-
MongodbSource::ensureIndex() in mongodb_source.php
-
ensureIndex method
-
-
- execute -
-
-
MongodbSource::execute() in mongodb_source.php
-
execute method
-
-
- -
-
g
- -
-
-
-
- getMongoCollection -
-
- -
get MongoDB Collection Object
-
-
- getMongoDb -
-
-
MongodbSource::getMongoDb() in mongodb_source.php
-
get MongoDB Object
-
-
- group -
-
-
MongodbSource::group() in mongodb_source.php
-
group method
-
-
- -
-
i
- -
-
-
-
- insertMulti -
-
-
MongodbSource::insertMulti() in mongodb_source.php
-
Inserts multiple values into a table
-
-
- isConnected -
-
-
MongodbSource::isConnected() in mongodb_source.php
-
check connection to the database
-
-
- isInterfaceSupported -
-
- -
isInterfaceSupported method
-
-
- -
-
l
- -
-
-
-
- listSources -
-
-
MongodbSource::listSources() in mongodb_source.php
-
Get list of available Collections
-
-
- logQuery -
-
-
MongodbSource::logQuery() in mongodb_source.php
-
logQuery method
-
-
- -
-
m
- -
-
-
-
- mongodb_source.php -
-
-
mongodb_source.php in mongodb_source.php
-
-
- mapReduce -
-
-
MongodbSource::mapReduce() in mongodb_source.php
-
mapReduce
-
-
- MongoDbDateFormatter -
-
-
MongoDbDateFormatter() in mongodb_source.php
-
MongoDbDateFormatter method
-
-
- MongodbSource -
-
-
MongodbSource in mongodb_source.php
-
MongoDB Source
-
-
- -
-
n
- -
-
-
-
- $name -
-
-
SqlCompatibleBehavior::$name in sql_compatible.php
-
name property
-
-
- $name -
-
-
SchemalessBehavior::$name in schemaless.php
-
name property
-
-
- name -
-
-
MongodbSource::name() in mongodb_source.php
-
Quotes identifiers.
-
-
- -
-
q
- -
-
-
-
- query -
-
-
MongodbSource::query() in mongodb_source.php
-
query method If call getMongoDb() from model, this method call getMongoDb().
-
-
- -
-
r
- -
-
-
-
- read -
-
-
MongodbSource::read() in mongodb_source.php
-
Read Data
-
-
- rollback -
-
-
MongodbSource::rollback() in mongodb_source.php
-
rollback method
-
-
- -
-
s
- -
-
-
-
- $settings -
-
-
SqlCompatibleBehavior::$settings in sql_compatible.php
-
Runtime settings
-
-
- $settings -
-
- -
settings property
-
-
- schemaless.php -
-
-
schemaless.php in schemaless.php
-
-
- sql_compatible.php -
-
-
sql_compatible.php in sql_compatible.php
-
-
- SchemalessBehavior -
-
-
SchemalessBehavior in schemaless.php
-
SchemalessBehavior class
-
-
- setup -
-
-
SqlCompatibleBehavior::setup() in sql_compatible.php
-
setup method
-
-
- setup -
-
-
SchemalessBehavior::setup() in schemaless.php
-
setup method
-
-
- SqlCompatibleBehavior -
-
-
SqlCompatibleBehavior in sql_compatible.php
-
SqlCompatibleBehavior class
-
-
- setTimeout -
-
-
MongodbSource::setTimeout() in mongodb_source.php
-
setTimeout Method
-
-
- -
-
t
- -
-
-
-
- truncate -
-
-
MongodbSource::truncate() in mongodb_source.php
-
Deletes all the records in a table
-
-
- -
-
u
- -
-
-
-
- update -
-
-
MongodbSource::update() in mongodb_source.php
-
Update Data
-
-
- updateAll -
-
-
MongodbSource::updateAll() in mongodb_source.php
-
Update multiple Record
-
-
- -
-
v
- -
-
-
-
- value -
-
-
MongodbSource::value() in mongodb_source.php
-
Prepares a value, or an array of values for database queries by quoting and escaping them.
-
-
- -
- a - b - c - d - e - g - i - l - m - n - q - r - s - t - u - v - _ -
- \ No newline at end of file diff --git a/docs/errors.html b/docs/errors.html deleted file mode 100644 index 0459e1c..0000000 --- a/docs/errors.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - phpDocumentor Parser Errors and Warnings - - - - - Post-parsing
- -

Post-parsing

-

Warnings:


-Warning - Class SchemalessBehavior parent ModelBehavior not found
-Warning - Class SqlCompatibleBehavior parent ModelBehavior not found
-Warning - Class MongodbSource parent DboSource not found
-

- Documentation generated on Tue, 26 Jul 2011 01:09:02 +0900 by phpDocumentor 1.4.3 -

- - \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index aafb23d..0000000 --- a/docs/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Generated Documentation - - - - - - - - - - - <H2>Frame Alert</H2> - <P>This document is designed to be viewed using the frames feature. - If you see this message, you are using a non-frame-capable web client.</P> - - - \ No newline at end of file diff --git a/docs/li_mongodb.html b/docs/li_mongodb.html deleted file mode 100644 index d770d8b..0000000 --- a/docs/li_mongodb.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - -
mongodb
-
- -
- -
Description
-
- Class trees
- Index of elements
-
- - - - - - - -
mongodb-models-behaviors
-
-
-
Classes
-
SchemalessBehavior
-
SqlCompatibleBehavior
-
Files
-
schemaless.php
-
sql_compatible.php
-
-
- - - - -
mongodb-models-datasources
-
-
-
Classes
-
MongodbSource
-
Functions
-
MongoDbDateFormatter
-
Files
-
mongodb_source.php
-
-
- - -
-
-

phpDocumentor v 1.4.3

- - \ No newline at end of file diff --git a/docs/media/banner.css b/docs/media/banner.css deleted file mode 100644 index ba1a7ba..0000000 --- a/docs/media/banner.css +++ /dev/null @@ -1,32 +0,0 @@ -body -{ - background-color: #DDDDDD; - margin: 0px; - padding: 0px; -} - -/* Banner (top bar) classes */ - -.banner { } - -.banner-menu -{ - clear: both; - padding: .5em; - border-top: 2px solid #999999; -} - -.banner-title -{ - text-align: right; - font-size: 20pt; - font-weight: bold; - margin: .2em; -} - -.package-selector -{ - background-color: #CCCCCC; - border: 1px solid black; - color: blue; -} diff --git a/docs/media/stylesheet.css b/docs/media/stylesheet.css deleted file mode 100644 index 051586b..0000000 --- a/docs/media/stylesheet.css +++ /dev/null @@ -1,142 +0,0 @@ -a { color: #0000FF; text-decoration: none; } -a:hover { color: #FF0000; text-decoration: underline; } -a:active { color: #FF0000; text-decoration: underline; } - -body { background-color: #EEEEEE; font-family: Verdana, Arial, sans-serif } -body, table { font-size: 10pt } -a img { border: 0px; } -dd { margin-left: 0px; padding-left: 1em; } - -/* Page layout/boxes */ - -.info-box {} -.info-box-title { margin: 1em 0em 0em 0em; padding: .25em; font-weight: normal; font-size: 14pt; border: 2px solid #999999; background-color: #DDDDDD } -.info-box-body { border: 1px solid #999999; padding: .5em; background-color: #F8F8F8; } -.nav-bar { font-size: 8pt; white-space: nowrap; text-align: right; padding: .2em; margin: 0em 0em 1em 0em; } - -.oddrow { background-color: #DDDDDD; border: 1px solid #999999; padding: .5em; margin-bottom: 1em} -.evenrow { background-color: #DDDDDD; border: 1px solid #999999; padding: .5em; margin-bottom: 1em} - -.page-body { max-width: 800px; margin: auto; } -.tree dl { margin: 0px } - -/* Index formatting classes */ - -.index-item-body { margin-top: .5em; margin-bottom: .5em} -.index-item-description { margin-top: .25em } -.index-item-details { font-weight: normal; font-style: italic; font-size: 8pt } -.index-letter-section { background-color: #EEEEEE; border: 1px dotted #999999; padding: .5em; margin-bottom: 1em} -.index-letter-title { font-size: 12pt; font-weight: bold } -.index-letter-menu { text-align: center; margin: 1em } -.index-letter { font-size: 12pt } - -/* Docbook classes */ - -.description {} -.short-description { font-weight: bold; color: #666666; } -.tags { padding-left: 0em; margin-left: 3em; color: #666666; list-style-type: square; } -.parameters { padding-left: 0em; margin-left: 3em; font-style: italic; list-style-type: square; } -.redefinitions { font-size: 8pt; padding-left: 0em; margin-left: 2em; } -.package { } -.package-title { font-weight: bold; font-size: 14pt; border-bottom: 1px solid black } -.package-details { font-size: 85%; } -.sub-package { font-weight: bold; font-size: 120% } -.tutorial { border-width: thin; border-color: #0066ff } -.tutorial-nav-box { width: 100%; border: 2px solid #999999; background-color: #DDDDDD } -.nav-button-disabled { color: #999999; } -.nav-button:active, -.nav-button:focus, -.nav-button:hover { background-color: #AAAAAA; outline: 1px solid #666666; text-decoration: none } -.folder-title { font-style: italic } - -/* Generic formatting */ - -.field { font-weight: bold; } -.detail { font-size: 8pt; } -.notes { font-style: italic; font-size: 8pt; } -.separator { background-color: #999999; height: 2px; } -.warning { color: #FF6600; } -.disabled { font-style: italic; color: #999999; } - -/* Code elements */ - -.line-number { } - -.class-table { width: 100%; } -.class-table-header { border-bottom: 1px dotted #666666; text-align: left} -.class-name { color: #000000; font-weight: bold; } - -.method-summary { padding-left: 1em; font-size: 8pt } -.method-header { } -.method-definition { margin-bottom: .3em } -.method-title { font-weight: bold; } -.method-name { font-weight: bold; } -.method-signature { font-size: 85%; color: #0066BB; margin: .5em 0em } -.method-result { font-style: italic; } - -.var-summary { padding-left: 1em; font-size: 8pt; } -.var-header { } -.var-title { margin-bottom: .3em } -.var-type { color: red; font-weight: bold } -.var-name { font-weight: bold; } -.var-default {} -.var-description { font-weight: normal; color: #000000; } - -.include-title { } -.include-type { font-style: italic; } -.include-name { font-weight: bold; } - -.const-title { } -.const-name { font-weight: bold; } - -/* Syntax highlighting */ - -.src-code { border: 1px solid #336699; padding: 1em; background-color: #EEEEEE; - font-family: 'Courier New', Courier, monospace; font-weight: normal; } -.src-line { font-family: 'Courier New', Courier, monospace; font-weight: normal; } - -.src-comm { color: #666666; } -.src-id { } -.src-inc { color: #0000FF; } -.src-key { color: #0000FF; } -.src-num { color: #CC0000; } -.src-str { color: #66cccc; } -.src-sym { font-weight: bold; } -.src-var { } - -.src-php { font-weight: bold; } - -.src-doc { color: #009999 } -.src-doc-close-template { color: #0000FF } -.src-doc-coretag { color: #0099FF; font-weight: bold } -.src-doc-inlinetag { color: #0099FF } -.src-doc-internal { color: #6699cc } -.src-doc-tag { color: #0080CC } -.src-doc-template { color: #0000FF } -.src-doc-type { font-style: italic } -.src-doc-var { font-style: italic } - -.tute-tag { color: #009999 } -.tute-attribute-name { color: #0000FF } -.tute-attribute-value { color: #0099FF } -.tute-entity { font-weight: bold; } -.tute-comment { font-style: italic } -.tute-inline-tag { color: #636311; font-weight: bold } - -/* tutorial */ - -.authors { } -.author { font-style: italic; font-weight: bold } -.author-blurb { margin: .5em 0em .5em 2em; font-size: 85%; font-weight: normal; font-style: normal } -.example { background-color: #DDDDDD; border: 1px solid #999999; padding: .5em; } -.listing { background-color: #DDDDDD; border: 1px solid #999999; padding: .5em; white-space: nowrap; } -.release-info { font-size: 85%; font-style: italic; margin: 1em 0em } -.ref-title-box { } -.ref-title { } -.ref-purpose { font-style: italic; color: #666666 } -.ref-synopsis { } -.title { font-weight: bold; border-bottom: 1px solid #888888; color: #888888; } -.cmd-synopsis { margin: 1em 0em } -.cmd-title { font-weight: bold } -.toc { margin-left: 2em; padding-left: 0em } - diff --git a/docs/mongodb/mongodb-models-behaviors/SchemalessBehavior.html b/docs/mongodb/mongodb-models-behaviors/SchemalessBehavior.html deleted file mode 100644 index 1e2e88c..0000000 --- a/docs/mongodb/mongodb-models-behaviors/SchemalessBehavior.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - Docs For Class SchemalessBehavior - - - - -
-

Class SchemalessBehavior

- - -
-
Description
- -
- -

SchemalessBehavior class

-
    -
  • uses: ModelBehavior
  • -
-

- Located in /behaviors/schemaless.php (line 34) -

- - -
ModelBehavior
-   |
-   --SchemalessBehavior
- -
-
- - - - -
-
Variable Summary
- -
-
-
- string - $name -
-
- array - $settings -
-
- array - $_defaultSettings -
-
-
-
- - -
-
Method Summary
- -
-
- -
- void - beforeSave - ( &$Model, mixed $Model) -
- -
- void - setup - ( &$Model, [array $config = array()], mixed $Model) -
-
-
-
- - -
-
Variables
- -
- - -
- -
- - string - $name - = 'Schemaless' (line 42) - -
- - -

name property

-
    -
  • var: 'Schemaless'
  • -
  • access: public
  • -
- - - - - -
- -
- -
- - array - $settings - = array() (line 50) - -
- - -

settings property

-
    -
  • access: public
  • -
- - - - - -
- -
- -
- - array - $_defaultSettings - = array(
)
(line 58) -
-
- - -

defaultSettings property

-
    -
  • access: protected
  • -
- - - - - -
- -
-
- - -
-
Methods
- -
- - -
- -
- beforeSave (line 84) -
- - -

beforeSave method

-

Set the schema to allow saving whatever has been passed

-
    -
  • access: public
  • -
- -
- void - - beforeSave - - ( &$Model, mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - - &$Model
  • -
- - -
- -
- -
- setup (line 71) -
- - -

setup method

-

Don't currently have any settings at all - disabled

-
    -
  • access: public
  • -
- -
- void - - setup - - ( &$Model, [array $config = array()], mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $config: array()
  • -
  • - - &$Model
  • -
- - -
- -
-
- - -

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/mongodb/mongodb-models-behaviors/SqlCompatibleBehavior.html b/docs/mongodb/mongodb-models-behaviors/SqlCompatibleBehavior.html deleted file mode 100644 index 18d8a10..0000000 --- a/docs/mongodb/mongodb-models-behaviors/SqlCompatibleBehavior.html +++ /dev/null @@ -1,443 +0,0 @@ - - - - - - Docs For Class SqlCompatibleBehavior - - - - -
-

Class SqlCompatibleBehavior

- - -
-
Description
- -
- -

SqlCompatibleBehavior class

-
    -
  • uses: ModelBehavior
  • -
-

- Located in /behaviors/sql_compatible.php (line 31) -

- - -
ModelBehavior
-   |
-   --SqlCompatibleBehavior
- -
-
- - - - -
-
Variable Summary
- -
-
-
- string - $name -
-
- array - $settings -
-
- array - $_defaultSettings -
-
-
-
- - -
-
Method Summary
- -
-
- -
- void - afterFind - ( &$Model, mixed $results, mixed $primary, mixed $Model) -
- -
- void - beforeFind - ( &$Model, mixed $query, mixed $Model) -
- -
- void - convertDates - ( &$results, mixed $results) -
- -
- void - setup - ( &$Model, [array $config = array()], mixed $Model) -
- -
- void - _translateConditions - ( &$Model,  &$conditions, mixed $Model, mixed $conditions) -
- -
- string - _translateOperator - (mixed $Model, mixed $operator) -
-
-
-
- - -
-
Variables
- -
- - -
- -
- - string - $name - = 'SqlCompatible' (line 39) - -
- - -

name property

-
    -
  • var: 'SqlCompatible'
  • -
  • access: public
  • -
- - - - - -
- -
- -
- - array - $settings - = array() (line 49) - -
- - -

Runtime settings

-

Keyed on model alias

-
    -
  • access: public
  • -
- - - - - -
- -
- -
- - array - $_defaultSettings - = array(
'convertDates' => true,
'operators' => array(
'!=' => '$ne',
'>' => '$gt',
'>=' => '$gte',
'<' => '$lt',
'<=' => '$lte',
'IN' => '$in',
'NOT' => '$not',
'NOT IN' => '$nin'
))
(line 57) -
-
- - -

defaultSettings property

-
    -
  • access: protected
  • -
- - - - - -
- -
-
- - -
-
Methods
- -
- - -
- -
- afterFind (line 94) -
- - -

If requested, convert dates from MongoDate objects to standard date strings

-
    -
  • access: public
  • -
- -
- void - - afterFind - - ( &$Model, mixed $results, mixed $primary, mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - mixed - $results
  • -
  • - mixed - $primary
  • -
  • - - &$Model
  • -
- - -
- -
- -
- beforeFind (line 111) -
- - -

beforeFind method

-

If conditions are an array ensure they are mongified

-
    -
  • access: public
  • -
- -
- void - - beforeFind - - ( &$Model, mixed $query, mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - mixed - $query
  • -
  • - - &$Model
  • -
- - -
- -
- -
- convertDates (line 125) -
- - -

Convert MongoDate objects to strings for the purpose of view simplicity

-
    -
  • access: public
  • -
- -
- void - - convertDates - - ( &$results, mixed $results) -
- -
    -
  • - mixed - $results
  • -
  • - - &$results
  • -
- - -
- -
- -
- setup (line 81) -
- - -

setup method

-

Allow overriding the operator map

-
    -
  • access: public
  • -
- -
- void - - setup - - ( &$Model, [array $config = array()], mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $config: array()
  • -
  • - - &$Model
  • -
- - -
- -
- -
- _translateConditions (line 145) -
- - -

translateConditions method

-

Loop on conditions and desqlify them

-
    -
  • access: protected
  • -
- -
- void - - _translateConditions - - ( &$Model,  &$conditions, mixed $Model, mixed $conditions) -
- -
    -
  • - mixed - $Model
  • -
  • - mixed - $conditions
  • -
  • - - &$Model
  • -
  • - - &$conditions
  • -
- - -
- -
- -
- _translateOperator (line 264) -
- - -

translateOperator method

-

Use the operator map for the model and return what the db really wants to hear

-
    -
  • access: protected
  • -
- -
- string - - _translateOperator - - (mixed $Model, mixed $operator) -
- -
    -
  • - mixed - $Model
  • -
  • - mixed - $operator
  • -
- - -
- -
-
- - -

- Documentation generated on Tue, 26 Jul 2011 01:09:02 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/mongodb/mongodb-models-behaviors/_behaviors---schemaless.php.html b/docs/mongodb/mongodb-models-behaviors/_behaviors---schemaless.php.html deleted file mode 100644 index 0cea78d..0000000 --- a/docs/mongodb/mongodb-models-behaviors/_behaviors---schemaless.php.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Docs for page schemaless.php - - - - -
-

/behaviors/schemaless.php

- - -
-
Description
- -
- -

Schemaless behavior.

-

Adds functionality specific to MongoDB/schemaless dbs Allow /not/ specifying the model's schema, and derive it (for cake-compatibility) from the data being saved. Note that used carelessly this is a pretty dangerous thing to allow - means a user can modify input forms adding whatever fields they like (unless you'er using the security component) and fill your db with their junk.

PHP version 5

Copyright (c) 2010, Andy Dawson

Licensed under The MIT License Redistributions of files must retain the above copyright notice.

- - -
-
- - -
-
Classes
- -
- - - - - - - - - -
ClassDescription
- SchemalessBehavior - - SchemalessBehavior class -
-
-
- - - - - -

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/mongodb/mongodb-models-behaviors/_behaviors---sql_compatible.php.html b/docs/mongodb/mongodb-models-behaviors/_behaviors---sql_compatible.php.html deleted file mode 100644 index 320f314..0000000 --- a/docs/mongodb/mongodb-models-behaviors/_behaviors---sql_compatible.php.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - Docs for page sql_compatible.php - - - - -
-

/behaviors/sql_compatible.php

- - -
-
Description
- -
- -

Sql Compatible.

-

Attach this behavior to be able to query mongo DBs without using mongo specific syntax. If you don't need this behavior don't attach it and save a few cycles

PHP version 5

Copyright (c) 2010, Andy Dawson

Licensed under The MIT License Redistributions of files must retain the above copyright notice.

- - -
-
- - -
-
Classes
- -
- - - - - - - - - -
ClassDescription
- SqlCompatibleBehavior - - SqlCompatibleBehavior class -
-
-
- - - - - -

- Documentation generated on Tue, 26 Jul 2011 01:09:02 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/mongodb/mongodb-models-datasources/MongodbSource.html b/docs/mongodb/mongodb-models-datasources/MongodbSource.html deleted file mode 100644 index 832ecf0..0000000 --- a/docs/mongodb/mongodb-models-datasources/MongodbSource.html +++ /dev/null @@ -1,1875 +0,0 @@ - - - - - - Docs For Class MongodbSource - - - - -
-

Class MongodbSource

- - -
-
Description
- -
- -

MongoDB Source

-

- Located in /datasources/mongodb_source.php (line 35) -

- - -
DboSource
-   |
-   --MongodbSource
- -
-
- - - - -
-
Variable Summary
- -
-
-
- array - $columns -
-
- boolean - $connected -
-
- array - $_baseConfig -
-
- resource - $_db -
-
- array - $_defaultSchema -
-
- string - $_driverVersion -
-
- mixed - $_startTime -
-
-
-
- - -
-
Method Summary
- -
-
- -
- void - __construct - ([array $config = array()], [bool $autoConnect = false]) -
- -
- void - __destruct - () -
- -
- void - begin - () -
- -
- array - calculate - ( &$Model, Model $Model) -
- -
- boolean - close - () -
- -
- void - commit - () -
- -
- boolean - connect - () -
- -
- boolean - create - ( &$Model, [array $fields = null], [array $values = null], Model $Model) -
- -
- void - createConnectionName - (array $config, string $version) -
- -
- void - createSchema - (mixed $schema, [mixed $tableName = null]) -
- -
- boolean - delete - ( &$Model, [array $conditions = null], Model $Model) -
- -
- void - deriveSchemaFromData - (mixed $Model, [array $data = array()]) -
- -
- array - describe - ( &$Model, [ $field = null], Model $Model) -
- -
- boolean - disconnect - () -
- -
- void - distinct - ( &$Model, [array $keys = array()], [array $params = array()], mixed $Model) -
- -
- void - dropSchema - (mixed $schema, [mixed $tableName = null]) -
- -
- void - ensureIndex - ( &$Model, [array $keys = array()], [array $params = array()], mixed $Model) -
- -
- void - execute - (mixed $query, [array $params = array()]) -
- -
- mixed - getMongoCollection - ( &$Model) -
- -
- mixed - getMongoDb - () -
- -
- void - group - ( &$Model, [array $params = array()], mixed $Model) -
- -
- void - insertMulti - (string $table, string $fields, array $values) -
- -
- boolean - isConnected - () -
- -
- void - isInterfaceSupported - (mixed $interface) -
- -
- array - listSources - ([array $data = null]) -
- -
- void - logQuery - (mixed $query, [array $args = array()]) -
- -
- mixed - mapReduce - (mixed $query, [integer $timeout = null]) -
- -
- string - name - (string $name) -
- -
- void - query - (mixed $query, [array $params = array()]) -
- -
- array - read - ( &$Model, [array $query = array()], Model $Model) -
- -
- void - rollback - () -
- -
- boolean - setTimeout - (int $ms) -
- -
- boolean - truncate - (mixed $table) -
- -
- boolean - update - ( &$Model, [array $fields = null], [array $values = null], [ $conditions = null], Model $Model) -
- -
- boolean - updateAll - ( &$Model, [array $fields = null], [array $conditions = null], Model $Model) -
- -
- mixed - value - (mixed $data, [string $column = null], [boolean $read = true]) -
- -
- void - _convertId - ( &$mixed, [bool $conditions = false], mixed $mixed) -
- -
- void - _prepareLogQuery - ( &$Model, mixed $Model) -
- -
- void - _setEmptyValues - ( &$data, [array $integers = array('limit', 'offset')], mixed $data) -
- -
- array - _stringify - ([ &$args = array()], [int $level = 0], array $args) -
- -
- void - _stripAlias - ([ &$args = array()], [string $alias = 'Model'], [bool $recurse = true], [string $check = 'key'], array $args) -
-
-
-
- - -
-
Variables
- -
- - -
- -
- - array - $columns - = array(
'boolean' => array('name' => 'boolean'),'string'=>array('name'=>'varchar'),'text'=>array('name'=>'text'),'integer'=>array('name'=>'integer','format'=>null,'formatter'=>'intval'),'float'=>array('name'=>'float','format'=>null,'formatter'=>'floatval'),'datetime'=>array('name'=>'datetime','format'=>null,'formatter'=>'MongodbDateFormatter'),'timestamp'=>array('name'=>'timestamp','format'=>null,'formatter'=>'MongodbDateFormatter'),'time'=>array('name'=>'time','format'=>null,'formatter'=>'MongodbDateFormatter'),'date'=>array('name'=>'date','format'=>null,'formatter'=>'MongodbDateFormatter'),)
(line 101) -
-
- - -

column definition

-
    -
  • access: public
  • -
- - - - - -
- -
- -
- - boolean - $connected - = null (line 47) - -
- - -

Are we connected to the DataSource?

-

true - yes null - haven't tried yet false - nope, and we can't connect

-
    -
  • access: public
  • -
- - - - - -
- -
- -
- - array - $_baseConfig - = array(
'set_string_id' => true,
'persistent' => true,
'host' => 'localhost',
'database' => '',
'port' => '27017',
'login' => '',
'password' => ''
)
(line 86) -
-
- - -

Base Config

-

set_string_id: true: In read() method, convert MongoId object to string and set it to array 'id'. false: not convert and set.

-
    -
  • access: public
  • -
- - - - - -
- -
- -
- - resource - $_db - = null (line 55) - -
- - -

Database Instance

-
    -
  • access: protected
  • -
- - - - - -
- -
- -
- - array - $_defaultSchema - = array(
'_id' => array('type' => 'string', 'length' => 24, 'key' => 'primary'),'created'=>array('type'=>'datetime','default'=>null))
(line 119) -
-
- - -

Default schema for the mongo models

-
    -
  • access: protected
  • -
- - - - - -
- -
- -
- - string - $_driverVersion - = Mongo::VERSION (line 63) - -
- - -

Mongo Driver Version

-
    -
  • access: protected
  • -
- - - - - -
- -
- -
- - mixed - $_startTime - = null (line 73) - -
- - -

startTime property

-

If debugging is enabled, stores the (micro)time the current query started

-
    -
  • var: null
  • -
  • access: protected
  • -
- - - - - -
- -
-
- - -
-
Methods
- -
- - -
- -
- Constructor __construct (line 134) -
- - -

construct method

-

By default don't try to connect until you need to

-
    -
  • access: public
  • -
- -
- void - - __construct - - ([array $config = array()], [bool $autoConnect = false]) -
- -
    -
  • - array - $config: Configuration array
  • -
  • - bool - $autoConnect: false
  • -
- - -
- -
- -
- Destructor __destruct (line 143) -
- - -

Destruct

-
    -
  • access: public
  • -
- -
- void - - __destruct - - () -
- - - -
- -
- -
- begin (line 411) -
- - -

begin method

-

Mongo doesn't support transactions

-
    -
  • access: public
  • -
- -
- void - - begin - - () -
- - - -
- -
- -
- calculate (line 422) -
- - -

Calculate

-
    -
  • access: public
  • -
- -
- array - - calculate - - ( &$Model, Model $Model) -
- -
    -
  • - Model - $Model
  • -
  • - - &$Model
  • -
- - -
- -
- -
- close (line 332) -
- - -

Close database connection

-
    -
  • return: Connected
  • -
  • access: public
  • -
- -
- boolean - - close - - () -
- - - -
- -
- -
- commit (line 157) -
- - -

commit method

-

MongoDB doesn't support transactions

-
    -
  • access: public
  • -
- -
- void - - commit - - () -
- - - -
- -
- -
- connect (line 172) -
- - -

Connect to the database

-

If using 1.0.2 or above use the mongodb:// format to connect The connect syntax changed in version 1.0.2 - so check for that too

If authentication information in present then authenticate the connection

-
    -
  • return: Connected
  • -
  • access: public
  • -
- -
- boolean - - connect - - () -
- - - -
- -
- -
- create (line 447) -
- - -

Create Data

-
    -
  • return: Insert result
  • -
  • access: public
  • -
- -
- boolean - - create - - ( &$Model, [array $fields = null], [array $values = null], Model $Model) -
- -
    -
  • - Model - $Model: Model Instance
  • -
  • - array - $fields: Field data
  • -
  • - array - $values: Save data
  • -
  • - - &$Model
  • -
- - -
- -
- -
- createConnectionName (line 210) -
- - -

create connection name.

-
    -
  • access: public
  • -
- -
- void - - createConnectionName - - (array $config, string $version) -
- -
    -
  • - array - $config
  • -
  • - string - $version: version of MongoDriver
  • -
- - -
- -
- -
- createSchema (line 497) -
- - -

createSchema method

-

Mongo no care for creating schema. Mongo work with no schema.

-
    -
  • access: public
  • -
- -
- void - - createSchema - - (mixed $schema, [mixed $tableName = null]) -
- -
    -
  • - mixed - $schema
  • -
  • - mixed - $tableName: null
  • -
- - -
- -
- -
- delete (line 851) -
- - -

Delete Data

-

For deleteAll(true, false) calls - conditions will arrive here as true - account for that and convert to an empty array For deleteAll(array('some conditions')) calls - conditions will arrive here as: array( Alias._id => array(1, 2, 3, ...) )

This format won't be understood by mongodb, it'll find 0 rows. convert to:

array( Alias._id => array('$in' => array(1, 2, 3, ...)) )

-
    -
  • return: Update result
  • -
  • TODO: bench remove() v drop. if it's faster to drop - just drop the collection taking into account existing indexes (recreate just the indexes)
  • -
  • access: public
  • -
- -
- boolean - - delete - - ( &$Model, [array $conditions = null], Model $Model) -
- -
    -
  • - Model - $Model: Model Instance
  • -
  • - array - $conditions
  • -
  • - - &$Model
  • -
- - -
- -
- -
- deriveSchemaFromData (line 804) -
- - -

deriveSchemaFromData method

-
    -
  • access: public
  • -
- -
- void - - deriveSchemaFromData - - (mixed $Model, [array $data = array()]) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $data: array()
  • -
- - -
- -
- -
- describe (line 386) -
- - -

Describe

-

Automatically bind the schemaless behavior if there is no explicit mongo schema. When called, if there is model data it will be used to derive a schema. a row is plucked out of the db and the data obtained used to derive the schema.

-
    -
  • return: if model instance has mongoSchema, return it.
  • -
  • access: public
  • -
- -
- array - - describe - - ( &$Model, [ $field = null], Model $Model) -
- -
    -
  • - Model - $Model
  • -
  • - - &$Model
  • -
  • - - $field
  • -
- - -
- -
- -
- disconnect (line 342) -
- - -

Disconnect from the database

-
    -
  • return: Connected
  • -
  • access: public
  • -
- -
- boolean - - disconnect - - () -
- - - -
- -
- -
- distinct (line 550) -
- - -

distinct method

-
    -
  • access: public
  • -
- -
- void - - distinct - - ( &$Model, [array $keys = array()], [array $params = array()], mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $keys: array()
  • -
  • - array - $params: array()
  • -
  • - - &$Model
  • -
- - -
- -
- -
- dropSchema (line 511) -
- - -

dropSchema method

-

Return a command to drop each table

-
    -
  • access: public
  • -
- -
- void - - dropSchema - - (mixed $schema, [mixed $tableName = null]) -
- -
    -
  • - mixed - $schema
  • -
  • - mixed - $tableName: null
  • -
- - -
- -
- -
- ensureIndex (line 635) -
- - -

ensureIndex method

-
    -
  • access: public
  • -
- -
- void - - ensureIndex - - ( &$Model, [array $keys = array()], [array $params = array()], mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $keys: array()
  • -
  • - array - $params: array()
  • -
  • - - &$Model
  • -
- - -
- -
- -
- execute (line 1142) -
- - -

execute method

-

If there is no query or the query is true, execute has probably been called as part of a db-agnostic process which does not have a mongo equivalent, don't do anything.

-
    -
  • access: public
  • -
- -
- void - - execute - - (mixed $query, [array $params = array()]) -
- -
    -
  • - mixed - $query
  • -
  • - array - $params: array()
  • -
- - -
- -
- -
- getMongoCollection (line 297) -
- - -

get MongoDB Collection Object

-
    -
  • return: MongoDB Collection Object
  • -
  • access: public
  • -
- -
- mixed - - getMongoCollection - - ( &$Model) -
- -
    -
  • - - &$Model
  • -
- - -
- -
- -
- getMongoDb (line 284) -
- - -

get MongoDB Object

-
    -
  • return: MongoDB Object
  • -
  • access: public
  • -
- -
- mixed - - getMongoDb - - () -
- - - -
- -
- -
- group (line 596) -
- - -

group method

-
    -
  • access: public
  • -
- -
- void - - group - - ( &$Model, [array $params = array()], mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - array - $params:

    array() Set params same as MongoCollection::group() key,initial, reduce, options(conditions, finalize)

    Ex. $params = array( 'key' => array('field' => true), 'initial' => array('csum' => 0), 'reduce' => 'function(obj, prev){prev.csum += 1;}', 'options' => array( 'condition' => array('age' => array('$gt' => 20)), 'finalize' => array(), ), );

  • -
  • - - &$Model
  • -
- - -
- -
- -
- insertMulti (line 238) -
- - -

Inserts multiple values into a table

-
    -
  • access: public
  • -
- -
- void - - insertMulti - - (string $table, string $fields, array $values) -
- -
    -
  • - string - $table
  • -
  • - string - $fields
  • -
  • - array - $values
  • -
- - -
- -
- -
- isConnected (line 271) -
- - -

check connection to the database

-
    -
  • return: Connected
  • -
  • access: public
  • -
- -
- boolean - - isConnected - - () -
- - - -
- -
- -
- isInterfaceSupported (line 319) -
- - -

isInterfaceSupported method

-

listSources is infact supported, however: cake expects it to return a complete list of all possible sources in the selected db - the possible list of collections is infinte, so it's faster and simpler to tell cake that the interface is /not/ supported so it assumes that <insert name of your table here> exist

-
    -
  • access: public
  • -
- -
- void - - isInterfaceSupported - - (mixed $interface) -
- -
    -
  • - mixed - $interface
  • -
- - -
- -
- -
- listSources (line 358) -
- - -

Get list of available Collections

-
    -
  • return: Collections
  • -
  • access: public
  • -
- -
- array - - listSources - - ([array $data = null]) -
- -
    -
  • - array - $data
  • -
- - -
- -
- -
- logQuery (line 1241) -
- - -

logQuery method

-

Set timers, errors and refer to the parent If there are arguments passed - inject them into the query Show MongoIds in a copy-and-paste-into-mongo format

-
    -
  • access: public
  • -
- -
- void - - logQuery - - (mixed $query, [array $args = array()]) -
- -
    -
  • - mixed - $query
  • -
  • - array - $args: array()
  • -
- - -
- -
- -
- mapReduce (line 1094) -
- - -

mapReduce

-
    -
  • return: false or array
  • -
  • access: public
  • -
- -
- mixed - - mapReduce - - (mixed $query, [integer $timeout = null]) -
- -
    -
  • - mixed - $query
  • -
  • - integer - $timeout: (milli second)
  • -
- - -
- -
- -
- name (line 434) -
- - -

Quotes identifiers.

-

MongoDb does not need identifiers quoted, so this method simply returns the identifier.

-
    -
  • return: The quoted identifier.
  • -
  • access: public
  • -
- -
- string - - name - - (string $name) -
- -
    -
  • - string - $name: The identifier to quote.
  • -
- - -
- -
- -
- query (line 1067) -
- - -

query method If call getMongoDb() from model, this method call getMongoDb().

-
    -
  • access: public
  • -
- -
- void - - query - - (mixed $query, [array $params = array()]) -
- -
    -
  • - mixed - $query
  • -
  • - array - $params: array()
  • -
- - -
- -
- -
- read (line 908) -
- - -

Read Data

-

For deleteAll(true) calls - the conditions will arrive here as true - account for that and switch to an empty array

-
    -
  • return: Results
  • -
  • access: public
  • -
- -
- array - - read - - ( &$Model, [array $query = array()], Model $Model) -
- -
    -
  • - Model - $Model: Model Instance
  • -
  • - array - $query: Query data
  • -
  • - - &$Model
  • -
- - -
- -
- -
- rollback (line 1039) -
- - -

rollback method

-

MongoDB doesn't support transactions

-
    -
  • access: public
  • -
- -
- void - - rollback - - () -
- - - -
- -
- -
- setTimeout (line 1222) -
- - -

setTimeout Method

-

Sets the MongoCursor timeout so long queries (like map / reduce) can run at will. Expressed in milliseconds, for an infinite timeout, set to -1

-
    -
  • access: public
  • -
- -
- boolean - - setTimeout - - (int $ms) -
- -
    -
  • - int - $ms
  • -
- - -
- -
- -
- truncate (line 1050) -
- - -

Deletes all the records in a table

-
    -
  • access: public
  • -
- -
- boolean - - truncate - - (mixed $table) -
- -
    -
  • - mixed - $table: A string or model class representing the table to be truncated
  • -
- - -
- -
- -
- update (line 677) -
- - -

Update Data

-

This method uses $set operator automatically with MongoCollection::update(). If you don't want to use $set operator, you can chose any one as follw.

  1. Set TRUE in Model::mongoNoSetOperator property.
  2. Set a mongodb operator in a key of save data as follow. - Model->save(array('_id' => $id, '$inc' => array('count' => 1))); - Don't use Model::mongoSchema property, - CakePHP delete '$inc' data in Model::Save().
  3. Set a Mongo operator in Model::mongoNoSetOperator property. - Model->mongoNoSetOperator = '$inc'; - Model->save(array('_id' => $id, array('count' => 1)));

-
    -
  • return: Update result
  • -
  • access: public
  • -
- -
- boolean - - update - - ( &$Model, [array $fields = null], [array $values = null], [ $conditions = null], Model $Model) -
- -
    -
  • - Model - $Model: Model Instance
  • -
  • - array - $fields: Field data
  • -
  • - array - $values: Save data
  • -
  • - - &$Model
  • -
  • - - $conditions
  • -
- - -
- -
- -
- updateAll (line 768) -
- - -

Update multiple Record

-
    -
  • return: Update result
  • -
  • access: public
  • -
- -
- boolean - - updateAll - - ( &$Model, [array $fields = null], [array $conditions = null], Model $Model) -
- -
    -
  • - Model - $Model: Model Instance
  • -
  • - array - $fields: Field data
  • -
  • - array - $conditions
  • -
  • - - &$Model
  • -
- - -
- -
- -
- value (line 1123) -
- - -

Prepares a value, or an array of values for database queries by quoting and escaping them.

-
    -
  • return: Prepared value or array of values.
  • -
  • access: public
  • -
- -
- mixed - - value - - (mixed $data, [string $column = null], [boolean $read = true]) -
- -
    -
  • - mixed - $data: A value or an array of values to prepare.
  • -
  • - string - $column: The column into which this data will be inserted
  • -
  • - boolean - $read: Value to be used in READ or WRITE context
  • -
- - -
- -
- -
- _convertId (line 1271) -
- - -

convertId method

-

$conditions is used to determine if it should try to auto correct _id => array() queries it only appies to conditions, hence the param name

-
    -
  • access: protected
  • -
- -
- void - - _convertId - - ( &$mixed, [bool $conditions = false], mixed $mixed) -
- -
    -
  • - mixed - $mixed
  • -
  • - bool - $conditions: false
  • -
  • - - &$mixed
  • -
- - -
- -
- -
- _prepareLogQuery (line 1200) -
- - -

prepareLogQuery method

-

Any prep work to log a query

-
    -
  • access: protected
  • -
- -
- void - - _prepareLogQuery - - ( &$Model, mixed $Model) -
- -
    -
  • - mixed - $Model
  • -
  • - - &$Model
  • -
- - -
- -
- -
- _setEmptyValues (line 1176) -
- - -

Set empty values, arrays or integers, for the variables Mongo uses

-
    -
  • access: protected
  • -
- -
- void - - _setEmptyValues - - ( &$data, [array $integers = array('limit', 'offset')], mixed $data) -
- -
    -
  • - mixed - $data
  • -
  • - array - $integers: array('limit', 'offset')
  • -
  • - - &$data
  • -
- - -
- -
- -
- _stringify (line 1299) -
- - -

stringify method

-

Takes an array of args as an input and returns an array of json-encoded strings. Takes care of any objects the arrays might be holding (MongoID);

-
    -
  • access: protected
  • -
- -
- array - - _stringify - - ([ &$args = array()], [int $level = 0], array $args) -
- -
    -
  • - array - $args: array()
  • -
  • - int - $level: 0 internal recursion counter
  • -
  • - - &$args
  • -
- - -
- -
- -
- _stripAlias (line 1339) -
- - -

Convert automatically array('Model.field' => 'foo') to array('field' => 'foo')

-

This introduces the limitation that you can't have a (nested) field with the same name as the model But it's a small price to pay to be able to use other behaviors/functionality with mongoDB

-
    -
  • access: protected
  • -
- -
- void - - _stripAlias - - ([ &$args = array()], [string $alias = 'Model'], [bool $recurse = true], [string $check = 'key'], array $args) -
- -
    -
  • - array - $args: array()
  • -
  • - string - $alias: 'Model'
  • -
  • - bool - $recurse: true
  • -
  • - string - $check: 'key', 'value' or 'both'
  • -
  • - - &$args
  • -
- - -
- -
-
- - -

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/mongodb/mongodb-models-datasources/_datasources---mongodb_source.php.html b/docs/mongodb/mongodb-models-datasources/_datasources---mongodb_source.php.html deleted file mode 100644 index 2211b85..0000000 --- a/docs/mongodb/mongodb-models-datasources/_datasources---mongodb_source.php.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - Docs for page mongodb_source.php - - - - -
-

/datasources/mongodb_source.php

- - -
-
Description
- -
- -

A CakePHP datasource for the mongoDB (http://www.mongodb.org/) document-oriented database.

-

This datasource uses Pecl Mongo (http://php.net/mongo) and is thus dependent on PHP 5.0 and greater.

Original implementation by ichikaway(Yasushi Ichikawa) http://github.com/ichikaway/

Reference: Nate Abele's lithium mongoDB datasource (http://li3.rad-dev.org/) Joél Perras' divan(http://github.com/jperras/divan/)

Copyright 2010, Yasushi Ichikawa http://github.com/ichikaway/

Contributors: Predominant, Jrbasso, tkyk, AD7six

Licensed under The MIT License Redistributions of files must retain the above copyright notice.

-
    -
  • copyright: Copyright 2010, Yasushi Ichikawa http://github.com/ichikaway/
  • -
  • license: The MIT License
  • -
- -
-
- - -
-
Classes
- -
- - - - - - - - - -
ClassDescription
- MongodbSource - - MongoDB Source -
-
-
- - - - - -
-
Functions
- -
- -
- -
- MongoDbDateFormatter (line 1375) -
- - -

MongoDbDateFormatter method

-

This function cannot be in the class because of the way model save is written

-
    -
  • access: public
  • -
- -
- void - - MongoDbDateFormatter - - ([mixed $date = null]) -
- -
    -
  • - mixed - $date: null
  • -
- - -
-
-
- -

- Documentation generated on Tue, 26 Jul 2011 01:09:01 +0900 by phpDocumentor 1.4.3 -

-
- \ No newline at end of file diff --git a/docs/packages.html b/docs/packages.html deleted file mode 100644 index 8d3e753..0000000 --- a/docs/packages.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file From a61f8866f661a632c393b9d7fe29952e77dbc3db Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 08:59:06 +1100 Subject: [PATCH 07/33] Remove outdated Cakephp 1.3 samples. --- samples/controllers/geos_controller.php | 101 ------------- samples/controllers/posts_controller.php | 142 ------------------ .../controllers/subdocuments_controller.php | 91 ----------- samples/models/geo.php | 34 ----- samples/models/post.php | 14 -- samples/models/subdocument.php | 15 -- samples/views/geos/add.ctp | 27 ---- samples/views/geos/index.ctp | 23 --- samples/views/posts/add.ctp | 17 --- samples/views/posts/createindex.ctp | 5 - samples/views/posts/edit.ctp | 18 --- samples/views/posts/index.ctp | 12 -- samples/views/subdocuments/add.ctp | 31 ---- samples/views/subdocuments/edit.ctp | 21 --- samples/views/subdocuments/index.ctp | 15 -- 15 files changed, 566 deletions(-) delete mode 100644 samples/controllers/geos_controller.php delete mode 100644 samples/controllers/posts_controller.php delete mode 100644 samples/controllers/subdocuments_controller.php delete mode 100644 samples/models/geo.php delete mode 100644 samples/models/post.php delete mode 100644 samples/models/subdocument.php delete mode 100755 samples/views/geos/add.ctp delete mode 100644 samples/views/geos/index.ctp delete mode 100644 samples/views/posts/add.ctp delete mode 100644 samples/views/posts/createindex.ctp delete mode 100644 samples/views/posts/edit.ctp delete mode 100644 samples/views/posts/index.ctp delete mode 100644 samples/views/subdocuments/add.ctp delete mode 100644 samples/views/subdocuments/edit.ctp delete mode 100644 samples/views/subdocuments/index.ctp diff --git a/samples/controllers/geos_controller.php b/samples/controllers/geos_controller.php deleted file mode 100644 index 33ee7d6..0000000 --- a/samples/controllers/geos_controller.php +++ /dev/null @@ -1,101 +0,0 @@ - 35, - 'page' => 1, - ); - - if(!empty($type) && !empty($lat) && !empty($long)) { - $lat = floatval($lat); - $long = floatval($long); - $opt1 = floatval($opt1); - $opt2 = floatval($opt2); - - switch($type) { - case('near'): - if(!empty($opt1)){ - $cond = array('loc' => array('$near' => array($lat, $long), '$maxDistance' => $opt1)); - } else { - $cond = array('loc' => array('$near' => array($lat, $long))); - } - break; - case('box'): - $lowerLeft = array($lat, $long); - $upperRight = array($opt1, $opt2); - $cond = array('loc' => array('$within' => array('$box' => array($lowerLeft, $upperRight)))); - break; - case('circle'): - $center = array($lat, $long); - $radius = $opt1; - $cond = array('loc' => array('$within' => array('$center' => array($center, $radius)))); - break; - } - $params['conditions'] = $cond; - - } else { - $params['order'] = array('_id' => -1); - } - - $results = $this->Geo->find('all', $params); - $this->set(compact('results')); - } - - /** - * add method - * - * @return void - * @access public - */ - public function add() { - if (!empty($this->data)) { - - $this->Geo->create(); - if ($this->Geo->save($this->data)) { - $this->flash(__('Geo saved.', true), array('action' => 'index')); - } else { - } - } - } - - /** - * delete method - * - * @param mixed $id null - * @return void - * @access public - */ - public function delete($id = null) { - if (!$id) { - $this->flash(__('Invalid Geo', true), array('action' => 'index')); - } - if ($this->Geo->delete($id)) { - $this->flash(__('Geo deleted', true), array('action' => 'index')); - } else { - $this->flash(__('Geo deleted Fail', true), array('action' => 'index')); - } - } -} diff --git a/samples/controllers/posts_controller.php b/samples/controllers/posts_controller.php deleted file mode 100644 index 58266cb..0000000 --- a/samples/controllers/posts_controller.php +++ /dev/null @@ -1,142 +0,0 @@ - array('title', 'body', 'hoge'), - //'fields' => array('Post.title', ), - //'conditions' => array('title' => 'hehe'), - //'conditions' => array('hoge' => array('$gt' => '10', '$lt' => '34')), - //'order' => array('title' => 1, 'body' => 1), - 'order' => array('_id' => -1), - 'limit' => 35, - 'page' => 1, - ); - $results = $this->Post->find('all', $params); - //$result = $this->Post->find('count', $params); - $this->set(compact('results')); - } - -/** - * add method - * - * @return void - * @access public - */ - public function add() { - if (!empty($this->data)) { - - $this->Post->create(); - if ($this->Post->save($this->data)) { - $this->flash(__('Post saved.', true), array('action' => 'index')); - } else { - } - } - } - -/** - * edit method - * - * @param mixed $id null - * @return void - * @access public - */ - public function edit($id = null) { - if (!$id && empty($this->data)) { - $this->flash(__('Invalid Post', true), array('action' => 'index')); - } - if (!empty($this->data)) { - - if ($this->Post->save($this->data)) { - $this->flash(__('The Post has been saved.', true), array('action' => 'index')); - } else { - } - } - if (empty($this->data)) { - $this->data = $this->Post->read(null, $id); - //$this->data = $this->Post->find('first', array('conditions' => array('_id' => $id))); - } - } - -/** - * delete method - * - * @param mixed $id null - * @return void - * @access public - */ - public function delete($id = null) { - if (!$id) { - $this->flash(__('Invalid Post', true), array('action' => 'index')); - } - if ($this->Post->delete($id)) { - $this->flash(__('Post deleted', true), array('action' => 'index')); - } else { - $this->flash(__('Post deleted Fail', true), array('action' => 'index')); - } - } - -/** - * deleteall method - * - * @return void - * @access public - */ - public function deleteall() { - $conditions = array('title' => 'aa'); - if ($this->Post->deleteAll($conditions)) { - $this->flash(__('Post deleteAll success', true), array('action' => 'index')); - - } else { - $this->flash(__('Post deleteAll Fail', true), array('action' => 'index')); - } - } - -/** - * updateall method - * - * @return void - * @access public - */ - public function updateall() { - $conditions = array('title' => 'ichi2' ); - - $field = array('title' => 'ichi' ); - - if ($this->Post->updateAll($field, $conditions)) { - $this->flash(__('Post updateAll success', true), array('action' => 'index')); - - } else { - $this->flash(__('Post updateAll Fail', true), array('action' => 'index')); - } - } - - public function createindex() { - $mongo = ConnectionManager::getDataSource($this->Post->useDbConfig); - $mongo->ensureIndex($this->Post, array('title' => 1)); - - } -} \ No newline at end of file diff --git a/samples/controllers/subdocuments_controller.php b/samples/controllers/subdocuments_controller.php deleted file mode 100644 index 5c1b63d..0000000 --- a/samples/controllers/subdocuments_controller.php +++ /dev/null @@ -1,91 +0,0 @@ - array('_id' => -1), - 'limit' => 35, - 'page' => 1, - ); - $results = $this->Subdocument->find('all', $params); - $this->set(compact('results')); - } - -/** - * add method - * - * @return void - * @access public - */ - public function add() { - if (!empty($this->data)) { - $this->Subdocument->create(); - if ($this->Subdocument->save($this->data)) { - $this->flash(__('Subdocument saved.', true), array('action' => 'index')); - } else { - } - } - } - -/** - * edit method - * - * @param mixed $id null - * @return void - * @access public - */ - public function edit($id = null) { - if (!$id && empty($this->data)) { - $this->flash(__('Invalid Subdocument', true), array('action' => 'index')); - } - if (!empty($this->data)) { - - if ($this->Subdocument->save($this->data)) { - $this->flash(__('The Subdocument has been saved.', true), array('action' => 'index')); - } else { - } - } - if (empty($this->data)) { - $this->data = $this->Subdocument->read(null, $id); - } - } - -/** - * delete method - * - * @param mixed $id null - * @return void - * @access public - */ - public function delete($id = null) { - if (!$id) { - $this->flash(__('Invalid Subdocument', true), array('action' => 'index')); - } - if ($this->Subdocument->delete($id)) { - $this->flash(__('Subdocument deleted', true), array('action' => 'index')); - } else { - $this->flash(__('Subdocument deleted Fail', true), array('action' => 'index')); - } - } -} \ No newline at end of file diff --git a/samples/models/geo.php b/samples/models/geo.php deleted file mode 100644 index b78ce4d..0000000 --- a/samples/models/geo.php +++ /dev/null @@ -1,34 +0,0 @@ - array('type'=>'string'), - 'body'=>array('type'=>'string'), - 'loc'=>array( - 'lat' => array('type'=>'float'), - 'long' => array('type'=>'float'), - ), - 'created'=>array('type'=>'datetime'), - 'modified'=>array('type'=>'datetime'), - ); -*/ - - function beforeSave() { - if(!empty($this->data[$this->alias]['loc'])) { - //convert location info from string to float - $this->data[$this->alias]['loc']['lat'] = floatval($this->data[$this->alias]['loc']['lat']); - $this->data[$this->alias]['loc']['long'] = floatval($this->data[$this->alias]['loc']['long']); - } - return true; - } - - - function afterSave($created) { - //create Gespatial Index - $mongo = $this->getDataSource(); - $mongo->ensureIndex($this, array('loc' => "2d")); - return true; - } - -} diff --git a/samples/models/post.php b/samples/models/post.php deleted file mode 100644 index 2add2ed..0000000 --- a/samples/models/post.php +++ /dev/null @@ -1,14 +0,0 @@ - array('type'=>'string'), - 'body'=>array('type'=>'string'), - 'hoge'=>array('type'=>'string'), - 'created'=>array('type'=>'datetime'), - 'modified'=>array('type'=>'datetime'), - ); -*/ -} \ No newline at end of file diff --git a/samples/models/subdocument.php b/samples/models/subdocument.php deleted file mode 100644 index f34c72f..0000000 --- a/samples/models/subdocument.php +++ /dev/null @@ -1,15 +0,0 @@ - array('type'=>'string'), - 'body'=>array('type'=>'string'), - 'subdoc'=>array( - 'name' => array('type'=>'string'), - 'age' => array('type'=>'integer') - ), - 'created'=>array('type'=>'datetime'), - 'modified'=>array('type'=>'datetime'), - ); -*/ -} \ No newline at end of file diff --git a/samples/views/geos/add.ctp b/samples/views/geos/add.ctp deleted file mode 100755 index c53726d..0000000 --- a/samples/views/geos/add.ctp +++ /dev/null @@ -1,27 +0,0 @@ -
-Form->create('Geo' , array( 'type' => 'post' ));?> -
- - Form->input('title'); - echo $this->Form->input('body'); - echo $this->Form->input('Geo.loc.lat', array('label' => 'latitude')); - echo $this->Form->input('Geo.loc.long', array('label' => 'longitude')); - ?> -
-Form->end('Submit');?> -
-
-
    -
  • Html->link(__('List Geos', true), array('action'=>'index'));?>
  • -
-
- - -Form->end('Submit');?> - -
-
    -
  • Html->link(__('List Geos', true), array('action'=>'index'));?>
  • -
-
diff --git a/samples/views/geos/index.ctp b/samples/views/geos/index.ctp deleted file mode 100644 index fd9a1f8..0000000 --- a/samples/views/geos/index.ctp +++ /dev/null @@ -1,23 +0,0 @@ -Html->link('Add data', 'add'); ?> -
-
- - - id: - [Html->link('delete','delete/'.$result['Geo']['_id']); ?>] - [ 'index', 'near', $result['Geo']['loc']['lat'], $result['Geo']['loc']['long']); - echo $this->Html->link('near here', $url); - ?>] - [ 'index', 'circle', $result['Geo']['loc']['lat'], $result['Geo']['loc']['long'], 10); - echo $this->Html->link('around here', $url); - ?>] -
- title:
- body:
- latitude:
- longitude:
- -
- diff --git a/samples/views/posts/add.ctp b/samples/views/posts/add.ctp deleted file mode 100644 index ceebdc1..0000000 --- a/samples/views/posts/add.ctp +++ /dev/null @@ -1,17 +0,0 @@ -
-Form->create('Post' , array( 'type' => 'post' ));?> -
- - Form->input('title'); - echo $this->Form->input('body'); - echo $this->Form->input('hoge'); - ?> -
-Form->end('Submit');?> -
-
-
    -
  • Html->link(__('List Posts', true), array('action'=>'index'));?>
  • -
-
diff --git a/samples/views/posts/createindex.ctp b/samples/views/posts/createindex.ctp deleted file mode 100644 index 7e46943..0000000 --- a/samples/views/posts/createindex.ctp +++ /dev/null @@ -1,5 +0,0 @@ -

Sample of create index in MongoDB

-
-
- -Creating index.... \ No newline at end of file diff --git a/samples/views/posts/edit.ctp b/samples/views/posts/edit.ctp deleted file mode 100644 index 897479f..0000000 --- a/samples/views/posts/edit.ctp +++ /dev/null @@ -1,18 +0,0 @@ -
-Form->create('Post' , array( 'type' => 'post' ));?> -
- - Form->hidden('_id'); - echo $this->Form->input('title'); - echo $this->Form->input('body'); - echo $this->Form->input('hoge'); - ?> -
-Form->end('Submit');?> -
-
-
    -
  • Html->link(__('List Posts', true), array('action'=>'index'));?>
  • -
-
diff --git a/samples/views/posts/index.ctp b/samples/views/posts/index.ctp deleted file mode 100644 index 05d05ff..0000000 --- a/samples/views/posts/index.ctp +++ /dev/null @@ -1,12 +0,0 @@ -Html->link('Add data', 'add'); ?> -
-
- - - id: [Html->link('edit','edit/'.$result['Post']['_id']); ?>] [Html->link('delete','delete/'.$result['Post']['_id']); ?>]
- title:
- body:
- hoge:
- -
- diff --git a/samples/views/subdocuments/add.ctp b/samples/views/subdocuments/add.ctp deleted file mode 100644 index a0469b8..0000000 --- a/samples/views/subdocuments/add.ctp +++ /dev/null @@ -1,31 +0,0 @@ -
-Form->create('Subdocument' , array( 'type' => 'post' ));?> -
- - Form->input('title'); - echo $this->Form->input('body'); - //echo $this->Form->input('subdoc.name'); - //echo $this->Form->input('subdoc.age'); - echo $this->Form->input('Subdocument.subdoc.0.name'); - echo $this->Form->input('Subdocument.subdoc.0.age'); - echo $this->Form->input('Subdocument.subdoc.1.name'); - echo $this->Form->input('Subdocument.subdoc.1.age'); - ?> -
-Form->end('Submit');?> -
-
-
    -
  • Html->link(__('List Subdocuments', true), array('action'=>'index'));?>
  • -
-
- - -Form->end('Submit');?> - -
-
    -
  • Html->link(__('List Subdocuments', true), array('action'=>'index'));?>
  • -
-
diff --git a/samples/views/subdocuments/edit.ctp b/samples/views/subdocuments/edit.ctp deleted file mode 100644 index 2b1be58..0000000 --- a/samples/views/subdocuments/edit.ctp +++ /dev/null @@ -1,21 +0,0 @@ -
-Form->create('Subdocument' , array( 'type' => 'post' ));?> -
- - Form->hidden('_id'); - echo $this->Form->input('title'); - echo $this->Form->input('body'); - echo $this->Form->input('Subdocument.subdoc.0.name'); - echo $this->Form->input('Subdocument.subdoc.0.age'); - echo $this->Form->input('Subdocument.subdoc.1.name'); - echo $this->Form->input('Subdocument.subdoc.1.age'); - ?> -
-Form->end('Submit');?> -
-
-
    -
  • Html->link(__('List Subdocuments', true), array('action'=>'index'));?>
  • -
-
diff --git a/samples/views/subdocuments/index.ctp b/samples/views/subdocuments/index.ctp deleted file mode 100644 index 1bc8b6c..0000000 --- a/samples/views/subdocuments/index.ctp +++ /dev/null @@ -1,15 +0,0 @@ -Html->link('Add data', 'add'); ?> -
-
- - - id: [Html->link('edit','edit/'.$result['Subdocument']['_id']); ?>] [Html->link('delete','delete/'.$result['Subdocument']['_id']); ?>]
- title:
- body:
- $val): ?> - subdoc_name:
- subdoc_age:
- - -
- From ae89f695d662b39d94083f885c9b547e325edb25 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 09:03:24 +1100 Subject: [PATCH 08/33] Remove debug statements. --- Model/Datasource/MongodbSource.php | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php index 7632d4f..bfbc382 100644 --- a/Model/Datasource/MongodbSource.php +++ b/Model/Datasource/MongodbSource.php @@ -306,7 +306,6 @@ public function isConnected() { * @access public */ public function getMongoDb() { - syslog(LOG_NOTICE, 'getMongoDb :'.print_r($this->_db, true)); if ($this->connected === false) { return false; } @@ -509,15 +508,11 @@ public function create(Model $Model, $fields = null, $values = null) { $this->logQuery("db.{$Model->table}.insert( :data , :params )", compact('data','params')); } - syslog(LOG_NOTICE, 'Create '); - if (! empty($return)) { - syslog(LOG_NOTICE, 'Return '.json_encode($return)); $id = $this->lastResult->getInsertedId(); if ($this->config['set_string_id'] && is_object($id)) { $id = $this->lastResult->getInsertedId()->__toString(); } - syslog(LOG_NOTICE, 'Create _id '.json_encode($id)); $Model->setInsertID($id); $Model->id = $id; return true; @@ -755,8 +750,6 @@ public function update(Model $Model, $fields = null, $values = null, $conditions $this->_prepareLogQuery($Model); // just sets a timer $return = false; if (! empty($data['_id'])) { - syslog(LOG_NOTICE, 'Update A'); - $this->_convertId($data['_id']); $cond = array('_id' => $data['_id']); unset($data['_id']); @@ -764,8 +757,6 @@ public function update(Model $Model, $fields = null, $values = null, $conditions $data = $this->setMongoUpdateOperator($Model, $data); $params = $this->collectionOptions['update']; try { - syslog(LOG_NOTICE, 'Cond '.json_encode($cond)); - syslog(LOG_NOTICE, 'Data '.json_encode($data)); if ($Model->mongoNoSetOperator === true) { $this->lastResult = $mongoCollectionObj->replaceOne($cond, $data, $params); } else { @@ -784,7 +775,6 @@ public function update(Model $Model, $fields = null, $values = null, $conditions } else { // Not sure this block ever executes. // If $data['_id'] is empty does the Model call $this->create() instead ?? - syslog(LOG_NOTICE, 'Update B'); $params = $this->collectionOptions['save']; try{ $this->lastResult = $mongoCollectionObj->insertOne($data, $params); @@ -797,8 +787,6 @@ public function update(Model $Model, $fields = null, $values = null, $conditions $this->logQuery("db.{$Model->useTable}.save( :data, :params )", compact('data', 'params')); } } - syslog(LOG_NOTICE, 'Update '.json_encode($this->lastResult)); - return $return; } @@ -820,34 +808,26 @@ public function setMongoUpdateOperator(&$Model, $data) { $updateField = 'modified'; } - syslog(LOG_NOTICE, 'setMongoUpdateOperator '.$updateField); - syslog(LOG_NOTICE, "mongoNoSetOperator ".$Model->mongoNoSetOperator); //setting Mongo operator if (empty($Model->mongoNoSetOperator)) { if(!preg_grep('/^\$/', array_keys($data))) { $data = array('$set' => $data); - syslog(LOG_NOTICE, 'SMUO A'); } else { if(!empty($data[$updateField])) { $modified = $data[$updateField]; unset($data[$updateField]); $data['$set'] = array($updateField => $modified); } - syslog(LOG_NOTICE, 'SMUO B'); } } elseif (substr($Model->mongoNoSetOperator,0,1) === '$') { if(!empty($data[$updateField])) { $modified = $data[$updateField]; unset($data[$updateField]); $data = array($Model->mongoNoSetOperator => $data, '$set' => array($updateField => $modified)); - syslog(LOG_NOTICE, 'SMUO C'); } else { $data = array($Model->mongoNoSetOperator => $data); - syslog(LOG_NOTICE, 'SMUO D'); } } - syslog(LOG_NOTICE, 'SMUO E'); - return $data; } @@ -879,9 +859,6 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { trigger_error($this->error); } - var_dump($this->lastResult); - syslog(LOG_NOTICE, 'updateAll ' . (! empty($this->lastResult))); - if ($this->fullDebug) { $this->logQuery("db.{$Model->table}.update( :conditions, :fields, :params )", array('conditions' => $conditions, 'fields' => $fields, 'params' => $this->collectionOptions['update']) @@ -1229,7 +1206,6 @@ public function query() { $count = 0; // Its a cursor - but is it always only a one document cursor ? foreach($cursor as $doc) { - syslog(LOG_NOTICE, 'Query : '.json_encode($doc)); $return = $doc; $count++; } From ca4d05cc721ecac2ae84fda42a611cc54c58b0bf Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 09:36:57 +1100 Subject: [PATCH 09/33] Update readme. --- README.markdown | 67 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/README.markdown b/README.markdown index c612b4d..f2f680c 100644 --- a/README.markdown +++ b/README.markdown @@ -1,23 +1,25 @@ # MongoDB datasource for CakePHP 2.8+ ## Requirements -PHP5.6, PHP7+ -pecl mongodb (http://php.net/mongo) +PHP 5.6, PHP 7+ +CakePHP 2.x (> 2.8, < 3.0) +php-mongodb extension (not the php-mongo extension). ## Installation -this repository should be installed in the same way as any other plugin. +This repository should be installed in the same way as any other plugin. +See http://book.cakephp.org/2.0/en/plugins/how-to-install-plugins.html To install the driver for use in a single application: cd my/app/plugins - git clone git://github.com/ichikaway/cakephp-mongodb.git mongodb + git clone git://github.com/HandsetDetection/MongoDBLib.git MongoDBLib To install the driver for use in any/multiple application(s) # where ROOT is the name of the directory parent to the base index.php of CakePHP. cd ROOT/plugins - git clone git://github.com/ichikaway/cakephp-mongodb.git mongodb + git clone git://github.com/HandsetDetection/MongoDBLib.git MongoDBLib ## Sample Code @@ -28,7 +30,7 @@ To use this DB driver, install (obviously) and define a db source such as follow class DATABASE_CONFIG { public $default = array( - 'driver' => 'mongodb.mongodbSource', + 'driver' => 'MongoDBLib.mongodbSource', 'database' => 'driver', 'host' => 'localhost', 'port' => 27017, @@ -42,35 +44,58 @@ To use this DB driver, install (obviously) and define a db source such as follow ); -More detail of replicaset in wiki: -https://github.com/ichikaway/cakephp-mongodb/wiki/How-to-connect-to-replicaset-servers +Model files need to have mongoSchema property, or make use of the schemaless behavior. +Mongo uses a primary key named "\_id" (cannot be renamed). It can be any format you like but if you don't explicitly set it Mongo will use an automatic 24 character (uu)id. -Model files need to have mongoSchema property - or make use of the schemaless behavior. +## Update Notes -Mongo uses a primary key named "\_id" (cannot be renamed). It can be any format you like but if you don't explicitly set it Mongo will use an automatic 24 character (uu)id. +This plugin was derived from the most excellent cakephp-mongodb plugin by Yasushi Ichikawa, originally built for CakePHP 1.3. +cakephp-mongodb works with the php-mono extension which has since been deprecated and will not run on PHP 7.0. -Before you start, you may find it useful to see [a model sample.](http://github.com/ichikaway/mongoDB-Datasource/blob/master/samples/models/post.php) -There are also some sample [controller actions: find,save,delete,deleteAll,updateAll](http://github.com/ichikaway/mongoDB-Datasource/blob/master/samples/controllers/posts_controller.php) note that your controller code needs no specific code to use this datasource. +This plugin is an updated version of cakephp-mongodb and strives to be as backwardly compatible as possible. It uses the +newer php-mongodb extension. If you're migrating from cakephp-mongodb to this plugin please be aware there are a number +of breaking changes. Namely, all the types and classes have changed. -## Author -Yasushi Ichikawa ([ichikaway](http://twitter.com/ichikaway)) +1) Types : All the types have changed. + +MongoId becomes MongoDB\BSON\ObjectID +MongoCode becomes MongoDB\BSON\JavaScript +MongoDate becomes MongoDB\BSON\UTCDateTime +MongoRegex becomes MongoDB\BSON\Regex +MongoBinData becomes MongoDB\BSON\Binary +MongoInt32 php-mongodb extension now chooses the best type automagically. +MongoInt64 php-mongodb extension now chooses the best type automagically. +MongoDBRef deprecated - no corresponding class +MongoMinKey becomes MongoDB\BSON\MinKey +MongoMaxKey becomes MongoDB\BSON\MaxKey +MongoTimestamp becomes MongoDB\BSON\Timestamp + +2) Classes + +There's too much detail to cover here with the class changes so check out these references below. +Old classes : http://php.net/manual/en/book.mongo.php +New classes : http://php.net/manual/en/set.mongodb.php + +3) All CRUD calls are the same and should return identical information to the cakephp-mongodb plugin. +Calls doing mapReduce, aggregation and executing database commands should be extensively tested. + + +## Original Authors +Yasushi Ichikawa ([ichikaway](http://twitter.com/ichikaway)) Andy Dawson ([AD7six](http://twitter.com/AD7six)) -## Contributors -[Predominant](http://github.com/predominant/) : Cleanup code, add documentation +## Original Contributors +[Predominant](http://github.com/predominant/) : Cleanup code, add documentation [Jrbasso](http://github.com/jrbasso/) : Cleanup code - [tkyk](http://github.com/tkyk/) : Fix bug, Add some function. -## Reference -Reference code, Thank you! +## Original Reference +Reference code, Thank you! [Nate Abele's lithium mongoDB datasource](http://li3.rad-dev.org/) - [Joél Perras' divan](http://github.com/jperras/divan/) - From 0f4daeccfca571a541bbc721b93744972496093b Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 09:52:55 +1100 Subject: [PATCH 10/33] Updated readme. --- README.markdown | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.markdown b/README.markdown index f2f680c..0c4fefa 100644 --- a/README.markdown +++ b/README.markdown @@ -1,25 +1,30 @@ # MongoDB datasource for CakePHP 2.8+ +Note : This datasource is Beta, feedback and pull requests (with tests) welcome. :-) + ## Requirements -PHP 5.6, PHP 7+ -CakePHP 2.x (> 2.8, < 3.0) -php-mongodb extension (not the php-mongo extension). + + PHP 5.6, PHP 7+ + CakePHP 2.x (> 2.8, < 3.0) + +## Dependency + + php-mongodb extension (not the php-mongo extension). + mongodb-php-library (https://docs.mongodb.com/php-library/master/, dependency is listed in composer) ## Installation This repository should be installed in the same way as any other plugin. See http://book.cakephp.org/2.0/en/plugins/how-to-install-plugins.html +Use composer + + composer require "HandsetDetection/MongoDBLib=^1.0.0" + To install the driver for use in a single application: cd my/app/plugins git clone git://github.com/HandsetDetection/MongoDBLib.git MongoDBLib - -To install the driver for use in any/multiple application(s) - - # where ROOT is the name of the directory parent to the base index.php of CakePHP. - cd ROOT/plugins - git clone git://github.com/HandsetDetection/MongoDBLib.git MongoDBLib ## Sample Code From 91412f9ebf1b8aee60ca071ce41ac07788fb344c Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 09:57:21 +1100 Subject: [PATCH 11/33] Cleanup JSON for Packagist --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6bc1b31..32c12a8 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "type": "cakephp-plugin", "require": { "php": ">=5.6", - "mongodb/mongodb": ">=1.0.0", + "mongodb/mongodb": ">=1.0.0" }, "require-dev": { "phpunit/phpunit": "4.0.*" From 39e9906e3698b381b00f9f765b5616f9e790246c Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 10:20:46 +1100 Subject: [PATCH 12/33] Remove uppercase chars from package name ! Doh, i always forget this. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 32c12a8..ff31d7a 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "HandsetDetection/MongoDBLib", + "name": "handsetdetection/mongodblib", "description": "A CakePHP Driver from MongoDB using the new mongodb extension. For CakePHP 2.8+ (Not CakePHP 3.x)", "homepage": "https://github.com/HandsetDetection/MongoDBLib", "keywords": ["cakephp", "mongodb", "php", "mongo"], From d52cce67dcc4ac9a1b39ec24eb2c0dd78ac3b23f Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 10:25:32 +1100 Subject: [PATCH 13/33] Adjust composer instructions for lowercase composer name. --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 0c4fefa..1af6e53 100644 --- a/README.markdown +++ b/README.markdown @@ -19,7 +19,7 @@ See http://book.cakephp.org/2.0/en/plugins/how-to-install-plugins.html Use composer - composer require "HandsetDetection/MongoDBLib=^1.0.0" + composer require "handsetdetection/mongodblib=^1.0.0" To install the driver for use in a single application: From ec2eae57a89e6c6cf0ebcad555df194dcff73b55 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 15:20:41 +1100 Subject: [PATCH 14/33] Add Travis CI --- .travis.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2370e88 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,37 @@ +language: php + +php: + - 5.6 + +env: + global: + - PLUGIN_NAME=MongoDBLib + - REQUIRE="" + + matrix: + - DB=mysql CAKE_VERSION=2.8 + - DB=mysql CAKE_VERSION=2.9 + +matrix: + include: + - php: 5.4 + env: + - DB=mysql + - CAKE_VERSION=2.7 + - CODECOVERAGE=1 + - php: 5.4 + env: + - PHPCS=1 + +before_script: + - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis + - travis_wait ../travis/before_script.sh + +script: + - ../travis/script.sh + +after_success: + - ../travis/after_success.sh + +notifications: + email: false From acc2fa95777c572c6321c34516d0dbd924a918b1 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 15:32:55 +1100 Subject: [PATCH 15/33] Install php-mongodb extension for php 5.6 Enable extension in php 5.6 & 7.0 Add php 7.0 as test target (will this work with cakephp 2.8?) --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2370e88..3c07599 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,11 @@ language: php php: - 5.6 + - 7.0 +services: + - mongodb + env: global: - PLUGIN_NAME=MongoDBLib @@ -14,16 +18,18 @@ env: matrix: include: - - php: 5.4 + - php: 5.6 env: - DB=mysql - CAKE_VERSION=2.7 - CODECOVERAGE=1 - - php: 5.4 + - php: 5.6 env: - PHPCS=1 before_script: + - if [[ $TRAVIS_PHP_VERSION =~ 5.6 ]] ; then echo yes | pecl install mongodb; fi; + - echo "extension=mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis - travis_wait ../travis/before_script.sh From 5b9ef91f833992573e292b4e9c7605e8f3d50019 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 16:16:30 +1100 Subject: [PATCH 16/33] Use correct path to tests. --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c07599..9ba1a05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,18 +10,19 @@ services: env: global: - PLUGIN_NAME=MongoDBLib + - DATASOURCE_NAME=MongodbSource - REQUIRE="" matrix: - - DB=mysql CAKE_VERSION=2.8 - - DB=mysql CAKE_VERSION=2.9 + - CAKE_VERSION=2.8 + - CAKE_VERSION=2.9 matrix: include: - php: 5.6 env: - DB=mysql - - CAKE_VERSION=2.7 + - CAKE_VERSION=2.8 - CODECOVERAGE=1 - php: 5.6 env: @@ -34,10 +35,7 @@ before_script: - travis_wait ../travis/before_script.sh script: - - ../travis/script.sh - -after_success: - - ../travis/after_success.sh + - if [ -d ../cakephp/app ]; then cd ../cakephp/app; fi; ./Console/cake test $PLUGIN_NAME Datasource/$DATASOURCE_NAME notifications: email: false From 7e8513dd176b6c96f13e16faf538cba845a1f15a Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 16:30:27 +1100 Subject: [PATCH 17/33] Remove debug from tests. --- Test/Case/Datasource/MongodbSourceTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index a9a5647..3a5ed97 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -131,7 +131,7 @@ class MongodbSourceTest extends CakeTestCase { public function setUp() { $connections = ConnectionManager::enumConnectionObjects(); - if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'mongodbSource') { + if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'MongodbSource') { $config = new DATABASE_CONFIG(); $this->_config = $config->test; } @@ -150,7 +150,7 @@ public function setUp() { $this->MongoArticle = ClassRegistry::init('MongoArticle'); $this->MongoArticle->setDataSource('mongo_test'); - syslog(LOG_NOTICE, 'setUp '.$this->getName()); + //syslog(LOG_NOTICE, 'setUp '.$this->getName()); } /** @@ -315,7 +315,6 @@ public function testGetMongoDb() { */ public function testGetMongoDbFromModel() { $obj = $this->Post->getMongoDb(); - syslog(LOG_NOTICE, 'testGetMongoDbFromModel :'.print_r($obj, true)); $this->assertTrue(is_object($obj)); $objName = get_class($obj); $this->assertEqual('MongoDB\Database', $objName); @@ -806,8 +805,6 @@ public function testUpdateWithoutMongoSchemaProperty() { $result = null; $result = $this->MongoArticle->find('all'); - print_r($result); - $this->assertEqual(1, count($result)); $resultData = $result[0]['MongoArticle']; $this->assertEqual($this->MongoArticle->id, $resultData['_id']); From e784112183efef0b558f623346dcb8c84c3c97a9 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 19:28:51 +1100 Subject: [PATCH 18/33] BugFix : Change MongoDate to new class MongoDB\BSON\UTCDateTime. --- Model/Behavior/SqlCompatibleBehavior.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Model/Behavior/SqlCompatibleBehavior.php b/Model/Behavior/SqlCompatibleBehavior.php index 6c499f7..99f5433 100644 --- a/Model/Behavior/SqlCompatibleBehavior.php +++ b/Model/Behavior/SqlCompatibleBehavior.php @@ -130,8 +130,8 @@ protected function convertDates(&$results) { foreach($results as &$row) { $this->convertDates($row); } - } elseif (is_a($results, 'MongoDate')) { - $results = date('Y-M-d h:i:s', $results->sec); + } elseif (is_a($results, 'MongoDB\BSON\UTCDateTime')) { + $results = date('Y-M-d h:i:s', $results->toDateTime()->getTimestamp()); } } From 228ac249c05b2438bf5daacce82221cd9eadebf0 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 19:31:06 +1100 Subject: [PATCH 19/33] BugFix : Replace MongoDate with MongoDB\BSON\UTCDateTime --- Test/Case/Datasource/MongodbSourceTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index 3a5ed97..227e9c7 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -130,8 +130,7 @@ class MongodbSourceTest extends CakeTestCase { */ public function setUp() { $connections = ConnectionManager::enumConnectionObjects(); - - if (!empty($connections['test']['classname']) && $connections['test']['classname'] === 'MongodbSource') { + if (! empty($connections['test']['classname']) && $connections['test']['classname'] === 'MongodbSource') { $config = new DATABASE_CONFIG(); $this->_config = $config->test; } @@ -780,8 +779,8 @@ public function testUpdateWithoutMongoSchemaProperty() { 'body' => 'aaaa', 'text' => 'bbbb', 'count' => 0, - 'created' => new mongoDate(), - 'modified' => new mongoDate(), + 'created' => new MongoDB\BSON\UTCDateTime(time()), + 'modified' => new MongoDB\BSON\UTCDateTime(time()), ); $saveData['MongoArticle'] = $data; From 20129c0b3f03a0803084d7adc7e2a15b76bc0434 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 19:31:58 +1100 Subject: [PATCH 20/33] BugFIx : Use MongoDB::MONGODB_VERSION constant to get the driver version. --- Model/Datasource/MongodbSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php index bfbc382..87c4d8c 100644 --- a/Model/Datasource/MongodbSource.php +++ b/Model/Datasource/MongodbSource.php @@ -62,7 +62,7 @@ class MongodbSource extends DboSource { * @var string * @access protected */ - protected $_driverVersion = Mongo::VERSION; + protected $_driverVersion = MONGODB_VERSION; /** * startTime property @@ -237,7 +237,7 @@ public function connect() { * Create connection string * * @param array $config - * @param string $version version of MongoDriver + * @string connection string */ public function createConnectionString($config) { $host = "mongodb://"; From 70e745e0ec00e0c971fa089e035aba5d5602e859 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 19:51:47 +1100 Subject: [PATCH 21/33] BugFix : Update exceptions to new exception classes. BugFix : Add try/catch blocks to all database calls. --- Model/Datasource/MongodbSource.php | 174 ++++++++++++++++++----------- 1 file changed, 106 insertions(+), 68 deletions(-) diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php index 87c4d8c..846676c 100644 --- a/Model/Datasource/MongodbSource.php +++ b/Model/Datasource/MongodbSource.php @@ -226,7 +226,7 @@ public function connect() { if ($this->_db = $this->connection->selectDatabase($this->config['database'])) { $this->connected = true; } - } catch(MongoException $e) { + } catch(MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -272,11 +272,11 @@ public function insertMulti($table, $fields, $values) { } $this->_prepareLogQuery($Model->table); // just sets a timer $params = array_merge($this->collectionOptions['batchInsert']); - try{ + try { $collection = $this->_db ->selectCollection($Model->table) ->insertMany($data, $params); - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -421,7 +421,7 @@ public function listSources($data = null) { public function describe($Model) { $Model->primaryKey = '_id'; $schema = array(); - if (!empty($Model->mongoSchema) && is_array($Model->mongoSchema)) { + if (! empty($Model->mongoSchema) && is_array($Model->mongoSchema)) { $schema = $Model->mongoSchema; return $schema + $this->_defaultSchema; } elseif ($this->isConnected() && is_a($Model, 'Model') && !empty($Model->Behaviors)) { @@ -500,7 +500,7 @@ public function create(Model $Model, $fields = null, $values = null) { ->selectCollection($Model->table) ->insertOne($data, $params); $return = true; - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -593,11 +593,11 @@ public function distinct(&$Model, $keys = array(), $params = array()) { if (array_key_exists('conditions', $params)) { $params = $params['conditions']; } - try{ + try { $return = $this->_db ->selectCollection($Model->table) ->distinct($keys, $params); - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -658,7 +658,7 @@ public function group($params, Model $Model = null) { $options ); $return = $tmp->toArray()[0]; - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -689,7 +689,7 @@ public function ensureIndex(&$Model, $keys = array(), $params = array()) { $return = $this->_db ->selectCollection($Model->table) ->createIndex($keys, $params); - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -741,7 +741,7 @@ public function update(Model $Model, $fields = null, $values = null, $conditions try { $mongoCollectionObj = $this->_db ->selectCollection($Model->table); - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); return false; @@ -763,7 +763,7 @@ public function update(Model $Model, $fields = null, $values = null, $conditions $this->lastResult = $mongoCollectionObj->updateOne($cond, $data, $params); } $return = true; - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -776,10 +776,10 @@ public function update(Model $Model, $fields = null, $values = null, $conditions // Not sure this block ever executes. // If $data['_id'] is empty does the Model call $this->create() instead ?? $params = $this->collectionOptions['save']; - try{ + try { $this->lastResult = $mongoCollectionObj->insertOne($data, $params); $return = true; - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -810,7 +810,7 @@ public function setMongoUpdateOperator(&$Model, $data) { //setting Mongo operator if (empty($Model->mongoNoSetOperator)) { - if(!preg_grep('/^\$/', array_keys($data))) { + if (! preg_grep('/^\$/', array_keys($data))) { $data = array('$set' => $data); } else { if(!empty($data[$updateField])) { @@ -820,7 +820,7 @@ public function setMongoUpdateOperator(&$Model, $data) { } } } elseif (substr($Model->mongoNoSetOperator,0,1) === '$') { - if(!empty($data[$updateField])) { + if (! empty($data[$updateField])) { $modified = $data[$updateField]; unset($data[$updateField]); $data = array($Model->mongoNoSetOperator => $data, '$set' => array($updateField => $modified)); @@ -854,7 +854,7 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { $this->lastResult = $this->_db ->selectCollection($Model->table) ->updateMany($conditions, $fields); - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } @@ -876,7 +876,7 @@ public function updateAll(&$Model, $fields = null, $conditions = null) { * @access public */ public function deriveSchemaFromData($Model, $data = array()) { - if (!$data) { + if (! $data) { $data = $Model->data; if ($data && array_key_exists($Model->alias, $data)) { $data = $data[$Model->alias]; @@ -948,20 +948,21 @@ public function delete(Model $Model, $conditions = null) { } $return = false; + $count = 0; try { $this->_prepareLogQuery($Model); $this->lastResult = $this->_db ->selectCollection($Model->table) ->deleteMany($conditions); $count = $this->lastResult->getDeletedCount(); - if ($this->fullDebug) { - $this->logQuery("db.{$Model->table}.remove( :conditions )", compact('conditions', 'count')); - } $return = true; - } catch (MongoException $e) { + } catch (MongoDB\Driver\Exception\Exception $e) { $this->error = $e->getMessage(); trigger_error($this->error); } + if ($this->fullDebug) { + $this->logQuery("db.{$Model->table}.remove( :conditions )", compact('conditions', 'count')); + } return $return; } @@ -1042,9 +1043,14 @@ public function read(Model $Model, $query = array(), $recursive = null) { $queryType = isset($Model->findQueryType) ? $Model->findQueryType : 'all'; if ($queryType === 'count') { - $count = $this->_db - ->selectCollection($Model->table) - ->count($conditions); + try { + $count = $this->_db + ->selectCollection($Model->table) + ->count($conditions); + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } if ($this->fullDebug) $this->logQuery("db.{$Model->useTable}.count( :conditions )", compact('conditions', 'count')); @@ -1059,25 +1065,32 @@ public function read(Model $Model, $query = array(), $recursive = null) { 'limit' => $limit, 'skip' => $offset ); - if (! empty($this->_findOptions)) + if (! empty($this->_findOptions)) { $options = array_merge($options, $this->_findOptions); - - $cursor = $this->_db - ->selectCollection($Model->table) - ->find($conditions, $options); + } $count = 0; - // Iterate over cursor - foreach($cursor as $mongodata) { - if ($this->config['set_string_id'] && ! empty($mongodata['_id']) && is_object($mongodata['_id'])) { - $mongodata['_id'] = $mongodata['_id']->__toString(); + try { + $cursor = $this->_db + ->selectCollection($Model->table) + ->find($conditions, $options); + + // Iterate over cursor + foreach($cursor as $mongodata) { + if ($this->config['set_string_id'] && ! empty($mongodata['_id']) && is_object($mongodata['_id'])) { + $mongodata['_id'] = $mongodata['_id']->__toString(); + } + $return[][$Model->alias] = $mongodata; + $count++; } - $return[][$Model->alias] = $mongodata; - $count++; + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); } - if ($this->fullDebug) + if ($this->fullDebug) { $this->logQuery("db.{$Model->table}.find( :conditions, :options )", compact('conditions', 'options', 'count')); + } return $return; } @@ -1100,22 +1113,36 @@ public function read(Model $Model, $query = array(), $recursive = null) { // If remove is set then replace the document otherwise update. if (! empty($remove)) { - $this->lastResult = $this->_db - ->selectCollection($Model->table) - ->findOneAndReplace($conditions, array('$set' => $modify), $options); + try { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->findOneAndReplace($conditions, array('$set' => $modify), $options); + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } - if ($this->fullDebug) + if ($this->fullDebug) { $logQuery = "db.{$Model->table}.findOneAndReplace( :conditions, :options )"; + } } else { - $this->lastResult = $this->_db - ->selectCollection($Model->table) - ->findOneAndUpdate($conditions, array('$set' => $modify), $options); + + try { + $this->lastResult = $this->_db + ->selectCollection($Model->table) + ->findOneAndUpdate($conditions, array('$set' => $modify), $options); + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } - if ($this->fullDebug) + if ($this->fullDebug) { $logQuery = "db.{$Model->table}.findOneAndUpdate( :conditions, :options )"; + } } - $result = MongoDB\BSON\toPHP($this->lastResult); + //$result = MongoDB\BSON\toPHP($this->lastResult); + $result = $this->lastResult; $count = 0; if (! empty($result)) { @@ -1126,10 +1153,9 @@ public function read(Model $Model, $query = array(), $recursive = null) { $return[][$Model->alias] = $result; } - if ($this->fullDebug) + if ($this->fullDebug) { $this->logQuery($logQuery, compact($conditions, $options, $count)); - - return $return; + } } return $return; } @@ -1157,9 +1183,15 @@ public function truncate($table) { if (! $this->isConnected()) { return false; } - return $this->_db - ->selectCollection($table) - ->deleteMany(); + try { + return $this->_db + ->selectCollection($table) + ->deleteMany(); + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); + } + return false; } /** @@ -1193,24 +1225,30 @@ public function query() { $this->_prepareLogQuery($Model); $return = array(); - $cursor = $this->_db - ->command($query); + try { + $cursor = $this->_db + ->command($query); - if (! is_object($cursor)) { - if ($this->fullDebug) { - $this->logQuery("Failed : db.command( :query )", compact('query')); + if (! is_object($cursor)) { + if ($this->fullDebug) { + $this->logQuery("Failed : db.command( :query )", compact('query')); + } + return false; } - return false; - } - $count = 0; - // Its a cursor - but is it always only a one document cursor ? - foreach($cursor as $doc) { - $return = $doc; - $count++; - } - if ($this->fullDebug) { - $this->logQuery("db.command( :query )", compact('query', 'count')); + $count = 0; + // Its a cursor - but is it always only a one document cursor ? + foreach($cursor as $doc) { + $return = $doc; + $count++; + } + + if ($this->fullDebug) { + $this->logQuery("db.command( :query )", compact('query', 'count')); + } + } catch (MongoDB\Driver\Exception\Exception $e) { + $this->error = $e->getMessage(); + trigger_error($this->error); } return $return; } @@ -1382,9 +1420,9 @@ protected function _stringify(&$args = array(), $level = 0) { $this->_stringify($arg, $level + 1); } elseif (is_object($arg) && is_callable(array($arg, '__toString'))) { $class = get_class($arg); - if ($class === 'MongoId') { + if ($class === 'MongoDB\BSON\ObjectID') { $arg = 'ObjectId(' . $arg->__toString() . ')'; - } elseif ($class === 'MongoRegex') { + } elseif ($class === 'MongoDB\BSON\Regex ') { $arg = '_regexstart_' . $arg->__toString() . '_regexend_'; } else { $arg = $class . '(' . $arg->__toString() . ')'; From eed423387aab28769f6831ab7dceebb5d6bfe38b Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 19:58:07 +1100 Subject: [PATCH 22/33] Have composer install deps. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9ba1a05..4b4ae15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ before_script: - echo "extension=mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis - travis_wait ../travis/before_script.sh + - travis_retry composer install script: - if [ -d ../cakephp/app ]; then cd ../cakephp/app; fi; ./Console/cake test $PLUGIN_NAME Datasource/$DATASOURCE_NAME From 034f27813d0c936d6ca29f267412d50a53f0626d Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 20:06:46 +1100 Subject: [PATCH 23/33] Ensure composer autoload.php executes before tests run. --- Model/Datasource/MongodbSource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php index 846676c..decffc1 100644 --- a/Model/Datasource/MongodbSource.php +++ b/Model/Datasource/MongodbSource.php @@ -25,6 +25,8 @@ * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ +require_once APP . 'Vendor/autoload.php'; + App::import('Datasource', 'DboSource'); App::import('Utility', 'CakeText'); From 83621ba4c27cd925b25cf6088137fdd852a96fad Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 20:15:27 +1100 Subject: [PATCH 24/33] Fix previous blunder - doh ! --- Model/Datasource/MongodbSource.php | 2 -- Test/Case/Datasource/MongodbSourceTest.php | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Model/Datasource/MongodbSource.php b/Model/Datasource/MongodbSource.php index decffc1..846676c 100644 --- a/Model/Datasource/MongodbSource.php +++ b/Model/Datasource/MongodbSource.php @@ -25,8 +25,6 @@ * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ -require_once APP . 'Vendor/autoload.php'; - App::import('Datasource', 'DboSource'); App::import('Utility', 'CakeText'); diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index 227e9c7..c66a87f 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -19,6 +19,7 @@ /** * Import relevant classes for testing */ + App::uses('Model', 'Model'); App::uses('AppModel', 'Model'); From d378b790ef20d6d298565e22d9d2a3400de66132 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 20:19:25 +1100 Subject: [PATCH 25/33] Add composer autoload to cakephp bootstrap. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4b4ae15..11f61e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ before_script: - git clone -b master https://github.com/FriendsOfCake/travis.git --depth 1 ../travis - travis_wait ../travis/before_script.sh - travis_retry composer install + - echo "require APP . 'vendor' . DS . 'autoload.php';" >> ../cakephp/app/Config/bootstrap.php script: - if [ -d ../cakephp/app ]; then cd ../cakephp/app; fi; ./Console/cake test $PLUGIN_NAME Datasource/$DATASOURCE_NAME From e350dc0f2a61b152a87ac9e29e1e33ea0546d2d9 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 20:37:20 +1100 Subject: [PATCH 26/33] Update composer.json for correct phpunit version. (Cakephp 2.x needs phpunit 3.x). --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ff31d7a..21be56a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "mongodb/mongodb": ">=1.0.0" }, "require-dev": { - "phpunit/phpunit": "4.0.*" + "phpunit/phpunit": "3.7.*" }, "authors": [ { From a696900cc8d8fe7ab6edf360e1d10fb04bcb288a Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 21:08:51 +1100 Subject: [PATCH 27/33] Remove reference to mysql. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 11f61e6..3b71a62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ matrix: include: - php: 5.6 env: - - DB=mysql - CAKE_VERSION=2.8 - CODECOVERAGE=1 - php: 5.6 From fc6357ad717df9fd81e51015e23d1200fc89b250 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 21:12:02 +1100 Subject: [PATCH 28/33] Cleanup better after tests. --- Test/Case/Datasource/MongodbSourceTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index c66a87f..e5e101c 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -160,11 +160,11 @@ public function setUp() { * @access public */ public function tearDown() { - //$this->dropData(); - //unset($this->Post); - //unset($this->Mongo); - //unset($this->mongodb); - //ClassRegistry::flush(); + $this->dropData(); + unset($this->Post); + unset($this->Mongo); + unset($this->mongodb); + ClassRegistry::flush(); } From ca8bdafb164e80802183d9dc7b71d8d72f14c534 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 21:15:59 +1100 Subject: [PATCH 29/33] BugFix : testCreateConnectionName using incorrect datasource name. --- Test/Case/Datasource/MongodbSourceTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index e5e101c..7fef561 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -231,7 +231,7 @@ public function dropData() { */ public function testCreateConnectionName() { $config = array( - 'datasource' => 'mongodb', + 'datasource' => 'MongoDBLib.MongodbSource', 'host' => 'localhost', 'login' => '', 'password' => '', @@ -245,7 +245,7 @@ public function testCreateConnectionName() { $this->assertIdentical($expect, $host); $config = array( - 'datasource' => 'mongodb', + 'datasource' => 'MongoDBLib.MongodbSource', 'host' => 'localhost', 'login' => 'user', 'password' => 'pass', From fcea761ca643925706d062bc51e1aa0275322f0d Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Tue, 1 Nov 2016 21:56:53 +1100 Subject: [PATCH 30/33] Fix datasource issues with $test database defaults. --- Test/Case/Datasource/MongodbSourceTest.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index 7fef561..9cf4492 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -130,11 +130,9 @@ class MongodbSourceTest extends CakeTestCase { * @access public */ public function setUp() { - $connections = ConnectionManager::enumConnectionObjects(); - if (! empty($connections['test']['classname']) && $connections['test']['classname'] === 'MongodbSource') { - $config = new DATABASE_CONFIG(); - $this->_config = $config->test; - } + // Recreate the $test database definition as our Datasource. + ConnectionManager::drop('test'); + ConnectionManager::create('test', $this->_config); ConnectionManager::create('mongo_test', $this->_config); $this->Mongo = new MongodbSource($this->_config); From 7c959382f2e5f517328aad13e892bfe39aabe04c Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Wed, 2 Nov 2016 13:38:30 +1100 Subject: [PATCH 31/33] Added debug to tests - tracing a travis issue. --- Test/Case/Datasource/MongodbSourceTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index 9cf4492..2d7b9cb 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -143,6 +143,7 @@ public function setUp() { $this->mongodb = ConnectionManager::getDataSource('mongo_test'); $this->mongodb->connect(); + echo $this->getMongodVersion()."\n"; $this->dropData(); $this->MongoArticle = ClassRegistry::init('MongoArticle'); @@ -209,6 +210,7 @@ public function dropData() { ->selectDatabase($this->_config['database']); foreach($db->listCollections() as $collection) { + echo "About to drop ".$collection->getName()." in ".$this->_config['database']."\n"; $result = $this->mongodb ->connection ->selectDatabase($this->_config['database']) From 3897805e6d43adc5b94c4a5732590e25290027e8 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Wed, 2 Nov 2016 14:05:28 +1100 Subject: [PATCH 32/33] Install Mongodb 3.2 (Travis uses 2.4). --- .travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3b71a62..fe073d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,6 @@ php: - 5.6 - 7.0 -services: - - mongodb - env: global: - PLUGIN_NAME=MongoDBLib @@ -17,6 +14,15 @@ env: - CAKE_VERSION=2.8 - CAKE_VERSION=2.9 +addons: + apt: + sources: + - mongodb-upstart + - mongodb-3.2-precise + packages: + - mongodb-org-server + - mongodb-org-shell + matrix: include: - php: 5.6 From b6a1d37f76096a5e315bb2a3555553c325177518 Mon Sep 17 00:00:00 2001 From: Richard Uren Date: Wed, 2 Nov 2016 14:14:42 +1100 Subject: [PATCH 33/33] Don't drop system collections. --- Test/Case/Datasource/MongodbSourceTest.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Test/Case/Datasource/MongodbSourceTest.php b/Test/Case/Datasource/MongodbSourceTest.php index 2d7b9cb..da65c01 100644 --- a/Test/Case/Datasource/MongodbSourceTest.php +++ b/Test/Case/Datasource/MongodbSourceTest.php @@ -143,7 +143,6 @@ public function setUp() { $this->mongodb = ConnectionManager::getDataSource('mongo_test'); $this->mongodb->connect(); - echo $this->getMongodVersion()."\n"; $this->dropData(); $this->MongoArticle = ClassRegistry::init('MongoArticle'); @@ -210,12 +209,15 @@ public function dropData() { ->selectDatabase($this->_config['database']); foreach($db->listCollections() as $collection) { - echo "About to drop ".$collection->getName()." in ".$this->_config['database']."\n"; - $result = $this->mongodb - ->connection - ->selectDatabase($this->_config['database']) - ->selectCollection($collection->getName()) - ->drop(); + $collectionName = $collection->getName(); + // Don't drop system collections. + if (! preg_match("/^system\./", $collectionName)) { + $result = $this->mongodb + ->connection + ->selectDatabase($this->_config['database']) + ->selectCollection($collectionName) + ->drop(); + } } } catch (MongoException $e) { trigger_error($e->getMessage());