Completed
Push — 2.0 ( c11079...9c6898 )
by Vermeulen
02:10
created

SqlSelect::addColumnsForSelect()   C

Complexity

Conditions 7
Paths 14

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 34
rs 6.7272
c 1
b 0
f 0
cc 7
eloc 18
nc 14
nop 2
1
<?php
2
3
namespace BfwSql;
4
5
use \Exception;
6
use \PDO;
7
8
/**
9
 * Class to write SELECT queries
10
 * 
11
 * @package bfw-sql
12
 * @author Vermeulen Maxime <[email protected]>
13
 * @version 2.0
14
 */
15
class SqlSelect extends SqlActions
16
{
17
    /**
18
     * @var string $returnType PHP Type used for return result
19
     */
20
    protected $returnType = '';
21
    
22
    /**
23
     * @var object $mainTable Informations about main table. Used for FROM part
24
     */
25
    protected $mainTable;
26
    
27
    /**
28
     * @var array $subQueries All sub-queries
29
     */
30
    protected $subQueries = [];
31
    
32
    /**
33
     * @var array $join List of all INNER JOIN
34
     */
35
    protected $join = [];
36
    
37
    /**
38
     * @var array $joinLeft List of all LEFT JOIN
39
     */
40
    protected $joinLeft = [];
41
    
42
    /**
43
     * @var array $joinRight List of all RIGHT JOIN
44
     */
45
    protected $joinRight = [];
46
    
47
    /**
48
     * @var string[] $order All columns used for ORDER BY part
49
     */
50
    protected $order = [];
51
    
52
    /**
53
     * @var string $limit The LIMIT part
54
     */
55
    protected $limit = '';
56
    
57
    /**
58
     * @var string[] $group The GROUP BY part
59
     */
60
    protected $group = [];
61
    
62
    /**
63
     * Constructor
64
     * 
65
     * @param \BfwSql\SqlConnect $sqlConnect Instance of SGBD connexion
66
     * @param string             $returnType PHP type used for return result
67
     */
68
    public function __construct(SqlConnect $sqlConnect, $returnType)
69
    {
70
        parent::__construct($sqlConnect);
71
        $this->returnType = $returnType;
72
    }
73
    
74
    /**
75
     * Define object used to save informations about a table
76
     * This object will be used to write query
77
     * 
78
     * @param string|array $table Tables informations
79
     * 
80
     * @return \stdClass
81
     */
82
    protected function obtainTableInfos($table)
83
    {
84
        if (!is_array($table) && !is_string($table)) {
85
            throw new Exception('Table information is not in the right format.');
86
        }
87
        
88
        if (is_array($table)) {
89
            $tableName = reset($table);
90
            $shortcut  = key($table);
91
        } else {
92
            $tableName = $table;
93
            $shortcut  = null;
94
        }
95
        
96
        $prefix = $this->sqlConnect->getConnectionInfos()->tablePrefix;
97
        
98
        return (object) [
99
            'tableName' => $prefix.$tableName,
100
            'shortcut'  => $shortcut
101
        ];
102
    }
103
    
104
    /**
105
     * Add columns for select
106
     * 
107
     * @param array|string $columns   Columns to add
108
     * @param string       $tableName Table name for will be columns added
109
     * 
110
     * @return void
111
     */
112
    protected function addColumnsForSelect($columns, $tableName)
113
    {
114
        if (!is_array($columns)) {
115
            $columns = (array) $columns;
116
        }
117
        
118
        foreach ($columns as $columnShortcut => $columnName) {
119
            //If value is a sql function or keyword, not add quote
120
            if (
121
                strpos($columnName, ' ') === false
122
                && strpos($columnName, '(') === false
123
            ) {
124
                //Add quote only if a column has been declared
125
                if ($columnName !== '*') {
126
                    $columnName = '`'.$columnName.'`';
127
                }
128
                
129
                $columnName = '`'.$tableName.'`.'.$columnName;
130
            }
131
            
132
            //If a column shortcut is declared
133
            if (is_string($columnShortcut)) {
134
                $this->columns[] = (object) [
135
                    'column'   => $columnName,
136
                    'shortcut' => $columnShortcut
137
                ];
138
            } else {
139
                $this->columns[] = (object) [
140
                    'column'   => $columnName,
141
                    'shortcut' => null
142
                ];
143
            }
144
        }
145
    }
146
    
147
    /**
148
     * Declare information for FROM part and column will be get for main table
149
     * 
150
     * @param string|array $table   Table name.
151
     *  It can be an array if a table shortcut is declared.
152
     *  In array mode, the format is ['asValue' => 'tableName']
153
     * @param string|array $columns (default: "*") Columns will be get for
154
     *  the table declared in first argument
155
     * 
156
     * @return \BfwSql\SqlSelect
157
     */
158
    public function from($table, $columns = '*')
159
    {
160
        $this->mainTable = $this->obtainTableInfos($table);
161
        
162
        $tableName = $this->mainTable->tableName;
163
        if ($this->mainTable->shortcut !== null) {
164
            $tableName = $this->mainTable->shortcut;
165
        }
166
        
167
        $this->addColumnsForSelect($columns, $tableName);
168
        
169
        return $this;
170
    }
171
    
172
    /**
173
     * Add a sub-query in the SELECT part on the request
174
     * 
175
     * @param \BfwSql\SqlActions|string $subRequest The sub-request
176
     * @param string                    $shortcut   The shortcut to use for
177
     *  this query in SELECT part
178
     * 
179
     * @return \BfwSql\SqlSelect
180
     */
181
    public function subQuery($subRequest, $shortcut)
182
    {
183
        if (
184
            is_object($subRequest)
185
            && $subRequest instanceof \BfwSql\SqlActions
186
        ) {
187
            $subQuery = $subRequest->assemble();
188
        } elseif (is_string($subRequest)) {
189
            $subQuery = $subRequest;
190
        } else {
191
            throw new Exception(
192
                'subRequest passed in parameters must be an instance of '
193
                .'BfwSql System or a string.'
194
            );
195
        }
196
        
197
        $this->subQueries[] = (object) [
198
            'query'    => $subQuery,
199
            'shortcut' => $shortcut
200
        ];
201
        
202
        return $this;
203
    }
204
    
205
    /**
206
     * Add a (inner|left|right) join to the request
207
     * 
208
     * @param string       $joinPropertyName The name of the property in this
209
     *  class where the join is add
210
     * @param string|array $table            Name of the table concerned by
211
     * the join. Or an array with the table shortcut in key.
212
     * @param string       $joinOn           SQL part "ON" for this join
213
     * @param string|array $joinColumns      Columns from the table joined to
214
     *  add in the SELECT part of the request
215
     * 
216
     * @return \BfwSql\SqlSelect
217
     */
218
    protected function createJoin(
219
        $joinPropertyName,
220
        $table,
221
        $joinOn,
222
        $joinColumns
223
    ) {
224
        $tableInfos     = $this->obtainTableInfos($table);
225
        $tableInfos->on = $joinOn;
226
        
227
        $tableName = $tableInfos->tableName;
228
        if ($tableInfos->shortcut !== null) {
229
            $tableName = $tableInfos->shortcut;
230
        }
231
        
232
        $this->{$joinPropertyName}[] = $tableInfos;
233
        $this->addColumnsForSelect($joinColumns, $tableName);
234
        
235
        return $this;
236
    }
237
    
238
    /**
239
     * Add a INNER JOIN to the request
240
     * 
241
     * @param string|array $table       Name of the table concerned by the
242
     *  join. Or an array with the table shortcut in key.
243
     * @param string       $joinOn      SQL part "ON" for this join
244
     * @param string|array $joinColumns Columns from the table joined to add
245
     *  in the SELECT part of the request
246
     * 
247
     * @return \BfwSql\SqlSelect
248
     */
249
    public function join($table, $joinOn, $joinColumns = '*')
250
    {
251
        return $this->createJoin('join', $table, $joinOn, $joinColumns);
252
    }
253
    
254
    /**
255
     * Add a LEFT JOIN to the request
256
     * 
257
     * @param string|array $table       Name of the table concerned by the
258
     *  join. Or an array with the table shortcut in key.
259
     * @param string       $joinOn      SQL part "ON" for this join
260
     * @param string|array $joinColumns Columns from the table joined to add
261
     *  in the SELECT part of the request
262
     * 
263
     * @return \BfwSql\SqlSelect
264
     */
265
    public function joinLeft($table, $joinOn, $joinColumns = '*')
266
    {
267
        return $this->createJoin('joinLeft', $table, $joinOn, $joinColumns);
268
    }
269
    
270
    /**
271
     * Add a RIGHT JOIN to the request
272
     * 
273
     * @param string|array $table       Name of the table concerned by the
274
     *  join. Or an array with the table shortcut in key.
275
     * @param string       $joinOn      SQL part "ON" for this join
276
     * @param string|array $joinColumns Columns from the table joined to add
277
     *  in the SELECT part of the request
278
     * 
279
     * @return \BfwSql\SqlSelect
280
     */
281
    public function joinRight($table, $joinOn, $joinColumns = '*')
282
    {
283
        return $this->createJoin('joinRight', $table, $joinOn, $joinColumns);
284
    }
285
    
286
    /**
287
     * Add a order condition to the request for the ORDER BY part
288
     * 
289
     * @param string $condition The new condition
290
     * 
291
     * @return \BfwSql\SqlSelect
292
     */
293
    public function order($condition)
294
    {
295
        $this->order[] = (string) $condition;
296
        return $this;
297
    }
298
    
299
    /**
300
     * Add information about the LIMIT part in request
301
     * 
302
     * @param array|integer $limit If it's a integer, the number of row to
303
     *  return. If an array, the format is [numberToStart, numberOfRowToReturn]
304
     * 
305
     * @return \BfwSql\SqlSelect
306
     */
307
    public function limit($limit)
308
    {
309
        if (!is_array($limit)) {
310
            $limit = (array) $limit;
311
        }
312
        
313
        if (isset($limit[1])) {
314
            $this->limit = $limit[0].', '.$limit[1];
315
        } else {
316
            $this->limit = (string) $limit[0];
317
        }
318
        
319
        return $this;
320
    }
321
    
322
    /**
323
     * Add a GROUP BY part to the request
324
     * 
325
     * @param string $condition The condition to use in GROUP BY
326
     * 
327
     * @return \BfwSql\SqlSelect
328
     */
329
    public function group($condition)
330
    {
331
        $this->group[] = $condition;
332
        return $this;
333
    }
334
    
335
    /**
336
     * Return the PDO constant for the returnType declared
337
     * 
338
     * @return integer
339
     */
340
    protected function obtainPdoFetchType()
341
    {
342
        if ($this->returnType === 'object') {
343
            return PDO::FETCH_OBJ;
344
        }
345
        
346
        return PDO::FETCH_ASSOC;
347
    }
348
    
349
    /**
350
     * Fetch one row of the result
351
     * 
352
     * @return mixed
353
     */
354
    public function fetchRow()
355
    {
356
        $req = $this->execute();
357
        return $req->fetch($this->obtainPdoFetchType());
358
    }
359
    
360
    /**
361
     * Fetch all rows returned by the request
362
     * 
363
     * @return generator
364
     */
365
    public function fetchAll()
366
    {
367
        $request = $this->execute(); //throw an Exception if error
368
        
369
        while ($row = $request->fetch($this->obtainPdoFetchType())) {
370
            yield $row;
371
        }
372
    }
373
    
374
    /**
375
     * {@inheritdoc}
376
     */
377
    protected function assembleRequest()
378
    {
379
        $this->assembledRequest = 'SELECT '.$this->generateSelect()
380
            .' FROM '.$this->generateFrom()
381
            .$this->generateJoin('join')
382
            .$this->generateJoin('joinLeft')
383
            .$this->generateJoin('joinRight')
384
            .$this->generateWhere()
385
            .$this->generateGroupBy()
386
            .$this->generateOrderBy()
387
            .$this->generateLimit();
388
        
389
        $this->callObserver();
390
    }
391
    
392
    /**
393
     * Write the SELECT part of the request
394
     * 
395
     * @return string
396
     */
397
    protected function generateSelect()
398
    {
399
        $select = '';
400
        foreach ($this->columns as $columnInfos) {
401
            if ($select != '') {
402
                $select .= ', ';
403
            }
404
            
405
            $select .= $columnInfos->column;
406
            if ($columnInfos->shortcut !== null) {
407
                $select .= ' AS `'.$columnInfos->shortcut.'`';
408
            }
409
        }
410
        
411
        foreach ($this->subQueries as $subQueryInfos) {
412
            if ($select != '') {
413
                $select .= ', ';
414
            }
415
416
            $select .= '('.$subQueryInfos->query.')'
417
                .' AS `'.$subQueryInfos->shortcut.'`';
418
        }
419
        
420
        return $select;
421
    }
422
    
423
    /**
424
     * Write the FROM part of the request
425
     * 
426
     * @return string
427
     */
428
    protected function generateFrom()
429
    {
430
        $from = '`'.$this->mainTable->tableName.'`';
431
        
432
        if ($this->mainTable->shortcut !== null) {
433
            $from .= ' AS `'.$this->mainTable->shortcut.'`';
434
        }
435
        
436
        return $from;
437
    }
438
    
439
    /**
440
     * Write a (inner|left|right) join in the request
441
     * 
442
     * @param string $joinProperty The join property name
443
     * 
444
     * @return string
445
     */
446
    protected function generateJoin($joinProperty)
447
    {
448
        $join = '';
449
        if (count($this->{$joinProperty}) === 0) {
450
            return $join;
451
        }
452
        
453
        if ($joinProperty == 'join') {
454
            $joinSqlName = ' INNER JOIN ';
455
        } elseif ($joinProperty == 'joinLeft') {
456
            $joinSqlName = ' LEFT JOIN ';
457
        } elseif ($joinProperty == 'joinRight') {
458
            $joinSqlName = ' RIGHT JOIN ';
459
        }
460
461
        foreach ($this->{$joinProperty} as $joinInfos) {
462
            $join .= $joinSqlName.'`'.$joinInfos->tableName.'`';
463
            if ($joinInfos->shortcut !== null) {
464
                $join .= ' AS `'.$joinInfos->shortcut.'`';
465
            }
466
467
            $join .= ' ON '.$joinInfos->on;
468
        }
469
        
470
        return $join;
471
    }
472
    
473
    /**
474
     * Write the ORDER BY part for the request
475
     * 
476
     * @return string
477
     */
478
    protected function generateOrderBy()
479
    {
480
        if (count($this->order) === 0) {
481
            return '';
482
        }
483
        
484
        $order = ' ORDER BY ';
485
        foreach ($this->order as $orderCondition) {
486
            if ($order != ' ORDER BY ') {
487
                $order .= ', ';
488
            }
489
            
490
            $order .= $orderCondition;
491
        }
492
        
493
        return $order;
494
    }
495
    
496
    /**
497
     * Write the GRUOP BY part for the request
498
     * 
499
     * @return string
500
     */
501
    protected function generateGroupBy()
502
    {
503
        if (count($this->group) === 0) {
504
            return '';
505
        }
506
        
507
        $group = ' GROUP BY ';
508
        foreach ($this->group as $groupCondition) {
509
            if ($group != ' GROUP BY ') {
510
                $group .= ', ';
511
            }
512
            
513
            $group .= $groupCondition;
514
        }
515
        
516
        return $group;
517
    }
518
    
519
    /**
520
     * Write the LIMIT part for the request
521
     * 
522
     * @return string
523
     */
524
    protected function generateLimit()
525
    {
526
        $limit = '';
527
        if ($this->limit !== '') {
528
            $limit = ' LIMIT '.$this->limit;
529
        }
530
        
531
        return $limit;
532
    }
533
} 
534