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 (substr ($ uKey , -3 ) === 'NOT ' ) {
167
+ // 'Special' case because it's awkward
168
+ $ childKey = key ($ value );
169
+ $ childValue = current ($ value );
170
+
171
+ if (in_array (substr ($ childKey , -1 ), array ('> ' , '< ' , '= ' ))) {
172
+ $ parts = explode (' ' , $ childKey );
173
+ $ operator = array_pop ($ parts );
174
+ if ($ operator = $ this ->_translateOperator ($ Model , $ operator )) {
175
+ $ childKey = implode (' ' , $ parts );
176
+ }
177
+ } else {
178
+ $ conditions [$ childKey ]['$nin ' ] = (array )$ childValue ;
179
+ unset($ conditions ['NOT ' ]);
180
+ $ return = true ;
181
+ continue ;
182
+ }
183
+
184
+ $ conditions [$ childKey ]['$not ' ][$ operator ] = $ childValue ;
185
+ unset($ conditions ['NOT ' ]);
186
+ $ return = true ;
187
+ continue ;
188
+ }
189
+ if (substr ($ uKey , -5 ) === ' LIKE ' ) {
190
+ // 'Special' case because it's awkward
191
+ if ($ value [0 ] === '% ' ) {
192
+ $ value = substr ($ value , 1 );
193
+ } else {
194
+ $ value = '^ ' . $ value ;
195
+ }
196
+ if (substr ($ value , -1 ) === '% ' ) {
197
+ $ value = substr ($ value , 0 , -1 );
198
+ } else {
199
+ $ value .= '$ ' ;
200
+ }
201
+ $ value = str_replace ('% ' , '.* ' , $ value );
202
+
203
+ $ conditions [substr ($ key , 0 , -5 )] = new MongoRegex ("/ $ value/i " );
204
+ unset($ conditions [$ key ]);
205
+ $ return = true ;
206
+ continue ;
207
+ }
208
+
209
+ if (!in_array (substr ($ key , -1 ), array ('> ' , '< ' , '= ' ))) {
210
+ $ return = true ;
211
+ continue ;
212
+ }
213
+ if (is_numeric ($ key && is_array ($ value ))) {
214
+ if ($ this ->_translateConditions ($ Model , $ value )) {
215
+ $ return = true ;
216
+ continue ;
217
+ }
218
+ }
219
+ $ parts = explode (' ' , $ key );
220
+ $ operator = array_pop ($ parts );
221
+ if ($ operator = $ this ->_translateOperator ($ Model , $ operator )) {
222
+ $ newKey = implode (' ' , $ parts );
223
+ $ conditions [$ newKey ][$ operator ] = $ value ;
224
+ unset($ conditions [$ key ]);
225
+ $ return = true ;
226
+ }
227
+ if (is_array ($ value )) {
228
+ if ($ this ->_translateConditions ($ Model , $ value )) {
229
+ $ return = true ;
230
+ continue ;
231
+ }
232
+ }
233
+ }
234
+ return $ return ;
235
+ }
236
+
237
+ /**
238
+ * translateOperator method
239
+ *
240
+ * Use the operator map for the model and return what the db really wants to hear
241
+ *
242
+ * @param mixed $Model
243
+ * @param mixed $operator
244
+ * @return string
245
+ * @access protected
246
+ */
247
+ protected function _translateOperator ($ Model , $ operator ) {
248
+ if (!empty ($ this ->settings [$ Model ->alias ]['operators ' ][$ operator ])) {
249
+ return $ this ->settings [$ Model ->alias ]['operators ' ][$ operator ];
250
+ }
251
+ return '' ;
252
+ }
253
+ }
0 commit comments