Complex classes like Mapper 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 Mapper, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class Mapper extends AbstractMapper implements |
||
14 | c\Filterable, |
||
15 | c\Mixable, |
||
16 | c\Typable |
||
17 | { |
||
18 | /** @var \Respect\Structural\Driver Holds our connector* */ |
||
19 | protected $driver; |
||
20 | |||
21 | /** @var string Namespace to look for entities * */ |
||
22 | public $entityNamespace = '\\'; |
||
23 | |||
24 | /** |
||
25 | * @param Driver $driver |
||
26 | */ |
||
27 | 3 | public function __construct(Driver $driver) |
|
28 | { |
||
29 | 3 | parent::__construct(); |
|
30 | |||
31 | 3 | $this->driver = $driver; |
|
32 | 3 | } |
|
33 | |||
34 | /** |
||
35 | * @return Mapper |
||
36 | */ |
||
37 | 3 | public function __get($name) |
|
38 | { |
||
39 | 3 | return parent::__get($name); |
|
40 | } |
||
41 | |||
42 | /** |
||
43 | * Flushes a single instance into the database. This method supports |
||
44 | * mixing, so flushing a mixed instance will flush distinct tables on the |
||
45 | * database |
||
46 | * |
||
47 | * @param object $entity Entity instance to be flushed |
||
48 | * |
||
49 | * @return null |
||
50 | */ |
||
51 | 3 | protected function flushSingle($entity) |
|
52 | { |
||
53 | 3 | $coll = $this->tracked[$entity]; |
|
54 | 3 | $cols = $this->extractColumns($entity, $coll); |
|
55 | |||
56 | 3 | if ($this->removed->contains($entity)) { |
|
57 | 1 | $this->rawDelete($coll, $entity); |
|
58 | 3 | } elseif ($this->new->contains($entity)) { |
|
59 | 1 | $this->rawInsert($coll, $entity); |
|
60 | 1 | } else { |
|
61 | 1 | $this->rawUpdate($cols, $coll); |
|
62 | } |
||
63 | 3 | } |
|
64 | |||
65 | 2 | public function persist($object, Collection $onCollection) |
|
66 | { |
||
67 | 2 | $next = $onCollection->getNext(); |
|
68 | |||
69 | 2 | if ($this->filterable($onCollection)) { |
|
70 | $next->setMapper($this); |
||
71 | $next->persist($object); |
||
72 | |||
73 | return; |
||
74 | } |
||
75 | |||
76 | 2 | if ($next) { |
|
77 | $remote = $this->getStyle()->remoteIdentifier($next->getName()); |
||
78 | $next->setMapper($this); |
||
79 | $next->persist($object->$remote); |
||
80 | } |
||
81 | |||
82 | 2 | foreach ($onCollection->getChildren() as $child) { |
|
83 | $remote = $this->getStyle()->remoteIdentifier($child->getName()); |
||
84 | $child->persist($object->$remote); |
||
85 | 2 | } |
|
86 | |||
87 | 2 | return parent::persist($object, $onCollection); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Receives columns from an entity and her collection. Returns the columns |
||
92 | * that belong only to the main entity. This method supports mixing, so |
||
93 | * extracting mixins will also persist them on their respective |
||
94 | * tables |
||
95 | * |
||
96 | * @param \Respect\Data\Collections\Collection $collection Target collection |
||
97 | * @param array $cols Entity columns |
||
98 | * |
||
99 | * @return array Columns left for the main collection |
||
100 | */ |
||
101 | 1 | protected function extractAndOperateMixins(Collection $collection, $cols) |
|
129 | |||
130 | 2 | protected function guessCondition(&$columns, Collection $collection) |
|
131 | { |
||
132 | 2 | $primaryName = $this->getStyle()->identifier($collection->getName()); |
|
133 | 2 | $condition = [$primaryName => $columns[$primaryName]]; |
|
134 | 2 | unset($columns[$primaryName]); |
|
135 | |||
136 | 2 | return $condition; |
|
137 | } |
||
138 | |||
139 | 1 | protected function rawDelete(Collection $collection, $entity) |
|
140 | { |
||
141 | 1 | $name = $collection->getName(); |
|
142 | 1 | $columns = $this->extractColumns($entity, $collection); |
|
143 | 1 | $condition = $this->guessCondition($columns, $collection); |
|
144 | |||
145 | 1 | return $this->driver->remove($name, $condition); |
|
146 | } |
||
147 | |||
148 | 1 | protected function rawUpdate(array $columns, Collection $collection) |
|
149 | { |
||
150 | 1 | $columns = $this->extractAndOperateMixins($collection, $columns); |
|
151 | 1 | $name = $collection->getName(); |
|
152 | 1 | $condition = $this->guessCondition($columns, $collection); |
|
153 | |||
154 | 1 | $this->driver->update($name, $condition, $columns); |
|
155 | 1 | } |
|
156 | |||
157 | 1 | protected function rawInsert(Collection $collection, $entity = null) |
|
158 | { |
||
159 | 1 | $name = $collection->getName(); |
|
160 | 1 | $this->driver->insert($name, $entity); |
|
161 | 1 | } |
|
162 | |||
163 | 3 | public function flush() |
|
164 | { |
||
165 | try { |
||
166 | 3 | foreach ($this->changed as $entity) { |
|
167 | 3 | $this->flushSingle($entity); |
|
168 | 3 | } |
|
169 | 3 | } catch (Exception $e) { |
|
170 | throw $e; |
||
171 | } |
||
172 | |||
173 | 3 | $this->reset(); |
|
174 | 3 | } |
|
175 | |||
176 | 2 | protected function createStatement(Collection $collection, $withExtra = null) |
|
177 | { |
||
178 | 2 | $query = $this->generateQuery($collection); |
|
179 | |||
180 | 2 | $withExtraList = (array)$withExtra; |
|
181 | |||
182 | 2 | $withExtraList = array_merge($withExtraList, $query); |
|
183 | |||
184 | 2 | return $this->driver->find($collection->getName(), $withExtraList); |
|
185 | } |
||
186 | |||
187 | 2 | protected function generateQuery(Collection $collection) |
|
188 | { |
||
189 | 2 | return $this->driver->generateQuery($collection); |
|
190 | } |
||
191 | |||
192 | 3 | protected function extractColumns($entity, Collection $collection) |
|
193 | { |
||
194 | 3 | $cols = get_object_vars($entity); |
|
195 | |||
196 | 3 | return $cols; |
|
197 | } |
||
198 | |||
199 | protected function hasComposition($entity, $next, $parent) |
||
200 | { |
||
201 | $style = $this->getStyle(); |
||
202 | |||
203 | return $entity === $style->composed($parent, $next) || $entity === $style->composed($next, $parent); |
||
204 | } |
||
205 | |||
206 | 2 | protected function fetchSingle(Collection $collection, $statement) |
|
225 | |||
226 | 2 | protected function getNewEntityByName($entityName) |
|
227 | { |
||
228 | 2 | $entityName = $this->getStyle()->styledName($entityName); |
|
229 | 2 | $entityClass = $this->entityNamespace . $entityName; |
|
230 | 2 | $entityClass = class_exists($entityClass) ? $entityClass : '\stdClass'; |
|
231 | |||
232 | 2 | return new $entityClass; |
|
233 | } |
||
234 | |||
235 | 2 | protected function transformSingleRow($row, $entityName) |
|
236 | { |
||
237 | 2 | $newRow = $this->getNewEntityByName($entityName); |
|
238 | |||
239 | 2 | foreach ($row as $prop => $value) { |
|
245 | |||
246 | 2 | protected function inferSet(&$entity, $prop, $value) |
|
256 | |||
257 | protected function fetchMulti(Collection $collection, $statement) |
||
271 | |||
272 | protected function createEntities($row, $statement, Collection $collection) |
||
295 | |||
296 | protected function buildEntitiesInstances(Collection $collection, SplObjectStorage $entities) |
||
320 | |||
321 | protected function postHydrate(SplObjectStorage $entities) |
||
335 | |||
336 | protected function tryHydration($entities, $sub, $field, &$value) |
||
347 | |||
348 | protected function getSetterStyle($name) |
||
354 | |||
355 | public function getFilters(Collection $collection) |
||
359 | |||
360 | public function getMixins(Collection $collection) |
||
364 | |||
365 | public function getType(Collection $collection) |
||
369 | |||
370 | 1 | public function mixable(Collection $collection) |
|
374 | |||
375 | 2 | public function typable(Collection $collection) |
|
379 | |||
380 | 2 | public function filterable(Collection $collection) |
|
384 | } |
||
385 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.