Source for file sql_compatible.php

Documentation is available at sql_compatible.php

  1. <?php
  2. /**
  3.  * Sql Compatible.
  4.  *
  5.  * Attach this behavior to be able to query mongo DBs without using mongo specific syntax.
  6.  * If you don't need this behavior don't attach it and save a few cycles
  7.  *
  8.  * PHP version 5
  9.  *
  10.  * Copyright (c) 2010, Andy Dawson
  11.  *
  12.  * Licensed under The MIT License
  13.  * Redistributions of files must retain the above copyright notice.
  14.  *
  15.  * @filesource
  16.  * @copyright     Copyright (c) 2010, Andy Dawson
  17.  * @link          www.ad7six.com
  18.  * @package       mongodb
  19.  * @subpackage    mongodb.models.behaviors
  20.  * @since         v 1.0 (24-May-2010)
  21.  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
  22.  */
  23.  
  24. /**
  25.  * SqlCompatibleBehavior class
  26.  *
  27.  * @uses          ModelBehavior
  28.  * @package       mongodb
  29.  * @subpackage    mongodb.models.behaviors
  30.  */
  31. class SqlCompatibleBehavior extends ModelBehavior {
  32.  
  33. /**
  34.  * name property
  35.  *
  36.  * @var string 'SqlCompatible'
  37.  * @access public
  38.  */
  39.     public $name = 'SqlCompatible';
  40.  
  41. /**
  42.  * Runtime settings
  43.  *
  44.  * Keyed on model alias
  45.  *
  46.  * @var array 
  47.  * @access public
  48.  */
  49.     public $settings = array();
  50.  
  51. /**
  52.  * defaultSettings property
  53.  *
  54.  * @var array 
  55.  * @access protected
  56.  */
  57.     protected $_defaultSettings = array(
  58.         'convertDates' => true,
  59.         'operators' => array(
  60.             '!=' => '$ne',
  61.             '>' => '$gt',
  62.             '>=' => '$gte',
  63.             '<' => '$lt',
  64.             '<=' => '$lte',
  65.             'IN' => '$in',
  66.             'NOT' => '$not',
  67.             'NOT IN' => '$nin'
  68.         )
  69.     );
  70.  
  71. /**
  72.  * setup method
  73.  *
  74.  * Allow overriding the operator map
  75.  *
  76.  * @param mixed $Model 
  77.  * @param array $config array()
  78.  * @return void 
  79.  * @access public
  80.  */
  81.     public function setup(&$Model, $config = array()) {
  82.         $this->settings[$Model->alias] = array_merge($this->_defaultSettings, $config);
  83.     }
  84.  
  85. /**
  86.  * If requested, convert dates from MongoDate objects to standard date strings
  87.  *
  88.  * @param mixed $Model 
  89.  * @param mixed $results 
  90.  * @param mixed $primary 
  91.  * @return void 
  92.  * @access public
  93.  */
  94.     public function afterFind(&$Model, $results, $primary) {
  95.         if ($this->settings[$Model->alias]['convertDates']) {
  96.             $this->convertDates($results);
  97.         }
  98.         return $results;
  99.     }
  100.  
  101. /**
  102.  * beforeFind method
  103.  *
  104.  * If conditions are an array ensure they are mongified
  105.  *
  106.  * @param mixed $Model 
  107.  * @param mixed $query 
  108.  * @return void 
  109.  * @access public
  110.  */
  111.     public function beforeFind(&$Model, $query) {
  112.         if (is_array($query['conditions']) && $this->_translateConditions($Model, $query['conditions'])) {
  113.             return $query;
  114.         }
  115.         return true;
  116.     }
  117.  
  118. /**
  119.  * Convert MongoDate objects to strings for the purpose of view simplicity
  120.  *
  121.  * @param mixed $results 
  122.  * @return void 
  123.  * @access public
  124.  */
  125.     public function convertDates(&$results) {
  126.         if (is_array($results)) {
  127.             foreach($results as &$row) {
  128.                 $this->convertDates($row);
  129.             }
  130.         } elseif (is_a($results, 'MongoDate')) {
  131.             $results = date('Y-M-d h:i:s', $results->sec);
  132.         }
  133.     }
  134.  
  135. /**
  136.  * translateConditions method
  137.  *
  138.  * Loop on conditions and desqlify them
  139.  *
  140.  * @param mixed $Model 
  141.  * @param mixed $conditions 
  142.  * @return void 
  143.  * @access protected
  144.  */
  145.     protected function _translateConditions(&$Model, &$conditions) {
  146.         $return = false;
  147.         foreach($conditions as $key => &$value) {
  148.             $uKey = strtoupper($key);
  149.             if (substr($uKey, -5) === 'NOT IN') {
  150.                 // 'Special' case because it has a space in it, and it's the whole key
  151.                 $conditions[substr($key, 0, -5)]['$nin'] = $value;
  152.                 unset($conditions[$key]);
  153.                 $return = true;
  154.                 continue;
  155.             }
  156.             if ($uKey === 'OR') {
  157.                 unset($conditions[$key]);
  158.                 foreach($value as $key => $part) {
  159.                     $part = array($key => $part);
  160.                     $this->_translateConditions($Model, $part);
  161.                     $conditions['$or'][] = $part;
  162.                 }
  163.                 $return = true;
  164.                 continue;
  165.             }
  166.             if ($key === $Model->primaryKey && is_array($value)) {
  167.                 //_id=>array(1,2,3) pattern, set  $in operator
  168.                 $isMongoOperator = false;
  169.                 foreach($value as $idKey => $idValue) {
  170.                     //check a mongo operator exists
  171.                     if(substr($idKey,0,1) === '$') {
  172.                         $isMongoOperator = true;
  173.                         continue;
  174.                     }
  175.                 }
  176.                 unset($idKey, $idValue);
  177.                 if($isMongoOperator === false) {
  178.                     $conditions[$key] = array('$in' => $value);
  179.                 }
  180.                 $return = true;
  181.                 continue;
  182.             }
  183.             if (substr($uKey, -3) === 'NOT') {
  184.                 // 'Special' case because it's awkward
  185.                 $childKey = key($value);
  186.                 $childValue = current($value);
  187.  
  188.                 if (in_array(substr($childKey, -1), array('>', '<', '='))) {
  189.                     $parts = explode(' ', $childKey);
  190.                     $operator = array_pop($parts);
  191.                     if ($operator = $this->_translateOperator($Model, $operator)) {
  192.                         $childKey = implode(' ', $parts);
  193.                     }
  194.                 } else {
  195.                     $conditions[$childKey]['$nin'] = (array)$childValue;
  196.                     unset($conditions['NOT']);
  197.                     $return = true;
  198.                     continue;
  199.                 }
  200.  
  201.                 $conditions[$childKey]['$not'][$operator] = $childValue;
  202.                 unset($conditions['NOT']);
  203.                 $return = true;
  204.                 continue;
  205.             }
  206.             if (substr($uKey, -5) === ' LIKE') {
  207.                 // 'Special' case because it's awkward
  208.                 if ($value[0] === '%') {
  209.                     $value = substr($value, 1);
  210.                 } else {
  211.                     $value = '^' . $value;
  212.                 }
  213.                 if (substr($value, -1) === '%') {
  214.                     $value = substr($value, 0, -1);
  215.                 } else {
  216.                     $value .= '$';
  217.                 }
  218.                 $value = str_replace('%', '.*', $value);
  219.  
  220.                 $conditions[substr($key, 0, -5)] = new MongoRegex("/$value/i");
  221.                 unset($conditions[$key]);
  222.                 $return = true;
  223.                 continue;
  224.             }
  225.  
  226.             if (!in_array(substr($key, -1), array('>', '<', '='))) {
  227.                 $return = true;
  228.                 continue;
  229.             }
  230.             if (is_numeric($key && is_array($value))) {
  231.                 if ($this->_translateConditions($Model, $value)) {
  232.                     $return = true;
  233.                     continue;
  234.                 }
  235.             }
  236.             $parts = explode(' ', $key);
  237.             $operator = array_pop($parts);
  238.             if ($operator = $this->_translateOperator($Model, $operator)) {
  239.                 $newKey = implode(' ', $parts);
  240.                 $conditions[$newKey][$operator] = $value;
  241.                 unset($conditions[$key]);
  242.                 $return = true;
  243.             }
  244.             if (is_array($value)) {
  245.                 if ($this->_translateConditions($Model, $value)) {
  246.                     $return = true;
  247.                     continue;
  248.                 }
  249.             }
  250.         }
  251.         return $return;
  252.     }
  253.  
  254. /**
  255.  * translateOperator method
  256.  *
  257.  * Use the operator map for the model and return what the db really wants to hear
  258.  *
  259.  * @param mixed $Model 
  260.  * @param mixed $operator 
  261.  * @return string 
  262.  * @access protected
  263.  */
  264.     protected function _translateOperator($Model, $operator) {
  265.         if (!empty($this->settings[$Model->alias]['operators'][$operator])) {
  266.             return $this->settings[$Model->alias]['operators'][$operator];
  267.         }
  268.         return '';
  269.     }
  270. }

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