Blame | Last modification | View Log | RSS feed
<?php/*** 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.** @filesource* @copyright Copyright (c) 2010, Andy Dawson* @link www.ad7six.com* @package mongodb* @subpackage mongodb.models.behaviors* @since v 1.0 (24-May-2010)* @license http://www.opensource.org/licenses/mit-license.php The MIT License*//*** SqlCompatibleBehavior class** @uses ModelBehavior* @package mongodb* @subpackage mongodb.models.behaviors*/class SqlCompatibleBehavior extends ModelBehavior {/*** name property** @var string 'SqlCompatible'* @access public*/public $name = 'SqlCompatible';/*** Runtime settings** Keyed on model alias** @var array* @access public*/public $settings = array();/*** defaultSettings property** @var array* @access protected*/protected $_defaultSettings = array('convertDates' => true,'dateFormat' => 'Y-M-d H:i:s','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($this->settings[$Model->alias]['dateFormat'], $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 string $format* @param mixed $results* @return void* @access protected*/protected function convertDates($format, &$results) {if (is_array($results)) {foreach($results as &$row) {$this->convertDates($format, $row);}} elseif (is_a($results, 'MongoDate')) {$results = date($format, $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, -6) === 'NOT IN') {// 'Special' case because it has a space in it, and it's the whole key$field = trim(substr($key, 0, -6));$conditions[$field]['$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 existsif(substr($idKey,0,1) === '$') {$isMongoOperator = true;continue;}}unset($idKey, $idValue);if($isMongoOperator === false) {$conditions[$key] = array('$in' => $value);}$return = true;continue;}if (is_numeric($key) && is_array($value)) {if ($this->_translateConditions($Model, $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 awkwardif ($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;}$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 '';}}