Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14098 anikendra 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
		'dateFormat' => 'Y-M-d H:i:s',
60
		'operators' => array(
61
			'!=' => '$ne',
62
			'>' => '$gt',
63
			'>=' => '$gte',
64
			'<' => '$lt',
65
			'<=' => '$lte',
66
			'IN' => '$in',
67
			'NOT' => '$not',
68
			'NOT IN' => '$nin'
69
		)
70
	);
71
 
72
/**
73
 * setup method
74
 *
75
 * Allow overriding the operator map
76
 *
77
 * @param mixed $Model
78
 * @param array $config array()
79
 * @return void
80
 * @access public
81
 */
82
	public function setup(Model $Model, $config = array()) {
83
		$this->settings[$Model->alias] = array_merge($this->_defaultSettings, $config);
84
	}
85
 
86
/**
87
 * If requested, convert dates from MongoDate objects to standard date strings
88
 *
89
 * @param mixed $Model
90
 * @param mixed $results
91
 * @param mixed $primary
92
 * @return void
93
 * @access public
94
 */
95
	public function afterFind(Model $Model, $results, $primary = false) {
96
		if ($this->settings[$Model->alias]['convertDates']) {
97
			$this->convertDates($this->settings[$Model->alias]['dateFormat'], $results);
98
		}
99
		return $results;
100
	}
101
 
102
/**
103
 * beforeFind method
104
 *
105
 * If conditions are an array ensure they are mongified
106
 *
107
 * @param mixed $Model
108
 * @param mixed $query
109
 * @return void
110
 * @access public
111
 */
112
	public function beforeFind(Model $Model, $query) {
113
		if (is_array($query['order'])) {
114
			$this->_translateOrders($Model, $query['order']);
115
		}
116
		if (is_array($query['conditions']) && $this->_translateConditions($Model, $query['conditions'])) {
117
			return $query;
118
		}
119
		return $query;
120
	}
121
 
122
/**
123
 * Convert MongoDate objects to strings for the purpose of view simplicity
124
 *
125
 * @param string $format
126
 * @param mixed  $results
127
 * @return void
128
 * @access protected
129
 */
130
	protected function convertDates($format, &$results) {
131
		if (is_array($results)) {
132
			foreach($results as &$row) {
133
				$this->convertDates($format, $row);
134
			}
135
		} elseif (is_a($results, 'MongoDate')) {
136
			$results = date($format, $results->sec);
137
		}
138
	}
139
 
140
 
141
/**
142
 * translateOrders method
143
 * change order syntax from SQL style to Mongo style
144
 *
145
 * @param mixed $Model
146
 * @param mixed $orders
147
 * @return void
148
 * @access protected
149
 */
150
	protected function _translateOrders(Model &$Model, &$orders) {
151
		if(!empty($orders[0])) {
152
			foreach($orders[0] as $key => $val) {
153
				if(preg_match('/^(.+) (ASC|DESC)$/i', $val, $match)) {
154
					$orders[0][$match[1]] = $match[2];
155
					unset($orders[0][$key]);
156
				}
157
			}
158
		}
159
	}
160
 
161
 
162
/**
163
 * translateConditions method
164
 *
165
 * Loop on conditions and desqlify them
166
 *
167
 * @param mixed $Model
168
 * @param mixed $conditions
169
 * @return void
170
 * @access protected
171
 */
172
	protected function _translateConditions(Model &$Model, &$conditions) {
173
		$return = false;
174
		foreach($conditions as $key => &$value) {
175
			$uKey = strtoupper($key);
176
			if (substr($uKey, -6) === 'NOT IN') {
177
				// 'Special' case because it has a space in it, and it's the whole key
178
				$field = trim(substr($key, 0, -6));
179
 
180
				$conditions[$field]['$nin'] = $value;
181
				unset($conditions[$key]);
182
				$return = true;
183
				continue;
184
			}
185
			if ($uKey === 'OR') {
186
				unset($conditions[$key]);
187
				foreach($value as $key => $part) {
188
					$part = array($key => $part);
189
					$this->_translateConditions($Model, $part);
190
					$conditions['$or'][] = $part;
191
				}
192
				$return = true;
193
				continue;
194
			}
195
			if ($key === $Model->primaryKey && is_array($value)) {
196
				//_id=>array(1,2,3) pattern, set  $in operator
197
				$isMongoOperator = false;
198
				foreach($value as $idKey => $idValue) {
199
					//check a mongo operator exists
200
					if(substr($idKey,0,1) === '$') {
201
						$isMongoOperator = true;
202
						continue;
203
					}
204
				}
205
				unset($idKey, $idValue);
206
				if($isMongoOperator === false) {
207
					$conditions[$key] = array('$in' => $value);
208
				}
209
				$return = true;
210
				continue;
211
			}
212
			if (is_numeric($key) && is_array($value)) {
213
				if ($this->_translateConditions($Model, $value)) {
214
					$return = true;
215
					continue;
216
				}
217
			}
218
			if (substr($uKey, -3) === 'NOT') {
219
				// 'Special' case because it's awkward
220
				$childKey = key($value);
221
				$childValue = current($value);
222
 
223
				if (in_array(substr($childKey, -1), array('>', '<', '='))) {
224
					$parts = explode(' ', $childKey);
225
					$operator = array_pop($parts);
226
					if ($operator = $this->_translateOperator($Model, $operator)) {
227
						$childKey = implode(' ', $parts);
228
					}
229
				} else {
230
					$conditions[$childKey]['$nin'] = (array)$childValue;
231
					unset($conditions['NOT']);
232
					$return = true;
233
					continue;
234
				}
235
 
236
				$conditions[$childKey]['$not'][$operator] = $childValue;
237
				unset($conditions['NOT']);
238
				$return = true;
239
				continue;
240
			}
241
			if (substr($uKey, -5) === ' LIKE') {
242
				// 'Special' case because it's awkward
243
				if ($value[0] === '%') {
244
					$value = substr($value, 1);
245
				} else {
246
					$value = '^' . $value;
247
				}
248
				if (substr($value, -1) === '%') {
249
					$value = substr($value, 0, -1);
250
				} else {
251
					$value .= '$';
252
				}
253
				$value = str_replace('%', '.*', $value);
254
 
255
				$conditions[substr($key, 0, -5)] = new MongoRegex("/$value/i");
256
				unset($conditions[$key]);
257
				$return = true;
258
				continue;
259
			}
260
			if (!in_array(substr($key, -1), array('>', '<', '='))) {
261
				$return = true;
262
				continue;
263
			}
264
			$parts = explode(' ', $key);
265
			$operator = array_pop($parts);
266
			if ($operator = $this->_translateOperator($Model, $operator)) {
267
				$newKey = implode(' ', $parts);
268
				$conditions[$newKey][$operator] = $value;
269
				unset($conditions[$key]);
270
				$return = true;
271
			}
272
			if (is_array($value)) {
273
				if ($this->_translateConditions($Model, $value)) {
274
					$return = true;
275
					continue;
276
				}
277
			}
278
		}
279
		return $return;
280
	}
281
 
282
/**
283
 * translateOperator method
284
 *
285
 * Use the operator map for the model and return what the db really wants to hear
286
 *
287
 * @param mixed $Model
288
 * @param mixed $operator
289
 * @return string
290
 * @access protected
291
 */
292
	protected function _translateOperator(Model $Model, $operator) {
293
		if (!empty($this->settings[$Model->alias]['operators'][$operator])) {
294
			return $this->settings[$Model->alias]['operators'][$operator];
295
		}
296
		return '';
297
	}
298
}