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