Makes the $limit argument optional.
[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
59 public function __construct($mode, $table, $condition)
60 {
61 if ($mode != self::MODE_LEFT && $mode != self::MODE_RIGHT && $mode != self::MODE_INNER) {
4aa4899b 62 Platal::page()->kill("Join mode error: unknown mode $mode");
285fb262
RB
63 return;
64 }
65 $this->mode = $mode;
66 $this->table = $table;
67 $this->condition = $condition;
68 }
69
70 public function mode()
71 {
72 return $this->mode;
73 }
74
75 public function table()
76 {
77 return $this->table;
78 }
79
bd9b36fe 80 public function condition()
285fb262
RB
81 {
82 return $this->condition;
83 }
285fb262 84
bd9b36fe
RB
85 /** Replace all "metas" in the condition with their translation.
86 * $ME always becomes the alias of the table
87 * @param $key The name the joined table will have in the final query
88 * @param $joinMetas An array of meta => conversion to apply to the condition
89 */
90 public function replaceJoinMetas($key, $joinMetas = array())
285fb262 91 {
bd9b36fe
RB
92 $joinMetas['$ME'] = $key;
93 return str_replace(array_keys($joinMetas), $joinMetas, $this->condition);
285fb262
RB
94 }
95
bd9b36fe
RB
96 /** Create a join command from an array of PlSqlJoin
97 * @param $joins The list of 'join' to convert into an SQL query
98 * @param $joinMetas An array of ('$META' => 'conversion') to apply to the joins.
99 */
100 public static function formatJoins(array $joins, array $joinMetas)
285fb262
RB
101 {
102 $str = '';
103 foreach ($joins as $key => $join) {
4aa4899b 104 if (!($join instanceof PlSqlJoin)) {
bd9b36fe 105 Platal::page()->kill("Error: not a join: $join");
285fb262
RB
106 }
107 $mode = $join->mode();
108 $table = $join->table();
109 $str .= ' ' . $mode . ' JOIN ' . $table . ' AS ' . $key;
110 if ($join->condition() != null) {
bd9b36fe 111 $str .= ' ON (' . $join->replaceJoinMetas($key, $joinMetas) . ')';
285fb262
RB
112 }
113 $str .= "\n";
114 }
115 return $str;
116 }
bd9b36fe 117}
0d78a96b 118// }}}
bd9b36fe 119
0d78a96b 120// {{{ class PlFilterOrder
f66f26e2
RB
121abstract class PlFilterOrder
122{
123 protected $desc = false;
124 public function __construct($desc = false)
125 {
126 $this->desc = $desc;
127 }
128
129 public function toggleDesc()
130 {
131 $this->desc = !$desc;
132 }
133
134 public function setDescending($desc = true)
135 {
136 $this->desc = $desc;
137 }
138
139 public function buildSort(PlFilter &$pf)
140 {
141 $sel = $this->getSortTokens($pf);
142 if (!is_array($sel)) {
143 $sel = array($sel);
144 }
145 if ($this->desc) {
146 foreach ($sel as $k => $s) {
147 $sel[$k] = $s . ' DESC';
148 }
149 }
150 return $sel;
151 }
152
0d78a96b 153 abstract protected function getSortTokens(PlFilter &$pf);
f66f26e2 154}
0d78a96b
RB
155// }}}
156
157// {{{ class PFO_Random
158class PFO_Random extends PlFilterOrder
159{
160 private $seed = null;
161
162 public function __construct($seed = null, $desc = false)
163 {
164 parent::__construct($desc);
165 $this->seed = $seed;
166 }
167
168 protected function getSortTokens(PlFilter &$pf)
169 {
170 if ($this->seed == null) {
171 return 'RAND()';
172 } else {
173 return XDB::format('RAND({?})', $this->seed);
174 }
175 }
176}
177// }}}
f66f26e2
RB
178
179// {{{ interface PlFilterCondition
180interface PlFilterCondition
181{
182 const COND_TRUE = 'TRUE';
183 const COND_FALSE = 'FALSE';
184
185 public function buildCondition(PlFilter &$pf);
186}
187// }}}
188
189// {{{ class PFC_OneChild
190abstract class PFC_OneChild implements PlFilterCondition
191{
192 protected $child;
193
194 public function __construct(&$child = null)
195 {
196 if (!is_null($child) && ($child instanceof PlFilterCondition)) {
197 $this->setChild($child);
198 }
199 }
200
201 public function setChild(PlFilterCondition &$cond)
202 {
203 $this->child =& $cond;
204 }
205}
206// }}}
207
208// {{{ class PFC_NChildren
209abstract class PFC_NChildren implements PlFilterCondition
210{
211 protected $children = array();
212
213 public function __construct()
214 {
215 $children = func_get_args();
216 foreach ($children as &$child) {
217 if (!is_null($child) && ($child instanceof PlFilterCondition)) {
218 $this->addChild($child);
219 }
220 }
221 }
222
223 public function addChild(PlFilterCondition &$cond)
224 {
225 $this->children[] =& $cond;
226 }
227
228 protected function catConds(array $cond, $op, $fallback)
229 {
230 if (count($cond) == 0) {
231 return $fallback;
232 } else if (count($cond) == 1) {
233 return $cond[0];
234 } else {
235 return '(' . implode(') ' . $op . ' (', $cond) . ')';
236 }
237 }
238}
239// }}}
240
241// {{{ class PFC_True
242class PFC_True implements PlFilterCondition
243{
244 public function buildCondition(PlFilter &$uf)
245 {
246 return self::COND_TRUE;
247 }
248}
249// }}}
250
251// {{{ class PFC_False
252class PFC_False implements PlFilterCondition
253{
254 public function buildCondition(PlFilter &$uf)
255 {
256 return self::COND_FALSE;
257 }
258}
259// }}}
260
261// {{{ class PFC_Not
262class PFC_Not extends PFC_OneChild
263{
264 public function buildCondition(PlFilter &$uf)
265 {
266 $val = $this->child->buildCondition($uf);
267 if ($val == self::COND_TRUE) {
268 return self::COND_FALSE;
269 } else if ($val == self::COND_FALSE) {
270 return self::COND_TRUE;
271 } else {
272 return 'NOT (' . $val . ')';
273 }
274 }
275}
276// }}}
277
278// {{{ class PFC_And
279class PFC_And extends PFC_NChildren
280{
281 public function buildCondition(PlFilter &$uf)
282 {
283 if (empty($this->children)) {
284 return self::COND_FALSE;
285 } else {
286 $true = self::COND_FALSE;
287 $conds = array();
288 foreach ($this->children as &$child) {
289 $val = $child->buildCondition($uf);
290 if ($val == self::COND_TRUE) {
291 $true = self::COND_TRUE;
292 } else if ($val == self::COND_FALSE) {
293 return self::COND_FALSE;
294 } else {
295 $conds[] = $val;
296 }
297 }
298 return $this->catConds($conds, 'AND', $true);
299 }
300 }
301}
302// }}}
303
304// {{{ class PFC_Or
305class PFC_Or extends PFC_NChildren
306{
307 public function buildCondition(PlFilter &$uf)
308 {
309 if (empty($this->children)) {
310 return self::COND_TRUE;
311 } else {
312 $true = self::COND_TRUE;
313 $conds = array();
314 foreach ($this->children as &$child) {
315 $val = $child->buildCondition($uf);
316 if ($val == self::COND_TRUE) {
317 return self::COND_TRUE;
318 } else if ($val == self::COND_FALSE) {
319 $true = self::COND_FALSE;
320 } else {
321 $conds[] = $val;
322 }
323 }
324 return $this->catConds($conds, 'OR', $true);
325 }
326 }
327}
328// }}}
329
0d78a96b 330// {{{ class PlFilter
bd9b36fe
RB
331abstract class PlFilter
332{
333 /** Filters objects matching the PlFilter
334 * @param $objects The objects to filter
335 * @param $limit The portion of the matching objects to show
336 */
e1746810 337 public abstract function filter(array $objects, $limit = null);
bd9b36fe
RB
338
339 public abstract function setCondition(PlFilterCondition &$cond);
340
341 public abstract function addSort(PlFilterOrder &$sort);
342
343 public abstract function getTotalCount();
344
345 /** Get objects, selecting only those within a limit
346 * @param $limit The portion of the matching objects to select
347 */
e1746810 348 public abstract function get($limit = null);
bd9b36fe
RB
349
350 /** PRIVATE FUNCTIONS
351 */
352
353 /** List of metas to replace in joins:
354 * '$COIN' => 'pan.x' means 'replace $COIN with pan.x in the condition of the joins'
355 *
356 * "$ME" => "joined table alias" is always added to these.
357 */
9a122520
RB
358 protected $joinMetas = array();
359
360 protected $joinMethods = array();
285fb262 361
bd9b36fe
RB
362 /** Build the 'join' part of the query
363 * This function will call all methods declared in self::$joinMethods
364 * to get an array of PlSqlJoin objects to merge
365 */
9a122520 366 protected function buildJoins()
285fb262
RB
367 {
368 $joins = array();
9a122520 369 foreach ($this->joinMethods as $method) {
285fb262
RB
370 $joins = array_merge($joins, $this->$method());
371 }
9a122520 372 return PlSqlJoin::formatJoins($joins, $this->joinMetas);
285fb262
RB
373 }
374
375}
0d78a96b 376// }}}
285fb262
RB
377
378?>