Bugfix in PlIteratorUtils::getArrayValueCallback
[platal.git] / classes / plfilter.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22 // {{{ class PlLimit
23 class PlLimit
24 {
25 private $count = null;
26 private $from = null;
27
28 public function __construct($count = null, $from = null)
29 {
30 $this->count = $count;
31 $this->from = $from;
32 }
33
34 public function getSql()
35 {
36 if (!is_null($this->count) && $this->count != 0) {
37 if (!is_null($this->from) && $this->from != 0) {
38 return XDB::format('LIMIT {?}, {?}', (int)$this->from, (int)$this->count);
39 } else {
40 return XDB::format('LIMIT {?}', (int)$this->count);
41 }
42 }
43 return '';
44 }
45 }
46 // }}}
47
48 // {{{ class PlSqlJoin
49 class PlSqlJoin
50 {
51 private $mode;
52 private $table;
53 private $condition;
54
55 const MODE_LEFT = 'LEFT';
56 const MODE_RIGHT = 'RIGHT';
57 const MODE_INNER = 'INNER';
58
59 private function __construct($mode, $params)
60 {
61 $table = array_shift($params);
62 $condition = call_user_func_array(array('XDB', 'format'), $params);
63 if ($mode != self::MODE_LEFT && $mode != self::MODE_RIGHT && $mode != self::MODE_INNER) {
64 Platal::page()->kill("Join mode error: unknown mode $mode");
65 return;
66 }
67 $this->mode = $mode;
68 $this->table = $table;
69 $this->condition = $condition;
70 }
71
72 public function mode()
73 {
74 return $this->mode;
75 }
76
77 public function table()
78 {
79 return $this->table;
80 }
81
82 public function condition()
83 {
84 return $this->condition;
85 }
86
87 /** Replace all "metas" in the condition with their translation.
88 * $ME always becomes the alias of the table
89 * @param $key The name the joined table will have in the final query
90 * @param $joinMetas An array of meta => conversion to apply to the condition
91 */
92 public function replaceJoinMetas($key, $joinMetas = array())
93 {
94 $joinMetas['$ME'] = $key;
95 return str_replace(array_keys($joinMetas), $joinMetas, $this->condition);
96 }
97
98 /** Create a join command from an array of PlSqlJoin
99 * @param $joins The list of 'join' to convert into an SQL query
100 * @param $joinMetas An array of ('$META' => 'conversion') to apply to the joins.
101 */
102 public static function formatJoins(array $joins, array $joinMetas)
103 {
104 $str = '';
105 foreach ($joins as $key => $join) {
106 if (!($join instanceof PlSqlJoin)) {
107 Platal::page()->kill("Error: not a join: $join");
108 }
109 $mode = $join->mode();
110 $table = $join->table();
111 $str .= ' ' . $mode . ' JOIN ' . $table . ' AS ' . $key;
112 if ($join->condition() != null) {
113 $str .= ' ON (' . $join->replaceJoinMetas($key, $joinMetas) . ')';
114 }
115 $str .= "\n";
116 }
117 return $str;
118 }
119
120 /** Build a left join
121 * @param table The name of the table.
122 * @param condition The condition of the jointure
123 */
124 public static function left()
125 {
126 $params = func_get_args();
127 return new PlSqlJoin(self::MODE_LEFT, $params);
128 }
129
130 /** Build a right join
131 * @param table The name of the table.
132 * @param condition The condition of the jointure
133 */
134 public static function right()
135 {
136 $params = func_get_args();
137 return new PlSqlJoin(self::MODE_RIGHT, $params);
138 }
139
140 /** Build a inner join
141 * @param table The name of the table.
142 * @param condition The condition of the jointure
143 */
144 public static function inner()
145 {
146 $params = func_get_args();
147 return new PlSqlJoin(self::MODE_INNER, $params);
148 }
149 }
150 // }}}
151
152 // {{{ class PlFilterOrder
153 abstract class PlFilterOrder
154 {
155 protected $desc = false;
156 public function __construct($desc = false)
157 {
158 $this->desc = $desc;
159 }
160
161 public function toggleDesc()
162 {
163 $this->desc = !$desc;
164 }
165
166 public function setDescending($desc = true)
167 {
168 $this->desc = $desc;
169 }
170
171 public function buildSort(PlFilter &$pf)
172 {
173 $sel = $this->getSortTokens($pf);
174 if (!is_array($sel)) {
175 $sel = array($sel);
176 }
177 if ($this->desc) {
178 foreach ($sel as $k => $s) {
179 $sel[$k] = $s . ' DESC';
180 }
181 }
182 return $sel;
183 }
184
185 abstract protected function getSortTokens(PlFilter &$pf);
186 }
187 // }}}
188
189 // {{{ class PFO_Random
190 class PFO_Random extends PlFilterOrder
191 {
192 private $seed = null;
193
194 public function __construct($seed = null, $desc = false)
195 {
196 parent::__construct($desc);
197 $this->seed = $seed;
198 }
199
200 protected function getSortTokens(PlFilter &$pf)
201 {
202 if ($this->seed == null) {
203 return 'RAND()';
204 } else {
205 return XDB::format('RAND({?})', $this->seed);
206 }
207 }
208 }
209 // }}}
210
211 // {{{ interface PlFilterCondition
212 interface PlFilterCondition
213 {
214 const COND_TRUE = 'TRUE';
215 const COND_FALSE = 'FALSE';
216
217 public function buildCondition(PlFilter &$pf);
218 }
219 // }}}
220
221 // {{{ class PFC_OneChild
222 abstract class PFC_OneChild implements PlFilterCondition
223 {
224 protected $child;
225
226 public function __construct(&$child = null)
227 {
228 if (!is_null($child) && ($child instanceof PlFilterCondition)) {
229 $this->setChild($child);
230 }
231 }
232
233 public function setChild(PlFilterCondition &$cond)
234 {
235 $this->child =& $cond;
236 }
237 }
238 // }}}
239
240 // {{{ class PFC_NChildren
241 abstract class PFC_NChildren implements PlFilterCondition
242 {
243 protected $children = array();
244
245 public function __construct()
246 {
247 $children = func_get_args();
248 foreach ($children as &$child) {
249 if (!is_null($child) && ($child instanceof PlFilterCondition)) {
250 $this->addChild($child);
251 }
252 }
253 }
254
255 public function addChild(PlFilterCondition &$cond)
256 {
257 $this->children[] =& $cond;
258 }
259
260 protected function catConds(array $cond, $op, $fallback)
261 {
262 if (count($cond) == 0) {
263 return $fallback;
264 } else if (count($cond) == 1) {
265 return $cond[0];
266 } else {
267 return '(' . implode(') ' . $op . ' (', $cond) . ')';
268 }
269 }
270 }
271 // }}}
272
273 // {{{ class PFC_True
274 class PFC_True implements PlFilterCondition
275 {
276 public function buildCondition(PlFilter &$uf)
277 {
278 return self::COND_TRUE;
279 }
280 }
281 // }}}
282
283 // {{{ class PFC_False
284 class PFC_False implements PlFilterCondition
285 {
286 public function buildCondition(PlFilter &$uf)
287 {
288 return self::COND_FALSE;
289 }
290 }
291 // }}}
292
293 // {{{ class PFC_Not
294 class PFC_Not extends PFC_OneChild
295 {
296 public function buildCondition(PlFilter &$uf)
297 {
298 $val = $this->child->buildCondition($uf);
299 if ($val == self::COND_TRUE) {
300 return self::COND_FALSE;
301 } else if ($val == self::COND_FALSE) {
302 return self::COND_TRUE;
303 } else {
304 return 'NOT (' . $val . ')';
305 }
306 }
307 }
308 // }}}
309
310 // {{{ class PFC_And
311 class PFC_And extends PFC_NChildren
312 {
313 public function buildCondition(PlFilter &$uf)
314 {
315 if (empty($this->children)) {
316 return self::COND_FALSE;
317 } else {
318 $true = self::COND_FALSE;
319 $conds = array();
320 foreach ($this->children as &$child) {
321 $val = $child->buildCondition($uf);
322 if ($val == self::COND_TRUE) {
323 $true = self::COND_TRUE;
324 } else if ($val == self::COND_FALSE) {
325 return self::COND_FALSE;
326 } else {
327 $conds[] = $val;
328 }
329 }
330 return $this->catConds($conds, 'AND', $true);
331 }
332 }
333 }
334 // }}}
335
336 // {{{ class PFC_Or
337 class PFC_Or extends PFC_NChildren
338 {
339 public function buildCondition(PlFilter &$uf)
340 {
341 if (empty($this->children)) {
342 return self::COND_TRUE;
343 } else {
344 $true = self::COND_TRUE;
345 $conds = array();
346 foreach ($this->children as &$child) {
347 $val = $child->buildCondition($uf);
348 if ($val == self::COND_TRUE) {
349 return self::COND_TRUE;
350 } else if ($val == self::COND_FALSE) {
351 $true = self::COND_FALSE;
352 } else {
353 $conds[] = $val;
354 }
355 }
356 return $this->catConds($conds, 'OR', $true);
357 }
358 }
359 }
360 // }}}
361
362 // {{{ class PlFilter
363 abstract class PlFilter
364 {
365 /** Filters objects matching the PlFilter
366 * @param $objects The objects to filter
367 * @param $limit The portion of the matching objects to show
368 */
369 public abstract function filter(array $objects, $limit = null);
370
371 public abstract function setCondition(PlFilterCondition &$cond);
372
373 public abstract function addSort(PlFilterOrder &$sort);
374
375 public abstract function getTotalCount();
376
377 /** Get objects, selecting only those within a limit
378 * @param $limit The portion of the matching objects to select
379 */
380 public abstract function get($limit = null);
381
382 /** PRIVATE FUNCTIONS
383 */
384
385 /** List of metas to replace in joins:
386 * '$COIN' => 'pan.x' means 'replace $COIN with pan.x in the condition of the joins'
387 *
388 * "$ME" => "joined table alias" is always added to these.
389 */
390 protected $joinMetas = array();
391
392 protected $joinMethods = array();
393
394 /** Build the 'join' part of the query
395 * This function will call all methods declared in self::$joinMethods
396 * to get an array of PlSqlJoin objects to merge
397 */
398 protected function buildJoins()
399 {
400 $joins = array();
401 foreach ($this->joinMethods as $method) {
402 $joins = array_merge($joins, $this->$method());
403 }
404 return PlSqlJoin::formatJoins($joins, $this->joinMetas);
405 }
406
407 }
408 // }}}
409
410 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
411 ?>