Complex classes like MySQLData 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 MySQLData, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
20 | class MySQLData extends AbstractData { |
||
21 | |||
22 | /** |
||
23 | * Holds the Doctrine DBAL instance. |
||
24 | */ |
||
25 | protected $database; |
||
26 | |||
27 | /** |
||
28 | * Flag whether to use UUIDs as primary key. |
||
29 | */ |
||
30 | protected $useUUIDs; |
||
31 | |||
32 | /** |
||
33 | * Gets the many-to-many fields. |
||
34 | * |
||
35 | * @return array|\string[] |
||
36 | * the many-to-many fields |
||
37 | */ |
||
38 | protected function getManyFields() { |
||
44 | |||
45 | /** |
||
46 | * Gets all form fields including the many-to-many-ones. |
||
47 | * |
||
48 | * @return array |
||
49 | * all form fields |
||
50 | */ |
||
51 | protected function getFormFields() { |
||
61 | |||
62 | /** |
||
63 | * Sets the values and parameters of the upcoming given query according |
||
64 | * to the entity. |
||
65 | * |
||
66 | * @param Entity $entity |
||
67 | * the entity with its fields and values |
||
68 | * @param QueryBuilder $queryBuilder |
||
69 | * the upcoming query |
||
70 | * @param string $setMethod |
||
71 | * what method to use on the QueryBuilder: 'setValue' or 'set' |
||
72 | */ |
||
73 | protected function setValuesAndParameters(Entity $entity, QueryBuilder $queryBuilder, $setMethod) { |
||
74 | $formFields = $this->getFormFields(); |
||
75 | $count = count($formFields); |
||
76 | for ($i = 0; $i < $count; ++$i) { |
||
77 | $type = $this->definition->getType($formFields[$i]); |
||
78 | $value = $entity->get($formFields[$i]); |
||
79 | if ($type == 'boolean') { |
||
80 | $value = $value ? 1 : 0; |
||
81 | } |
||
82 | if ($type == 'reference' && is_array($value)) { |
||
83 | $value = $value['id']; |
||
84 | } |
||
85 | $queryBuilder->$setMethod('`'.$formFields[$i].'`', '?'); |
||
86 | $queryBuilder->setParameter($i, $value); |
||
87 | } |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Performs the cascading children deletion. |
||
92 | * |
||
93 | * @param integer $id |
||
94 | * the current entities id |
||
95 | * @param boolean $deleteCascade |
||
96 | * whether to delete children and sub children |
||
97 | */ |
||
98 | protected function deleteChildren($id, $deleteCascade) { |
||
99 | foreach ($this->definition->getChildren() as $childArray) { |
||
100 | $childData = $this->definition->getServiceProvider()->getData($childArray[2]); |
||
101 | $children = $childData->listEntries([$childArray[1] => $id]); |
||
102 | foreach ($children as $child) { |
||
103 | $childData->doDelete($child, $deleteCascade); |
||
104 | } |
||
105 | } |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Checks whether the by id given entity still has children referencing it. |
||
110 | * |
||
111 | * @param integer $id |
||
112 | * the current entities id |
||
113 | * |
||
114 | * @return boolean |
||
115 | * true if the entity still has children |
||
116 | */ |
||
117 | protected function hasChildren($id) { |
||
118 | foreach ($this->definition->getChildren() as $child) { |
||
119 | $queryBuilder = $this->database->createQueryBuilder(); |
||
120 | $queryBuilder |
||
121 | ->select('COUNT(id)') |
||
122 | ->from('`'.$child[0].'`', '`'.$child[0].'`') |
||
123 | ->where('`'.$child[1].'` = ?') |
||
124 | ->andWhere('deleted_at IS NULL') |
||
125 | ->setParameter(0, $id); |
||
126 | $queryResult = $queryBuilder->execute(); |
||
127 | $result = $queryResult->fetch(\PDO::FETCH_NUM); |
||
128 | if ($result[0] > 0) { |
||
129 | return true; |
||
130 | } |
||
131 | } |
||
132 | return false; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * {@inheritdoc} |
||
137 | */ |
||
138 | protected function doDelete(Entity $entity, $deleteCascade) { |
||
139 | $result = $this->shouldExecuteEvents($entity, 'before', 'delete'); |
||
140 | if (!$result) { |
||
141 | return static::DELETION_FAILED_EVENT; |
||
142 | } |
||
143 | $id = $entity->get('id'); |
||
144 | if ($deleteCascade) { |
||
145 | $this->deleteChildren($id, $deleteCascade); |
||
146 | } elseif ($this->hasChildren($id)) { |
||
147 | return static::DELETION_FAILED_STILL_REFERENCED; |
||
148 | } |
||
149 | |||
150 | $query = $this->database->createQueryBuilder(); |
||
151 | $query |
||
152 | ->update('`'.$this->definition->getTable().'`') |
||
153 | ->set('deleted_at', 'UTC_TIMESTAMP()') |
||
154 | ->where('id = ?') |
||
155 | ->setParameter(0, $id); |
||
156 | |||
157 | $query->execute(); |
||
158 | $this->shouldExecuteEvents($entity, 'after', 'delete'); |
||
159 | return static::DELETION_SUCCESS; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Gets all possible many-to-many ids existing for this definition. |
||
164 | * |
||
165 | * @param array $fields |
||
166 | * the many field names to fetch for |
||
167 | * @param $params |
||
168 | * the parameters the possible many field values to fetch for |
||
169 | * @return array |
||
170 | * an array of this many-to-many ids |
||
171 | */ |
||
172 | protected function getManyIds(array $fields, array $params) { |
||
173 | $manyIds = []; |
||
174 | foreach ($fields as $field) { |
||
175 | $thisField = $this->definition->getSubTypeField($field, 'many', 'thisField'); |
||
176 | $thatField = $this->definition->getSubTypeField($field, 'many', 'thatField'); |
||
177 | $queryBuilder = $this->database->createQueryBuilder(); |
||
178 | $queryBuilder |
||
179 | ->select('`'.$thisField.'`') |
||
180 | ->from($field) |
||
181 | ->where('`'.$thatField.'` IN (?)') |
||
182 | ->setParameter(0, array_column($params[$field], 'id'), Connection::PARAM_STR_ARRAY) |
||
183 | ->groupBy('`'.$thisField.'`') |
||
184 | ; |
||
185 | $queryResult = $queryBuilder->execute(); |
||
186 | $manyResults = $queryResult->fetchAll(\PDO::FETCH_ASSOC); |
||
187 | $manyIds = array_merge($manyIds, array_column($manyResults, $thisField)); |
||
188 | |||
189 | } |
||
190 | return $manyIds; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Adds sorting parameters to the query. |
||
195 | * |
||
196 | * @param QueryBuilder $queryBuilder |
||
197 | * the query |
||
198 | * @param $filter |
||
199 | * the filter all resulting entities must fulfill, the keys as field names |
||
200 | * @param $filterOperators |
||
201 | * the operators of the filter like "=" defining the full condition of the field |
||
202 | */ |
||
203 | protected function addFilter(QueryBuilder $queryBuilder, array $filter, array $filterOperators) { |
||
229 | |||
230 | /** |
||
231 | * Adds pagination parameters to the query. |
||
232 | * |
||
233 | * @param QueryBuilder $queryBuilder |
||
234 | * the query |
||
235 | * @param integer|null $skip |
||
236 | * the rows to skip |
||
237 | * @param integer|null $amount |
||
238 | * the maximum amount of rows |
||
239 | */ |
||
240 | protected function addPagination(QueryBuilder $queryBuilder, $skip, $amount) { |
||
249 | |||
250 | /** |
||
251 | * Adds sorting parameters to the query. |
||
252 | * |
||
253 | * @param QueryBuilder $queryBuilder |
||
254 | * the query |
||
255 | * @param string|null $sortField |
||
256 | * the sort field |
||
257 | * @param boolean|null $sortAscending |
||
258 | * true if sort ascending, false if descending |
||
259 | */ |
||
260 | protected function addSort(QueryBuilder $queryBuilder, $sortField, $sortAscending) { |
||
272 | |||
273 | /** |
||
274 | * Adds the id and name of referenced entities to the given entities. The |
||
275 | * reference field is before the raw id of the referenced entity and after |
||
276 | * the fetch, it's an array with the keys id and name. |
||
277 | * |
||
278 | * @param Entity[] &$entities |
||
279 | * the entities to fetch the references for |
||
280 | * @param string $field |
||
281 | * the reference field |
||
282 | */ |
||
283 | protected function fetchReferencesForField(array &$entities, $field) { |
||
321 | |||
322 | /** |
||
323 | * Generates a new UUID. |
||
324 | * |
||
325 | * @return string|null |
||
326 | * the new UUID or null if this instance isn't configured to do so |
||
327 | */ |
||
328 | protected function generateUUID() { |
||
337 | |||
338 | /** |
||
339 | * Enriches the given mapping of entity id to raw entity data with some many-to-many data. |
||
340 | * |
||
341 | * @param array $idToData |
||
342 | * a reference to the map entity id to raw entity data |
||
343 | * @param $manyField |
||
344 | * the many field to enrich data with |
||
345 | */ |
||
346 | protected function enrichWithManyField(&$idToData, $manyField) { |
||
369 | |||
370 | /** |
||
371 | * Fetches to the rows belonging many-to-many entries and adds them to the rows. |
||
372 | * |
||
373 | * @param array $rows |
||
374 | * the rows to enrich |
||
375 | * @return array |
||
376 | * the enriched rows |
||
377 | */ |
||
378 | protected function enrichWithMany(array $rows) { |
||
392 | |||
393 | /** |
||
394 | * First, deletes all to the given entity related many-to-many entries from the DB |
||
395 | * and then writes them again. |
||
396 | * |
||
397 | * @param Entity $entity |
||
398 | * the entity to save the many-to-many entries of |
||
399 | */ |
||
400 | protected function saveMany(Entity $entity) { |
||
416 | |||
417 | /** |
||
418 | * Adds the id and name of referenced entities to the given entities. Each |
||
419 | * reference field is before the raw id of the referenced entity and after |
||
420 | * the fetch, it's an array with the keys id and name. |
||
421 | * |
||
422 | * @param Entity[] &$entities |
||
423 | * the entities to fetch the references for |
||
424 | * |
||
425 | * @return void |
||
426 | */ |
||
427 | protected function enrichWithReference(array &$entities) { |
||
438 | |||
439 | /** |
||
440 | * Constructor. |
||
441 | * |
||
442 | * @param EntityDefinition $definition |
||
443 | * the entity definition |
||
444 | * @param FileProcessorInterface $fileProcessor |
||
445 | * the file processor to use |
||
446 | * @param $database |
||
447 | * the Doctrine DBAL instance to use |
||
448 | * @param boolean $useUUIDs |
||
449 | * flag whether to use UUIDs as primary key |
||
450 | */ |
||
451 | public function __construct(EntityDefinition $definition, FileProcessorInterface $fileProcessor, $database, $useUUIDs) { |
||
457 | |||
458 | /** |
||
459 | * {@inheritdoc} |
||
460 | */ |
||
461 | public function get($id) { |
||
468 | |||
469 | /** |
||
470 | * {@inheritdoc} |
||
471 | */ |
||
472 | public function listEntries(array $filter = [], array $filterOperators = [], $skip = null, $amount = null, $sortField = null, $sortAscending = null) { |
||
496 | |||
497 | /** |
||
498 | * {@inheritdoc} |
||
499 | */ |
||
500 | public function create(Entity $entity) { |
||
539 | |||
540 | /** |
||
541 | * {@inheritdoc} |
||
542 | */ |
||
543 | public function update(Entity $entity) { |
||
567 | |||
568 | /** |
||
569 | * {@inheritdoc} |
||
570 | */ |
||
571 | public function getIdToNameMap($entity, $nameField) { |
||
591 | |||
592 | /** |
||
593 | * {@inheritdoc} |
||
594 | */ |
||
595 | public function countBy($table, array $params, array $paramsOperators, $excludeDeleted) { |
||
635 | |||
636 | /** |
||
637 | * {@inheritdoc} |
||
638 | */ |
||
639 | public function hasManySet($field, array $thatIds, $excludeId = null) { |
||
661 | |||
662 | } |
||
663 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.