Simplify (and fix) PlFilterIterator.
[platal.git] / classes / plfilter.php
CommitLineData
285fb262
RB
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
0d78a96b 22// {{{ class PlLimit
bd9b36fe
RB
23class 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 {
4aa4899b 36 if (!is_null($this->count) && $this->count != 0) {
bd9b36fe
RB
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}
0d78a96b 46// }}}
bd9b36fe 47
0d78a96b 48// {{{ class PlSqlJoin
285fb262
RB
49class 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
ae3effcc 59 private function __construct($mode, $params)
285fb262 60 {
ae3effcc
FB
61 $table = array_shift($params);
62 $condition = call_user_func_array(array('XDB', 'format'), $params);
285fb262 63 if ($mode != self::MODE_LEFT && $mode != self::MODE_RIGHT && $mode != self::MODE_INNER) {
4aa4899b 64 Platal::page()->kill("Join mode error: unknown mode $mode");
285fb262
RB
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
bd9b36fe 82 public function condition()
285fb262
RB
83 {
84 return $this->condition;
85 }
285fb262 86
bd9b36fe
RB
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())
285fb262 93 {
bd9b36fe
RB
94 $joinMetas['$ME'] = $key;
95 return str_replace(array_keys($joinMetas), $joinMetas, $this->condition);
285fb262
RB
96 }
97
bd9b36fe
RB
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)
285fb262
RB
103 {
104 $str = '';
105 foreach ($joins as $key => $join) {
4aa4899b 106 if (!($join instanceof PlSqlJoin)) {
bd9b36fe 107 Platal::page()->kill("Error: not a join: $join");
285fb262
RB
108 }
109 $mode = $join->mode();
110 $table = $join->table();
111 $str .= ' ' . $mode . ' JOIN ' . $table . ' AS ' . $key;
112 if ($join->condition() != null) {
bd9b36fe 113 $str .= ' ON (' . $join->replaceJoinMetas($key, $joinMetas) . ')';
285fb262
RB
114 }
115 $str .= "\n";
116 }
117 return $str;
118 }
ae3effcc
FB
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 }
bd9b36fe 149}
0d78a96b 150// }}}
bd9b36fe 151
0d78a96b 152// {{{ class PlFilterOrder
f66f26e2
RB
153abstract 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
0d78a96b 185 abstract protected function getSortTokens(PlFilter &$pf);
f66f26e2 186}
0d78a96b
RB
187// }}}
188
189// {{{ class PFO_Random
190class 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// }}}
f66f26e2
RB
210
211// {{{ interface PlFilterCondition
212interface 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
222abstract 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
241abstract 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
274class 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
284class 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
294class 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
311class 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
337class 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
0d78a96b 362// {{{ class PlFilter
bd9b36fe
RB
363abstract 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 */
e1746810 369 public abstract function filter(array $objects, $limit = null);
bd9b36fe
RB
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 */
e1746810 380 public abstract function get($limit = null);
bd9b36fe
RB
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 */
9a122520
RB
390 protected $joinMetas = array();
391
392 protected $joinMethods = array();
285fb262 393
bd9b36fe
RB
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 */
9a122520 398 protected function buildJoins()
285fb262
RB
399 {
400 $joins = array();
9a122520 401 foreach ($this->joinMethods as $method) {
285fb262
RB
402 $joins = array_merge($joins, $this->$method());
403 }
9a122520 404 return PlSqlJoin::formatJoins($joins, $this->joinMetas);
285fb262
RB
405 }
406
407}
0d78a96b 408// }}}
285fb262 409
ae3effcc 410// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
285fb262 411?>