Complex classes like QueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use QueryBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class QueryBuilder extends \yii\db\QueryBuilder |
||
22 | { |
||
23 | |||
24 | /** |
||
25 | * @var array mapping from abstract column types (keys) to physical column types (values). |
||
26 | */ |
||
27 | public $typeMap = [ |
||
28 | Schema::TYPE_PK => 'integer NOT NULL PRIMARY KEY', |
||
29 | Schema::TYPE_UPK => 'integer NOT NULL PRIMARY KEY', |
||
30 | Schema::TYPE_BIGPK => 'bigint NOT NULL PRIMARY KEY', |
||
31 | Schema::TYPE_UBIGPK => 'bigint NOT NULL PRIMARY KEY', |
||
32 | Schema::TYPE_CHAR => 'char(1)', |
||
33 | Schema::TYPE_STRING => 'varchar(255)', |
||
34 | Schema::TYPE_TEXT => 'blob sub_type text', |
||
35 | Schema::TYPE_SMALLINT => 'smallint', |
||
36 | Schema::TYPE_INTEGER => 'integer', |
||
37 | Schema::TYPE_BIGINT => 'bigint', |
||
38 | Schema::TYPE_FLOAT => 'float', |
||
39 | Schema::TYPE_DOUBLE => 'double precision', |
||
40 | Schema::TYPE_DECIMAL => 'numeric(10,0)', |
||
41 | Schema::TYPE_DATETIME => 'timestamp', |
||
42 | Schema::TYPE_TIMESTAMP => 'timestamp', |
||
43 | Schema::TYPE_TIME => 'time', |
||
44 | Schema::TYPE_DATE => 'date', |
||
45 | Schema::TYPE_BINARY => 'blob', |
||
46 | Schema::TYPE_BOOLEAN => 'smallint', |
||
47 | Schema::TYPE_MONEY => 'numeric(18,4)', |
||
48 | ]; |
||
49 | |||
50 | public function init() |
||
62 | |||
63 | 181 | protected function defaultExpressionBuilders() |
|
64 | { |
||
65 | return array_merge(parent::defaultExpressionBuilders(), [ |
||
66 | 181 | 'yii\db\Expression' => 'edgardmessias\db\firebird\ExpressionBuilder', |
|
67 | 181 | 'yii\db\conditions\InCondition' => 'edgardmessias\db\firebird\conditions\InConditionBuilder', |
|
68 | 181 | 'yii\db\conditions\LikeCondition' => 'edgardmessias\db\firebird\conditions\LikeConditionBuilder', |
|
69 | 181 | ]); |
|
70 | 181 | } |
|
71 | 181 | ||
72 | /** |
||
73 | * Generates a SELECT SQL statement from a [[Query]] object. |
||
74 | 181 | * @param Query $query the [[Query]] object from which the SQL statement will be generated. |
|
75 | 181 | * @param array $params the parameters to be bound to the generated SQL statement. These parameters will |
|
76 | * be included in the result with the additional parameters generated during the query building process. |
||
77 | 181 | * @return array the generated SQL statement (the first array element) and the corresponding |
|
78 | 34 | * parameters to be bound to the SQL statement (the second array element). The parameters returned |
|
79 | 34 | * include those provided in `$params`. |
|
80 | 34 | */ |
|
81 | public function build($query, $params = []) |
||
82 | { |
||
83 | $query = $query->prepare($this); |
||
84 | 181 | ||
85 | 2 | $params = empty($params) ? $query->params : array_merge($params, $query->params); |
|
86 | 2 | ||
87 | 2 | $clauses = [ |
|
88 | $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption), |
||
89 | $this->buildFrom($query->from, $params), |
||
90 | $this->buildJoin($query->join, $params), |
||
91 | $this->buildWhere($query->where, $params), |
||
92 | 181 | $this->buildGroupBy($query->groupBy), |
|
93 | 181 | $this->buildHaving($query->having, $params), |
|
94 | 2 | ]; |
|
95 | |||
96 | $sql = implode($this->separator, array_filter($clauses)); |
||
97 | 181 | $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset); |
|
98 | |||
99 | if (!empty($query->orderBy)) { |
||
100 | foreach ($query->orderBy as $expression) { |
||
101 | if ($expression instanceof Expression) { |
||
102 | $params = array_merge($params, $expression->params); |
||
103 | 181 | } |
|
104 | } |
||
105 | 181 | } |
|
106 | 53 | if (!empty($query->groupBy)) { |
|
107 | 53 | foreach ($query->groupBy as $expression) { |
|
108 | 3 | if ($expression instanceof Expression) { |
|
109 | $params = array_merge($params, $expression->params); |
||
110 | 52 | } |
|
111 | 52 | } |
|
112 | 21 | } |
|
113 | 21 | ||
114 | $union = $this->buildUnion($query->union, $params); |
||
115 | 52 | if ($union !== '') { |
|
116 | $sql = "$sql{$this->separator}$union"; |
||
117 | } |
||
118 | |||
119 | return [$sql, $params]; |
||
120 | 181 | } |
|
121 | |||
122 | /** |
||
123 | * @inheritdoc |
||
124 | */ |
||
125 | public function buildSelect($columns, &$params, $distinct = false, $selectOption = null) |
||
126 | 5 | { |
|
127 | if (is_array($columns)) { |
||
128 | 5 | foreach ($columns as $i => $column) { |
|
129 | 5 | if (!is_string($column)) { |
|
130 | 5 | continue; |
|
131 | } |
||
132 | 5 | $matches = []; |
|
133 | 5 | if (preg_match('/^(COUNT|SUM|AVG|MIN|MAX)\([\{\[]{0,2}(\w+|\*)[\}\]]{0,2}\)$/i', $column, $matches)) { |
|
134 | 5 | $function = $matches[1]; |
|
135 | 5 | $alias = $matches[2] != '*' ? $matches[2] : 'ALL'; |
|
136 | 5 | ||
137 | 5 | $columns[$i] = "{$column} AS {$function}_{$alias}"; |
|
138 | 5 | } |
|
139 | 5 | } |
|
140 | } |
||
141 | 5 | ||
142 | return parent::buildSelect($columns, $params, $distinct, $selectOption); |
||
143 | } |
||
144 | 5 | ||
145 | public function buildLimit($limit, $offset) |
||
146 | 5 | { |
|
147 | $sql = ''; |
||
148 | if ($this->hasLimit($limit)) { |
||
149 | if ($limit instanceof \yii\db\ExpressionInterface) { |
||
150 | $limit = "($limit)"; |
||
151 | } |
||
152 | 181 | $sql = 'FIRST ' . $limit; |
|
153 | } |
||
154 | if ($this->hasOffset($offset)) { |
||
155 | 181 | if ($offset instanceof \yii\db\ExpressionInterface) { |
|
156 | 181 | $offset = "($offset)"; |
|
157 | 34 | } |
|
158 | $sql .= ' SKIP ' . $offset; |
||
159 | } |
||
160 | 181 | ||
161 | 181 | return ltrim($sql); |
|
162 | } |
||
163 | 181 | ||
164 | 180 | /** |
|
165 | * @inheritdoc |
||
166 | */ |
||
167 | public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset) |
||
168 | 3 | { |
|
169 | 2 | ||
170 | 2 | $orderBy = $this->buildOrderBy($orderBy); |
|
171 | 2 | if ($orderBy !== '') { |
|
172 | $sql .= $this->separator . $orderBy; |
||
173 | } |
||
174 | |||
175 | 2 | $limit = $this->buildLimit($limit, $offset); |
|
176 | 2 | if ($limit !== '') { |
|
177 | 2 | $sql = preg_replace('/^SELECT /i', 'SELECT ' . $limit . ' ', $sql, 1); |
|
178 | 2 | } |
|
179 | |||
180 | return $sql; |
||
181 | 1 | } |
|
182 | 1 | ||
183 | 1 | /** |
|
184 | 1 | * @param array $unions |
|
185 | * @param array $params the binding parameters to be populated |
||
186 | * @return string the UNION clause built from [[Query::$union]]. |
||
187 | */ |
||
188 | public function buildUnion($unions, &$params) |
||
189 | { |
||
190 | if (empty($unions)) { |
||
191 | return ''; |
||
192 | } |
||
193 | |||
194 | $result = ''; |
||
195 | |||
196 | 181 | foreach ($unions as $i => $union) { |
|
197 | $query = $union['query']; |
||
198 | 181 | if ($query instanceof Query) { |
|
199 | 181 | list($unions[$i]['query'], $params) = $this->build($query, $params); |
|
200 | } |
||
201 | |||
202 | 2 | $result .= 'UNION ' . ($union['all'] ? 'ALL ' : '') . $unions[$i]['query'] . ' '; |
|
203 | } |
||
204 | 2 | ||
205 | 2 | return trim($result); |
|
206 | 2 | } |
|
207 | 2 | ||
208 | /** |
||
209 | * @inheritdoc |
||
210 | 2 | */ |
|
211 | public function prepareInsertValues($table, $columns, $params = []) |
||
212 | { |
||
213 | 2 | $schema = $this->db->getSchema(); |
|
214 | $tableSchema = $schema->getTableSchema($table); |
||
215 | $columnSchemas = $tableSchema !== null ? $tableSchema->columns : []; |
||
216 | |||
217 | //Empty insert |
||
218 | if (empty($columns) && !empty($columnSchemas)) { |
||
219 | $columns = []; |
||
220 | foreach ($columnSchemas as $columnSchema) { |
||
221 | 3 | if (!$columnSchema->autoIncrement) { |
|
222 | $columns[$columnSchema->name] = $columnSchema->defaultValue; |
||
223 | 3 | } |
|
224 | } |
||
225 | } |
||
226 | |||
227 | if (is_array($columns)) { |
||
228 | foreach ($columns as $name => $value) { |
||
229 | 3 | if ($value instanceof \yii\db\ExpressionInterface) { |
|
230 | continue; |
||
231 | 3 | } |
|
232 | if ($value instanceof \yii\db\PdoValue) { |
||
233 | continue; |
||
234 | 3 | } |
|
235 | if (isset($columnSchemas[$name]) && in_array($columnSchemas[$name]->type, [Schema::TYPE_TEXT, Schema::TYPE_BINARY])) { |
||
236 | $columns[$name] = [$value, \PDO::PARAM_LOB]; |
||
237 | } |
||
238 | } |
||
239 | } |
||
240 | 23 | ||
241 | return parent::prepareInsertValues($table, $columns, $params); |
||
242 | 23 | } |
|
243 | 23 | ||
244 | 23 | /** |
|
245 | * @inheritdoc |
||
246 | */ |
||
247 | protected function prepareInsertSelectSubQuery($columns, $schema, $params = []) |
||
248 | { |
||
249 | /** |
||
250 | 23 | * @see https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-dml-insert.html#fblangref25-dml-insert-select-unstable |
|
251 | 1 | */ |
|
252 | 1 | if (version_compare($this->db->firebird_version, '3.0.0', '<')) { |
|
253 | 1 | throw new NotSupportedException('Firebird < 3.0.0 has the "Unstable Cursor" problem'); |
|
254 | 1 | } |
|
255 | |||
256 | return parent::prepareInsertSelectSubQuery($columns, $schema, $params); |
||
257 | } |
||
258 | |||
259 | 23 | /** |
|
260 | 23 | * @inheritdoc |
|
261 | 23 | */ |
|
262 | 1 | public function prepareUpdateSets($table, $columns, $params = []) |
|
263 | 23 | { |
|
264 | 23 | $schema = $this->db->getSchema(); |
|
265 | $tableSchema = $schema->getTableSchema($table); |
||
266 | $columnSchemas = $tableSchema !== null ? $tableSchema->columns : []; |
||
267 | |||
268 | foreach ($columns as $name => $value) { |
||
269 | 23 | if ($value instanceof \yii\db\ExpressionInterface) { |
|
270 | continue; |
||
271 | } |
||
272 | if ($value instanceof \yii\db\PdoValue) { |
||
273 | continue; |
||
274 | } |
||
275 | if (isset($columnSchemas[$name]) && in_array($columnSchemas[$name]->type, [Schema::TYPE_TEXT, Schema::TYPE_BINARY])) { |
||
276 | $columns[$name] = [$value, \PDO::PARAM_LOB]; |
||
277 | } |
||
278 | } |
||
279 | return parent::prepareUpdateSets($table, $columns, $params); |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * @inheritdoc |
||
284 | */ |
||
285 | public function batchInsert($table, $columns, $rows, &$params = []) |
||
286 | { |
||
287 | if (empty($rows)) { |
||
288 | return ''; |
||
289 | } |
||
290 | 13 | ||
291 | $schema = $this->db->getSchema(); |
||
292 | 13 | if (($tableSchema = $schema->getTableSchema($table)) !== null) { |
|
293 | 13 | $columnSchemas = $tableSchema->columns; |
|
294 | 13 | } else { |
|
295 | $columnSchemas = []; |
||
296 | } |
||
297 | |||
298 | 13 | $values = []; |
|
299 | 13 | foreach ($rows as $row) { |
|
300 | 2 | $vs = []; |
|
301 | 11 | foreach ($row as $i => $value) { |
|
302 | 13 | if (isset($columns[$i], $columnSchemas[$columns[$i]]) && !is_array($value)) { |
|
303 | $value = $columnSchemas[$columns[$i]]->dbTypecast($value); |
||
304 | } |
||
305 | 13 | if (is_string($value)) { |
|
306 | $value = $schema->quoteValue($value); |
||
307 | } elseif (is_float($value)) { |
||
308 | // ensure type cast always has . as decimal separator in all locales |
||
309 | $value = \yii\helpers\StringHelper::floatToString($value); |
||
310 | } elseif ($value === false) { |
||
311 | 8 | $value = 0; |
|
312 | } elseif ($value === null) { |
||
313 | 8 | $value = 'NULL'; |
|
314 | 2 | } elseif ($value instanceof ExpressionInterface) { |
|
|
|||
315 | $value = $this->buildExpression($value, $params); |
||
316 | } |
||
317 | 7 | $vs[] = $value; |
|
318 | 7 | } |
|
319 | 7 | $values[] = 'INSERT INTO ' . $schema->quoteTableName($table) |
|
320 | . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $vs) . ');'; |
||
321 | } |
||
322 | |||
323 | foreach ($columns as $i => $name) { |
||
324 | 7 | $columns[$i] = $schema->quoteColumnName($name); |
|
325 | 7 | } |
|
326 | 7 | ||
327 | 7 | return 'EXECUTE block AS BEGIN ' . implode(' ', $values) . ' END;'; |
|
328 | 7 | } |
|
329 | 4 | ||
330 | /** |
||
331 | 7 | * {@inheritdoc} |
|
332 | 4 | * @see https://www.firebirdsql.org/refdocs/langrefupd21-update-or-insert.html |
|
333 | 4 | * @see https://www.firebirdsql.org/refdocs/langrefupd21-merge.html |
|
334 | 1 | */ |
|
335 | 4 | public function upsert($table, $insertColumns, $updateColumns, &$params) |
|
403 | 2 | ||
404 | 2 | /** |
|
405 | 2 | * @inheritdoc |
|
406 | 2 | */ |
|
407 | public function renameTable($oldName, $newName) |
||
411 | 3 | ||
412 | 3 | /** |
|
413 | * @inheritdoc |
||
414 | 3 | */ |
|
415 | public function truncateTable($table) |
||
419 | |||
420 | /** |
||
421 | * @inheritdoc |
||
422 | */ |
||
423 | public function dropColumn($table, $column) |
||
428 | |||
429 | /** |
||
430 | * @inheritdoc |
||
431 | */ |
||
432 | public function renameColumn($table, $oldName, $newName) |
||
438 | |||
439 | /** |
||
440 | * @inheritdoc |
||
441 | */ |
||
442 | public function alterColumn($table, $column, $type) |
||
518 | |||
519 | /** |
||
520 | * @inheritdoc |
||
521 | 5 | */ |
|
522 | public function dropIndex($name, $table) |
||
526 | |||
527 | 4 | /** |
|
528 | 4 | * {@inheritdoc} |
|
529 | */ |
||
530 | public function addDefaultValue($name, $table, $column, $value) |
||
535 | 4 | ||
536 | 4 | /** |
|
537 | 4 | * {@inheritdoc} |
|
538 | */ |
||
539 | public function dropDefaultValue($name, $table) |
||
544 | 1 | ||
545 | /** |
||
546 | * @inheritdoc |
||
547 | */ |
||
548 | public function resetSequence($table, $value = null) |
||
579 | |||
580 | /** |
||
581 | * @inheritdoc |
||
582 | */ |
||
583 | public function createTable($table, $columns, $options = null) |
||
621 | |||
622 | /** |
||
623 | * @inheritdoc |
||
624 | */ |
||
625 | public function dropTable($table) |
||
657 | |||
658 | /** |
||
659 | * Creates a SELECT EXISTS() SQL statement. |
||
660 | * @param string $rawSql the subquery in a raw form to select from. |
||
661 | * @return string the SELECT EXISTS() SQL statement. |
||
662 | * |
||
663 | * @since 2.0.8 |
||
664 | */ |
||
665 | public function selectExists($rawSql) |
||
669 | } |
||
670 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.