Dotclear

source: inc/core/class.dc.selectstatement.php @ 3741:0ab84fb44787

Revision 3741:0ab84fb44787, 11.9 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Add some helpers, cope with mixed (single or array) arg for clause fragment methods, add an optional parameter to reset first the corresponding element

Line 
1<?php
2/**
3 * @brief Select query statement builder
4 *
5 * dcSelectStatement is a class used to build select queries
6 *
7 * @package Dotclear
8 * @subpackage Core
9 *
10 * @copyright Bruno Hondelatte & Association Dotclear
11 * @copyright GPL-2.0-only
12 */
13
14/**
15 * Select Statement : small utility to build select queries
16 */
17class dcSelectStatement
18{
19    protected $core;
20    protected $con;
21
22    protected $columns;
23    protected $from;
24    protected $join;
25    protected $where;
26    protected $cond;
27    protected $sql;
28    protected $having;
29    protected $order;
30    protected $group;
31    protected $limit;
32    protected $offset;
33    protected $distinct;
34
35    /**
36     * Class constructor
37     *
38     * @param dcCore    $core   dcCore instance
39     * @param mixed     $from   optional from clause(s)
40     */
41    public function __construct(&$core, $from = null)
42    {
43        $this->core = &$core;
44        $this->con  = &$core->con;
45
46        $this->columns =
47        $this->from    =
48        $this->join    =
49        $this->where   =
50        $this->cond    =
51        $this->sql     =
52        $this->having  =
53        $this->order   =
54        $this->group   =
55        array();
56        $this->limit    = null;
57        $this->offset   = null;
58        $this->distinct = false;
59
60        if ($from !== null) {
61            if (is_array($from)) {
62                $this->froms($from);
63            } else {
64                $this->from($from);
65            }
66        }
67    }
68
69    /**
70     * Magic getter method
71     *
72     * @param      string  $property  The property
73     *
74     * @return     mixed   property value if property exists
75     */
76    public function __get($property)
77    {
78        if (property_exists($this, $property)) {
79            return $this->$property;
80        }
81        trigger_error('Unknown property ' . $property, E_USER_ERROR);
82        return;
83    }
84
85    /**
86     * Magic setter method
87     *
88     * @param      string  $property  The property
89     * @param      mixed   $value     The value
90     *
91     * @return     self
92     */
93    public function __set($property, $value)
94    {
95        if (property_exists($this, $property)) {
96            $this->$property = $value;
97        } else {
98            trigger_error('Unknown property ' . $property, E_USER_ERROR);
99        }
100        return $this;
101    }
102
103    /**
104     * Adds column(s)
105     *
106     * @param mixed     $c      the column(s)
107     * @param boolean   $reset  reset previous column(s) first
108     *
109     * @return dcSelectStatement self instance, enabling to chain calls
110     */
111    public function columns($c, $reset = false)
112    {
113        if ($reset) {
114            $this->columns = array();
115        }
116        if (is_array($c)) {
117            $this->columns = array_merge($this->columns, $c);
118        } else {
119            array_push($this->columns, $c);
120        }
121        return $this;
122    }
123
124    /**
125     * columns() alias
126     *
127     * @param      mixed    $c      the column(s)
128     * @param      boolean  $reset  reset previous column(s) first
129     *
130     * @return dcSelectStatement self instance, enabling to chain calls
131     */
132    public function column($c, $reset = false)
133    {
134        return $this->columns($c, $reset);
135    }
136
137    /**
138     * Adds FROM clause(s)
139     *
140     * @param mixed     $c      the from clause(s)
141     * @param boolean   $reset  reset previous from(s) first
142     *
143     * @return dcSelectStatement self instance, enabling to chain calls
144     */
145    public function from($c, $reset = false)
146    {
147        if ($reset) {
148            $this->from = array();
149        }
150        if (is_array($c)) {
151            $c          = array_map(trim(ltrim($c, ',')), $c); // Cope with legacy code
152            $this->from = array_merge($this->from, $c);
153        } else {
154            $c = trim(ltrim($c, ',')); // Cope with legacy code
155            array_push($this->from, $c);
156        }
157        return $this;
158    }
159
160    /**
161     * Adds JOIN clause(s) (applied on first from item only)
162     *
163     * @param mixed     $c      the join clause(s)
164     * @param boolean   $reset  reset previous join(s) first
165     *
166     * @return dcSelectStatement self instance, enabling to chain calls
167     */
168    public function join($c, $reset = false)
169    {
170        if ($reset) {
171            $this->join = array();
172        }
173        if (is_array($c)) {
174            $this->join = array_merge($this->join, $c);
175        } else {
176            array_push($this->join, $c);
177        }
178        return $this;
179    }
180
181    /**
182     * Adds WHERE clause(s) condition (each will be AND combined in statement)
183     *
184     * @param mixed     $c      the clause(s)
185     * @param boolean   $reset  reset previous where(s) first
186     *
187     * @return dcSelectStatement self instance, enabling to chain calls
188     */
189    public function where($c, $reset = false)
190    {
191        if ($reset) {
192            $this->where = array();
193        }
194        if (is_array($c)) {
195            $this->where = array_merge($this->where, $c);
196        } else {
197            array_push($this->where, $c);
198        }
199        return $this;
200    }
201
202    /**
203     * Adds additional WHERE clause condition(s) (including an operator at beginning)
204     *
205     * @param mixed     $c      the clause(s)
206     * @param boolean   $reset  reset previous condition(s) first
207     *
208     * @return dcSelectStatement self instance, enabling to chain calls
209     */
210    public function cond($c, $reset = false)
211    {
212        if ($reset) {
213            $this->cond = array();
214        }
215        if (is_array($c)) {
216            $this->cond = array_merge($this->cond, $c);
217        } else {
218            array_push($this->cond, $c);
219        }
220        return $this;
221    }
222
223    /**
224     * Adds generic clause(s)
225     *
226     * @param mixed     $c      the clause(s)
227     * @param boolean   $reset  reset previous generic clause(s) first
228     *
229     * @return dcSelectStatement self instance, enabling to chain calls
230     */
231    public function sql($c, $reset = false)
232    {
233        if ($reset) {
234            $this->sql = array();
235        }
236        if (is_array($c)) {
237            $this->sql = array_merge($this->sql, $c);
238        } else {
239            array_push($this->sql, $c);
240        }
241        return $this;
242    }
243
244    /**
245     * Adds HAVING clause(s)
246     *
247     * @param mixed     $c      the clause(s)
248     * @param boolean   $reset  reset previous having(s) first
249     *
250     * @return dcSelectStatement self instance, enabling to chain calls
251     */
252    public function having($c, $reset = false)
253    {
254        if ($reset) {
255            $this->having = array();
256        }
257        if (is_array($c)) {
258            $this->having = array_merge($this->having, $c);
259        } else {
260            array_push($this->having, $c);
261        }
262        return $this;
263    }
264
265    /**
266     * Adds ORDER BY clause(s)
267     *
268     * @param mixed     $c      the clause(s)
269     * @param boolean   $reset  reset previous order(s) first
270     *
271     * @return dcSelectStatement self instance, enabling to chain calls
272     */
273    public function order($c, $reset = false)
274    {
275        if ($reset) {
276            $this->order = array();
277        }
278        if (is_array($c)) {
279            $this->order = array_merge($this->order, $c);
280        } else {
281            array_push($this->order, $c);
282        }
283        return $this;
284    }
285
286    /**
287     * Adds GROUP BY clause(s)
288     *
289     * @param mixed     $c      the clause(s)
290     * @param boolean   $reset  reset previous group(s) first
291     *
292     * @return dcSelectStatement self instance, enabling to chain calls
293     */
294    public function group($c, $reset = false)
295    {
296        if ($reset) {
297            $this->group = array();
298        }
299        if (is_array($c)) {
300            $this->group = array_merge($this->group, $c);
301        } else {
302            array_push($this->group, $c);
303        }
304        return $this;
305    }
306
307    /**
308     * Defines the LIMIT for select
309     *
310     * @param mixed $limit
311     * @return dcSelectStatement self instance, enabling to chain calls
312     */
313    public function limit($limit)
314    {
315        $offset = null;
316        if (is_array($limit)) {
317            // Keep only values
318            $limit = array_values($limit);
319            // If 2 values, [0] -> offset, [1] -> limit
320            // If 1 value, [0] -> limit
321            if (isset($limit[1])) {
322                $offset = $limit[0];
323                $limit  = $limit[1];
324            } else {
325                $limit = limit[0];
326            }
327        }
328        $this->limit = $limit;
329        if ($offset !== null) {
330            $this->offset = $offset;
331        }
332        return $this;
333    }
334
335    /**
336     * Defines the OFFSET for select
337     *
338     * @param integer $offset
339     * @return dcSelectStatement self instance, enabling to chain calls
340     */
341    public function offset($offset)
342    {
343        $this->offset = $offset;
344        return $this;
345    }
346
347    /**
348     * Defines the DISTINCT flag for select
349     *
350     * @param boolean $distinct
351     * @return dcSelectStatement self instance, enabling to chain calls
352     */
353    public function distinct($distinct)
354    {
355        $this->distinct = $distinct;
356        return $this;
357    }
358
359    // Helpers
360
361    /**
362     * Escape an identifier
363     *
364     * @param      string  $identifier  The identifier
365     *
366     * @return     string
367     */
368    public function escape($identifier)
369    {
370        return $this->con->escape($identifier);
371    }
372
373    /**
374     * Return an SQL IN (…) fragment
375     *
376     * @param      mixed  $list   The list
377     *
378     * @return     string
379     */
380    public function in($list)
381    {
382        return $this->con->in($list);
383    }
384
385    /**
386     * Return an SQL formatted date
387     *
388     * @param   string    $field     Field name
389     * @param   string    $pattern   Date format
390     *
391     * @return     string
392     */
393    public function dateFormat($field, $pattern)
394    {
395        return $this->con->dateFormat($field, $pattern);
396    }
397
398    /**
399     * Returns the select statement
400     *
401     * @return string the statement
402     */
403    public function statement()
404    {
405        // Check if source given
406        if (!count($this->from)) {
407            trigger_error(__('SQL SELECT requires a FROM source'), E_USER_ERROR);
408            return '';
409        }
410
411        // Query
412        $query = 'SELECT ' . ($this->distinct ? 'DISTINCT ' : '');
413
414        // Specific column(s) or all (*)
415        if (count($this->columns)) {
416            $query .= join(', ', $this->columns) . ' ';
417        } else {
418            $query .= '* ';
419        }
420
421        // Table(s) and Join(s)
422        $query .= 'FROM ' . $this->from[0] . ' ';
423        $query .= join(' ', $this->join) . ' ';
424        if (count($this->from) > 1) {
425            array_shift($this->from);
426            $query .= ', ' . join(', ', $this->from) . ' '; // All other from(s)
427        }
428
429        // Where clause(s)
430        if (count($this->where)) {
431            $query .= 'WHERE ' . join(' AND ', $this->where) . ' ';
432        }
433
434        // Direct where clause(s)
435        if (count($this->cond)) {
436            if (!count($this->where)) {
437                $query .= 'WHERE 1 '; // Hack to cope with the operator included in top of each condition
438            }
439            $query .= join(' ', $this->cond) . ' ';
440        }
441
442        // Generic clause(s)
443        if (count($this->sql)) {
444            $query .= join(' ', $this->sql) . ' ';
445        }
446
447        // Order by clause (columns or aliases and optionnaly order ASC/DESC)
448        if (count($this->order)) {
449            $query .= 'ORDER BY ' . join(', ', $this->order) . ' ';
450        }
451
452        // Group by clause (columns or aliases)
453        if (count($this->group)) {
454            $query .= 'GROUP BY ' . join(', ', $this->group) . ' ';
455        }
456
457        // Having clause(s)
458        if (count($this->having)) {
459            $query .= 'HAVING ' . join(' AND ', $this->having) . ' ';
460        }
461
462        // Limit clause
463        if ($this->limit !== null) {
464            $query .= 'LIMIT ' . $this->limit . ' ';
465        }
466
467        // Offset clause
468        if ($this->offset !== null) {
469            $query .= 'OFFSET ' . $this->offset . ' ';
470        }
471
472        return $query;
473    }
474}
Note: See TracBrowser for help on using the repository browser.

Sites map