Completed
Push — 2.0 ( 5c736a...5f5613 )
by Vermeulen
01:39
created

SqlSelect::obtainTableInfos()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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