QueryFilter::sql()   C
last analyzed

Complexity

Conditions 17
Paths 2

Size

Total Lines 63
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 17
eloc 40
c 3
b 0
f 0
nc 2
nop 0
dl 0
loc 63
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
/** 
4
 *  ___      _        _
5
 * | _ \__ _| |_ __ _| |__  __ _ ___ ___
6
 * |  _/ _` |  _/ _` | '_ \/ _` (_-</ -_)
7
 * |_| \__,_|\__\__,_|_.__/\__,_/__/\___|
8
 * 
9
 * This file is part of Kristuff\Patabase.
10
 * (c) Kristuff <[email protected]>
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 *
15
 * @version    1.0.1
16
 * @copyright  2017-2022 Christophe Buliard
17
 */
18
19
namespace Kristuff\Patabase\Query;
20
21
use Kristuff\Patabase;
22
use Kristuff\Patabase\Query;
23
use Kristuff\Patabase\Query\QueryBuilder;
24
use Kristuff\Patabase\Driver\DatabaseDriver;
25
26
/**
27
 * Class QueryFilter
28
 *
29
 * Abstract base class for WHERE and HAVING statements
30
 */
31
abstract class QueryFilter
32
{
33
    /**
34
     * QueryBuilder instance
35
     *
36
     * @access protected
37
     * @var    Query\QueryBuilder
38
     */
39
    protected $query;
40
41
    /**
42
     * Top QueryBuilder instance (in case of subquery)
43
     *
44
     * @access protected
45
     * @var    Query\QueryBuilder
46
     */
47
    protected $topQuery = null;
48
49
    /**
50
     * Driver instance
51
     *
52
     * @access protected
53
     * @var    Driver\DatabaseDriver
0 ignored issues
show
Bug introduced by
The type Kristuff\Patabase\Query\Driver\DatabaseDriver was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
54
     */
55
    protected $driver;
56
57
    /**
58
     * List of WHERE/HAVING conditions
59
     *
60
     * @access private
61
     * @var    array
62
     */
63
    protected $conditions = array();
64
65
    /**
66
     * Get whether a group (AND / OR) is open
67
     *
68
     * @access private
69
     * @var    bool
70
     */
71
    protected $isGroupOpen = false;
72
73
    /**
74
     * sql base: WHERE or HAVING
75
     *
76
     * @access protected
77
     * @var    string
78
     */
79
    protected $sqlBase = '';
80
81
    /**
82
     * Internal method to add condition
83
     *
84
     * @access private
85
     * @param string    $type    
86
     * @param string    $column  
87
     * @param string    $sql     
88
     * @param mixed     $value
89
     * @param string    $operator  
90
     *
91
     * @return void
92
     */
93
    protected function addCondition(string $type, string $sql, ?string $column, $value, ?string $operator = ''): void
94
    {
95
        $this->conditions[] = array(
96
            'type'     =>  $type,
97
            'sql'      =>  $sql,
98
            'column'   =>  $column,
99
            'value'    =>  $value,
100
            'operator' =>  $operator,
101
        );
102
    }
103
104
    /**
105
     * Get an argument name based on column name
106
     * Make sure the argument name is unique to Avoid collision in query parameters.
107
     *
108
     * @access private
109
     * @param string   $column     The column name
110
     *
111
     * @return string
112
     */
113
    private function getArgumentName(string $column): string
114
    {
115
        $arg = ':__' . str_replace('.', '_', $column); 
116
        return $this->topQuery->sqlParameterExists($arg) ? $arg . uniqid() : $arg;
117
    }
118
119
    /**
120
     * Constructor
121
     *
122
     * @access public
123
     * @param  Query\QueryBuilder       $query         The QueryBuilder instance
124
     * @param  Driver\DatabaseDriver    $driver        The Driver instance
125
     * @param  Query\QueryBuilder       $topQuery      The top QueryBuilder instance in case of sub query (default is null)
126
     */
127
    public function __construct(QueryBuilder $query, DatabaseDriver $driver, QueryBuilder $topQuery = null)
128
    {
129
        $this->query    = $query;
130
        $this->topQuery = $topQuery ? $topQuery : $query;
131
        $this->driver   = $driver;
0 ignored issues
show
Documentation Bug introduced by
It seems like $driver of type Kristuff\Patabase\Driver\DatabaseDriver is incompatible with the declared type Kristuff\Patabase\Query\Driver\DatabaseDriver of property $driver.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
132
    }
133
134
    /**
135
     * Begin OR group condition
136
     *
137
     * @access public
138
     * @return $this 
139
     */
140
    public function beginOr()
141
    {
142
        $this->addCondition('group_start', '(', null, null, 'OR');
143
        $this->isGroupOpen = true;
144
        return $this;
145
    }
146
147
    /**
148
     * Close OR group condition
149
     *
150
     * @access public
151
     * @return QueryBuilder
152
     */
153
    public function closeOr(): QueryBuilder
154
    {
155
        return $this->closeGroup();
156
    }
157
158
    /**
159
     * Begin AND group condition
160
     *
161
     * @access public
162
     * @return $this
163
     */
164
    public function beginAnd()
165
    {
166
        $this->addCondition('group_start', '(', null, null, 'AND');
167
        $this->isGroupOpen = true;
168
        return $this;
169
    }
170
171
    /**
172
     * Close AND group condition
173
     *
174
     * @access public
175
     * @return QueryBuilder
176
     */
177
    public function closeAnd(): QueryBuilder
178
    {
179
        return $this->closeGroup();
180
    }
181
182
    /**
183
     * Close group condition
184
     *
185
     * @access public
186
     * @return QueryBuilder
187
     */
188
    public function closeGroup(): QueryBuilder
189
    {
190
        $this->addCondition('group_end', ')', null, null);
191
        $this->isGroupOpen = false;
192
        return $this->query;
193
    }
194
195
    /**
196
     * Return function
197
     *
198
     * @access protected
199
     * @return $this|QueryBuilder
200
     */
201
    protected function returnFunction()
202
    {
203
         return $this->isGroupOpen === true ? $this : $this->query;
204
    }
205
206
    /**
207
     * Construct and returns the sql for IN or NOT IN statement
208
     *
209
     * @access protected
210
     * @param array    $item       The filter item
211
     *
212
     * @return string
213
     */
214
    protected function getSqlInOrNotIn(array $item): string
215
    {
216
        // define argument for each values
217
        $valueArgs = array();
218
        foreach($item['value'] as $value){
219
            $arg = $this->getArgumentName($item['column']); 
220
            $valueArgs[] = $arg;
221
222
            // set parameters
223
            $this->topQuery->setSqlParameter($arg, $value); 
224
        }
225
        
226
        //build and return sql
227
        return $item['sql']. '(' . implode(', ', $valueArgs) .')';
228
    }
229
230
    /**
231
     * Build sql and parameters
232
     *
233
     * @access public
234
     * @return string 
235
     */
236
    public function sql(): string
237
    {
238
        $sql = '';
239
        if (!empty($this->conditions)) {
240
            $sql = ' '. $this->sqlBase  .' ';  // start the SQL WHERE or HAVING clause
241
            $currentOperator = 'AND';          // current condition operator
242
            
243
            foreach ($this->conditions as $key => $item) {
244
245
                // need operator AND or OR, except for the first or if 
246
                // previous item is a begin group item
247
                $isSqlNeedOperator = $key > 0  && $this->conditions[$key -1]['type'] != 'group_start';
248
249
                switch ($item['type']) {
250
                    
251
                    case'group_start':
252
                        $currentOperator = $item['operator']; // register operator
253
                        $sql .= '(' ;
254
                        break;
255
                    
256
                    case'group_end':
257
                        $currentOperator = 'AND'; // reset operator
258
                        $sql .= ')';
259
                        break;
260
261
                   case 'NULL':
262
                   case 'NOT_NULL':
263
                        $sql .=  $isSqlNeedOperator ? ' '.$currentOperator.' ' : '';
264
                        $sql .=  $item['sql'];
265
                        break;
266
267
                    case 'IN':
268
                    case 'NOT_IN':
269
                        $sql .=  $isSqlNeedOperator ? ' '.$currentOperator.' ' : '';
270
                        $sql .=  $this->getSqlInOrNotIn($item);
271
                        break;
272
273
                    default:
274
                        // support for column literral 
275
                        if (is_string($item['value']) && 
276
                            strlen($item['value']) >  strlen(Patabase\Constants::COLUMN_LITERALL) &&
277
                            substr($item['value'], 0, strlen(Patabase\Constants::COLUMN_LITERALL)) === 
278
                                                             Patabase\Constants::COLUMN_LITERALL) {
279
280
                            $arg = substr($item['value'], strlen(Patabase\Constants::COLUMN_LITERALL));
281
                            $sql .=  $isSqlNeedOperator ? ' '.$currentOperator.' ' : '';
282
                            $sql .=  $item['sql'] . $this->query->escape($arg);
283
284
                        }   else {
285
                                // *normal* value 
286
                                $arg = $this->getArgumentName($item['column']);
287
                                $sql .=  $isSqlNeedOperator ? ' '.$currentOperator.' ' : '';
288
                                $sql .=  $item['sql'] . $arg;
289
                               
290
                                // set parameters
291
                                $this->topQuery->setSqlParameter($arg, $item['value']); 
292
                        }
293
                    break;
294
                }
295
            }
296
        }
297
        // now return
298
        return $sql;        
299
    }
300
}