Total Complexity | 56 |
Total Lines | 241 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 1 | Features | 0 |
Complex classes like InConditionBuilder 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.
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 InConditionBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
22 | class InConditionBuilder implements ExpressionBuilderInterface |
||
23 | { |
||
24 | use ExpressionBuilderTrait; |
||
25 | |||
26 | |||
27 | /** |
||
28 | * Method builds the raw SQL from the $expression that will not be additionally |
||
29 | * escaped or quoted. |
||
30 | * |
||
31 | * @param ExpressionInterface|InCondition $expression the expression to be built. |
||
32 | * @param array $params the binding parameters. |
||
33 | * @return string the raw SQL that will not be additionally escaped or quoted. |
||
34 | */ |
||
35 | public function build(ExpressionInterface $expression, array &$params = []) |
||
36 | { |
||
37 | $operator = strtoupper($expression->getOperator()); |
||
|
|||
38 | $column = $expression->getColumn(); |
||
39 | $values = $expression->getValues(); |
||
40 | |||
41 | if ($column === []) { |
||
42 | // no columns to test against |
||
43 | return $operator === 'IN' ? '0=1' : ''; |
||
44 | } |
||
45 | |||
46 | if ($values instanceof Query) { |
||
47 | return $this->buildSubqueryInCondition($operator, $column, $values, $params); |
||
48 | } |
||
49 | |||
50 | if (!is_array($values) && !$values instanceof \Traversable) { |
||
51 | // ensure values is an array |
||
52 | $values = (array) $values; |
||
53 | } |
||
54 | |||
55 | if (is_array($column)) { |
||
56 | if (count($column) > 1) { |
||
57 | return $this->buildCompositeInCondition($operator, $column, $values, $params); |
||
58 | } |
||
59 | $column = reset($column); |
||
60 | } |
||
61 | |||
62 | if ($column instanceof \Traversable) { |
||
63 | if (iterator_count($column) > 1) { |
||
64 | return $this->buildCompositeInCondition($operator, $column, $values, $params); |
||
65 | } |
||
66 | $column->rewind(); |
||
67 | $column = $column->current(); |
||
68 | } |
||
69 | |||
70 | if ($column instanceof Expression) { |
||
71 | $column = $column->expression; |
||
72 | } |
||
73 | |||
74 | if (is_array($values)) { |
||
75 | $rawValues = $values; |
||
76 | } elseif ($values instanceof \Traversable) { |
||
77 | $rawValues = $this->getRawValuesFromTraversableObject($values); |
||
78 | } |
||
79 | |||
80 | $nullCondition = null; |
||
81 | $nullConditionOperator = null; |
||
82 | if (isset($rawValues) && in_array(null, $rawValues, true)) { |
||
83 | $nullCondition = $this->getNullCondition($operator, $column); |
||
84 | $nullConditionOperator = $operator === 'IN' ? 'OR' : 'AND'; |
||
85 | } |
||
86 | |||
87 | $sqlValues = $this->buildValues($expression, $values, $params); |
||
88 | if (empty($sqlValues)) { |
||
89 | if ($nullCondition === null) { |
||
90 | return $operator === 'IN' ? '0=1' : ''; |
||
91 | } |
||
92 | return $nullCondition; |
||
93 | } |
||
94 | |||
95 | if (strpos($column, '(') === false) { |
||
96 | $column = $this->queryBuilder->db->quoteColumnName($column); |
||
97 | } |
||
98 | if (count($sqlValues) > 1) { |
||
99 | $sql = "$column $operator (" . implode(', ', $sqlValues) . ')'; |
||
100 | } else { |
||
101 | $operator = $operator === 'IN' ? '=' : '<>'; |
||
102 | $sql = $column . $operator . reset($sqlValues); |
||
103 | } |
||
104 | |||
105 | return $nullCondition !== null && $nullConditionOperator !== null |
||
106 | ? sprintf('%s %s %s', $sql, $nullConditionOperator, $nullCondition) |
||
107 | : $sql; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Builds $values to be used in [[InCondition]] |
||
112 | * |
||
113 | * @param ConditionInterface|InCondition $condition |
||
114 | * @param array $values |
||
115 | * @param array $params the binding parameters |
||
116 | * @return array of prepared for SQL placeholders |
||
117 | */ |
||
118 | protected function buildValues(ConditionInterface $condition, $values, &$params) |
||
119 | { |
||
120 | $sqlValues = []; |
||
121 | $column = $condition->getColumn(); |
||
122 | |||
123 | if (is_array($column)) { |
||
124 | $column = reset($column); |
||
125 | } |
||
126 | |||
127 | if ($column instanceof \Traversable) { |
||
128 | $column->rewind(); |
||
129 | $column = $column->current(); |
||
130 | } |
||
131 | |||
132 | if ($column instanceof Expression) { |
||
133 | $column = $column->expression; |
||
134 | } |
||
135 | |||
136 | foreach ($values as $i => $value) { |
||
137 | if (is_array($value) || $value instanceof \ArrayAccess) { |
||
138 | $value = isset($value[$column]) ? $value[$column] : null; |
||
139 | } |
||
140 | if ($value === null) { |
||
141 | continue; |
||
142 | } elseif ($value instanceof ExpressionInterface) { |
||
143 | $sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params); |
||
144 | } else { |
||
145 | $sqlValues[$i] = $this->queryBuilder->bindParam($value, $params); |
||
146 | } |
||
147 | } |
||
148 | |||
149 | return $sqlValues; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Builds SQL for IN condition. |
||
154 | * |
||
155 | * @param string $operator |
||
156 | * @param array|string $columns |
||
157 | * @param Query $values |
||
158 | * @param array $params |
||
159 | * @return string SQL |
||
160 | */ |
||
161 | protected function buildSubqueryInCondition($operator, $columns, $values, &$params) |
||
162 | { |
||
163 | $sql = $this->queryBuilder->buildExpression($values, $params); |
||
164 | |||
165 | if (is_array($columns)) { |
||
166 | foreach ($columns as $i => $col) { |
||
167 | if ($col instanceof Expression) { |
||
168 | $col = $col->expression; |
||
169 | } |
||
170 | if (strpos($col, '(') === false) { |
||
171 | $columns[$i] = $this->queryBuilder->db->quoteColumnName($col); |
||
172 | } |
||
173 | } |
||
174 | |||
175 | return '(' . implode(', ', $columns) . ") $operator $sql"; |
||
176 | } |
||
177 | |||
178 | if ($columns instanceof Expression) { |
||
179 | $columns = $columns->expression; |
||
180 | } |
||
181 | if (strpos($columns, '(') === false) { |
||
182 | $columns = $this->queryBuilder->db->quoteColumnName($columns); |
||
183 | } |
||
184 | |||
185 | return "$columns $operator $sql"; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Builds SQL for IN condition. |
||
190 | * |
||
191 | * @param string $operator |
||
192 | * @param array|\Traversable $columns |
||
193 | * @param array $values |
||
194 | * @param array $params |
||
195 | * @return string SQL |
||
196 | */ |
||
197 | protected function buildCompositeInCondition($operator, $columns, $values, &$params) |
||
198 | { |
||
199 | $vss = []; |
||
200 | foreach ($values as $value) { |
||
201 | $vs = []; |
||
202 | foreach ($columns as $column) { |
||
203 | if ($column instanceof Expression) { |
||
204 | $column = $column->expression; |
||
205 | } |
||
206 | if (isset($value[$column])) { |
||
207 | $vs[] = $this->queryBuilder->bindParam($value[$column], $params); |
||
208 | } else { |
||
209 | $vs[] = 'NULL'; |
||
210 | } |
||
211 | } |
||
212 | $vss[] = '(' . implode(', ', $vs) . ')'; |
||
213 | } |
||
214 | |||
215 | if (empty($vss)) { |
||
216 | return $operator === 'IN' ? '0=1' : ''; |
||
217 | } |
||
218 | |||
219 | $sqlColumns = []; |
||
220 | foreach ($columns as $i => $column) { |
||
221 | if ($column instanceof Expression) { |
||
222 | $column = $column->expression; |
||
223 | } |
||
224 | $sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column; |
||
225 | } |
||
226 | |||
227 | return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')'; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Builds is null/is not null condition for column based on operator |
||
232 | * |
||
233 | * @param string $operator |
||
234 | * @param string $column |
||
235 | * @return string is null or is not null condition |
||
236 | * @since 2.0.31 |
||
237 | */ |
||
238 | protected function getNullCondition($operator, $column) { |
||
239 | $column = $this->queryBuilder->db->quoteColumnName($column); |
||
240 | if ($operator === 'IN') { |
||
241 | return sprintf('%s IS NULL', $column); |
||
242 | } |
||
243 | return sprintf('%s IS NOT NULL', $column); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * @param \Traversable $traversableObject |
||
248 | * @return array raw values |
||
249 | * @since 2.0.31 |
||
250 | */ |
||
251 | protected function getRawValuesFromTraversableObject(\Traversable $traversableObject) |
||
263 | } |
||
264 | } |
||
265 |