Completed
Push — 2.0 ( 2cf4f4...5af7db )
by Vermeulen
02:23
created

SqlActions::addNotQuotedColumns()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 22
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 22
loc 22
rs 8.6737
cc 5
eloc 12
nc 4
nop 1
1
<?php
2
3
namespace BfwSql;
4
5
use \Exception;
6
7
/**
8
 * Abstract class used for all query writer class.
9
 * 
10
 * @package bfw-sql
11
 * @author Vermeulen Maxime <[email protected]>
12
 * @version 2.0
13
 */
14
abstract class SqlActions
15
{
16
    /**
17
     * @const QUOTE_ALL To automatic quote all string values.
18
     * Used by SqlInsert and SqlUpdate.
19
     */
20
    const QUOTE_ALL = 'all';
21
    
22
    /**
23
     * @const QUOTE_ALL To not automatic quote string values.
24
     * Used by SqlInsert and SqlUpdate.
25
     */
26
    const QUOTE_NONE = 'none';
27
    
28
    /**
29
     * @const QUOTE_ALL To automatic quote string values only for somes columns
30
     * Used by SqlInsert and SqlUpdate.
31
     */
32
    const QUOTE_PARTIALLY = 'partially';
33
    
34
    /**
35
     * @var \BfwSql\SqlConnect $sqlConnect SqlConnect object
36
     */
37
    protected $sqlConnect;
38
    
39
    /**
40
     * @var string $assembledRequest The request will be executed
41
     */
42
    protected $assembledRequest = '';
43
    
44
    /**
45
     * @var boolean $isPreparedRequest If is a prepared request
46
     */
47
    protected $isPreparedRequest = true;
48
    
49
    /**
50
     * @var string $tableName The main table name for request
51
     */
52
    protected $tableName = '';
53
    
54
    /**
55
     * @var array $columns List of impacted columns by the request
56
     */
57
    protected $columns = array();
58
    
59
    /**
60
     * @var string $quoteStatus The current automic quote status.
61
     */
62
    protected $quoteStatus = self::QUOTE_ALL;
63
    
64
    /**
65
     * @var array $quotedColumns List of columns where value will be quoted if
66
     * is string.
67
     */
68
    protected $quotedColumns = [];
69
    
70
    /**
71
     * @var array $notQuotedColumns List of columns where value will not be
72
     * quoted if is string.
73
     */
74
    protected $notQuotedColumns = [];
75
    
76
    /**
77
     * @var string[] $where All filter use in where part of the request
78
     */
79
    protected $where = array();
80
    
81
    /**
82
     * @var string[] $preparedRequestArgs Arguments used by prepared request
83
     */
84
    protected $preparedRequestArgs = array();
85
    
86
    /**
87
     * @var array $prepareDriversOptions SGBD driver option used for
88
     *  prepared request
89
     * 
90
     * @link http://php.net/manual/en/pdo.prepare.php
91
     */
92
    protected $prepareDriversOptions = array();
93
    
94
    /**
95
     * @var boolean $noResult If request has sent no result.
96
     */
97
    protected $noResult = false;
98
    
99
    /**
100
     * @var \PDOStatement $lastRequestStatement The PDOStatement pour the
101
     *  last request executed.
102
     */
103
    protected $lastRequestStatement;
104
    
105
    /**
106
     * Constructor
107
     * 
108
     * @param \BfwSql\SqlConnect $sqlConnect Instance of SGBD connexion
109
     */
110
    public function __construct(\BfwSql\SqlConnect $sqlConnect)
111
    {
112
        $this->sqlConnect = $sqlConnect;
113
    }
114
    
115
    /**
116
     * Getter to access at sqlConnect property
117
     * 
118
     * @return \BfwSql\SqlConnect
119
     */
120
    public function getSqlConnect()
121
    {
122
        return $this->sqlConnect;
123
    }
124
    
125
    /**
126
     * Setter to enable or disable prepared request
127
     * 
128
     * @param boolean $preparedRequestStatus The new status for prepared request
129
     * 
130
     * @return \BfwSql\SqlActions
131
     */
132
    public function setIsPreparedRequest($preparedRequestStatus)
133
    {
134
        $this->isPreparedRequest = (bool) $preparedRequestStatus;
135
        
136
        return $this;
137
    }
138
    
139
    /**
140
     * Getter to access at preparedRequestArgs property
141
     * 
142
     * @return array
143
     */
144
    public function getPreparedRequestArgs()
145
    {
146
        return $this->preparedRequestArgs;
147
    }
148
    
149
    /**
150
     * Getter to access at prepareDriversOptions property
151
     * 
152
     * @return array
153
     */
154
    public function getPrepareDriversOptions()
155
    {
156
        return $this->prepareDriversOptions;
157
    }
158
    
159
    /**
160
     * Define driver options to prepared request
161
     * 
162
     * @link http://php.net/manual/fr/pdo.prepare.php
163
     * 
164
     * @param array $driverOptions Drivers options
165
     * 
166
     * @return \BfwSql\SqlActions
167
     */
168
    public function setPrepareDriversOptions($driverOptions)
169
    {
170
        $this->prepareDriversOptions = $driverOptions;
171
        
172
        return $this;
173
    }
174
    
175
    /**
176
     * Check if a request is assemble or not.
177
     * If not, run the method assembleRequest.
178
     * 
179
     * @return boolean
180
     */
181
    public function isAssembled()
182
    {
183
        if ($this->assembledRequest === '') {
184
            return false;
185
        }
186
        
187
        return true;
188
    }
189
    
190
    /**
191
     * Write the query
192
     * 
193
     * @return void
194
     */
195
    protected abstract function assembleRequest();
196
    
197
    /**
198
     * Return the assembled request
199
     * 
200
     * @param boolean $force : Force to re-assemble request
201
     * 
202
     * @return string
203
     */
204
    public function assemble($force = false)
205
    {
206
        if ($this->isAssembled() === false || $force === true) {
207
            $this->assembleRequest();
208
        }
209
        
210
        return $this->assembledRequest;
211
    }
212
    
213
    /**
214
     * Execute the assembled request
215
     * 
216
     * @return array The pdo errorInfo array
217
     */
218
    protected function executeQuery()
219
    {
220
        $pdo = $this->sqlConnect->getPDO();
221
        $this->sqlConnect->upNbQuery();
222
        $this->assemble();
223
        
224
        if ($this->isPreparedRequest) {
225
            $req = $pdo->prepare(
226
                $this->assembledRequest,
227
                $this->prepareDriversOptions
228
            );
229
            
230
            $req->execute($this->preparedRequestArgs);
231
            $error = $req->errorInfo();
232
        } else {
233
            $pdoMethodToCall = 'exec';
234
            if ($this instanceof \BfwSql\SqlSelect) {
235
                $pdoMethodToCall = 'query';
236
            }
237
            
238
            $req   = $pdo->{$pdoMethodToCall}($this->assembledRequest);
239
            $error = $pdo->errorInfo();
240
        }
241
        
242
        $this->lastRequestStatement = $req;
243
        
244
        return $error;
245
    }
246
    
247
    /**
248
     * Execute the assembled request and check if there are errors
249
     * Update property noResult
250
     * 
251
     * @throws \Exception If the request fail
252
     * 
253
     * @return \PDOStatement|integer
254
     */
255
    public function execute()
256
    {
257
        $error = $this->executeQuery();
258
        
259
        //Throw an exception if they are an error with the request
260
        if ($error[0] !== null && $error[0] !== '00000') {
261
            throw new Exception($error[2]);
262
        }
263
        
264
        if ($this->lastRequestStatement === false) {
265
            throw new Exception(
266
                'An error occurred during the execution of the request'
267
            );
268
        }
269
        
270
        $this->noResult = false;
271
        if ($this->obtainImpactedRows() === 0) {
272
            $this->noResult = true;
273
        }
274
275
        return $this->lastRequestStatement;
276
    }
277
    
278
    /**
279
     * Closes the cursor, enabling the statement to be executed again.
280
     * 
281
     * @link http://php.net/manual/fr/pdostatement.closecursor.php
282
     * 
283
     * @return void
284
     */
285
    public function closeCursor()
286
    {
287
        return $this->lastRequestStatement->closeCursor();
288
    }
289
    
290
    /**
291
     * Return the number of impacted rows by the last request
292
     * 
293
     * @return int|bool
294
     */
295
    public function obtainImpactedRows()
296
    {
297
        if (is_object($this->lastRequestStatement)) {
298
            //If pdo::query or pdo::prepare
299
            return $this->lastRequestStatement->rowCount();
300
        } elseif (is_integer($this->lastRequestStatement)) {
301
            //If pdo::exec
302
            return $this->lastRequestStatement;
303
        }
304
        
305
        //Security if call without executed a request
306
        return false;
307
    }
308
    
309
    /**
310
     * To call this own request without use query writer
311
     * 
312
     * @param string $request The user request
313
     * 
314
     * @return void
315
     */
316
    public function query($request)
317
    {
318
        $this->assembledRequest = $request;
319
    }
320
    
321
    /**
322
     * Add a filter to where part of the request
323
     * 
324
     * @param string     $filter          The filter to add
325
     * @param array|null $preparedFilters (default: null) Filters to add
326
     *  in prepared request
327
     * 
328
     * @throws \Exception If key on prepared request is already used
329
     * 
330
     * @return \BfwSql\SqlActions
331
     */
332
    public function where($filter, $preparedFilters = null)
333
    {
334
        $this->where[] = $filter;
335
        
336
        if (is_array($preparedFilters)) {
337
            $this->addPreparedFilters($preparedFilters);
338
        }
339
        
340
        return $this;
341
    }
342
    
343
    /**
344
     * Add filters to prepared requests
345
     * 
346
     * @param array $preparedFilters Filters to add in prepared request
347
     * 
348
     * @return void
349
     */
350
    protected function addPreparedFilters($preparedFilters)
351
    {
352
        foreach ($preparedFilters as $prepareKey => $prepareValue) {
353
            $this->preparedRequestArgs[$prepareKey] = $prepareValue;
354
        }
355
    }
356
    
357
    /**
358
     * Write the where part of a sql query and return it
359
     * 
360
     * @return string
361
     */
362
    protected function generateWhere()
363
    {
364
        $where = '';
365
        
366
        //check if there are filters to write
367
        if (count($this->where) > 0) {
368
            $where = ' WHERE ';
369
            
370
            foreach ($this->where as $filter) {
371
                
372
                if ($where != ' WHERE ') {
373
                    $where .= ' AND ';
374
                }
375
                
376
                $where .= $filter;
377
            } 
378
        }
379
        
380
        return $where;
381
    }
382
    
383
    /**
384
     * Add datas to insert or update for a column.
385
     * Used by UPDATE and INSERT requests
386
     * 
387
     * @param array $columns Datas to add or update
388
     *  Format : array('sqlColumnName' => 'valueForThisColumn', ...);
389
     * 
390
     * @return \BfwSql\SqlActions
391
     */
392
    public function addDatasForColumns(array $columns)
393
    {
394
        foreach ($columns as $columnName => $data) {
395
            if (
396
                isset($this->columns[$columnName])
397
                && $this->columns[$columnName] != $data
398
            ) {
399
                throw new \Exception(
400
                    'A different data is already declared for the column '
401
                    .$columnName
402
                );
403
            }
404
            
405
            $this->columns[$columnName] = $data;
406
        }
407
        
408
        return $this;
409
    }
410
    
411
    /**
412
     * Declare columns should be automatic quoted if value is string.
413
     * 
414
     * @param string ...$columns Columns name
415
     * 
416
     * @throws Exception If the column is already declared to be not quoted
417
     * 
418
     * @return \BfwSql\SqlActions
419
     */
420 View Code Duplication
    public function addQuotedColumns(...$columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
421
    {
422
        if ($this instanceof SqlSelect || $this instanceof SqlDelete) {
423
            throw new Exception(
424
                'Sorry, automatic quoted value is not supported into '
425
                .get_called_class().' class'
426
            );
427
        }
428
        
429
        foreach ($columns as $columnName) {
430
            if (isset($this->notQuotedColumns[$columnName])) {
431
                throw new Exception(
432
                    'The column '.$columnName.' is already declared to be a'
433
                    .' not quoted value.'
434
                );
435
            }
436
            
437
            $this->quotedColumns[$columnName] = true;
438
        }
439
        
440
        return $this;
441
    }
442
    
443
    /**
444
     * Declare columns should not be automatic quoted if value is string.
445
     * 
446
     * @param string ...$columns Columns name
447
     * 
448
     * @throws Exception If the column is already declared to be quoted
449
     * 
450
     * @return \BfwSql\SqlActions
451
     */
452 View Code Duplication
    public function addNotQuotedColumns(...$columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
    {
454
        if ($this instanceof SqlSelect || $this instanceof SqlDelete) {
455
            throw new Exception(
456
                'Sorry, automatic quoted value is not supported into '
457
                .get_called_class().' class'
458
            );
459
        }
460
        
461
        foreach ($columns as $columnName) {
462
            if (isset($this->quotedColumns[$columnName])) {
463
                throw new Exception(
464
                    'The column '.$columnName.' is already declared to be a'
465
                    .' quoted value.'
466
                );
467
            }
468
            
469
            $this->notQuotedColumns[$columnName] = true;
470
        }
471
        
472
        return $this;
473
    }
474
    
475
    /**
476
     * Quote a value if need, else return the value passed in parameter
477
     * 
478
     * @param string $columnName The column corresponding to the value
479
     * @param string $value      The value to quote
480
     * 
481
     * @return string
482
     */
483
    protected function quoteValue($columnName, $value)
484
    {
485
        if ($this->quoteStatus === self::QUOTE_NONE) {
486
            return $value;
487
        }
488
        
489
        /**
490
         * If the status allow to quote partially, check if the column is
491
         * not declared to be quoted or is declared to be not quote.
492
         */
493
        if (
494
            (
495
                !isset($this->quotedColumns[$columnName])
496
                || isset($this->notQuotedColumns[$columnName])
497
            )
498
            && $this->quoteStatus === self::QUOTE_PARTIALLY
499
        ) {
500
            return $value;
501
        }
502
        
503
        if (!is_string($value)) {
504
            return $value;
505
        }
506
        
507
        return '"'.$value.'"';
508
    }
509
    
510
    /**
511
     * Send a notify to application observers
512
     * 
513
     * @return void
514
     */
515
    protected function callObserver()
516
    {
517
        $app = \BFW\Application::getInstance();
518
        $app->setContext($this);
519
        $app->notifyAction('BfwSqlRequest');
520
    }
521
}
522