Complex classes like Repository 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 Repository, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class Repository implements Contracts\Repository |
||
9 | { |
||
10 | private $type; |
||
11 | private $entities = []; |
||
12 | private $keyMap = []; |
||
13 | private $findCache = []; |
||
14 | |||
15 | private $magicMethodRules = [ |
||
16 | 'by' => false, |
||
17 | 'firstBy' => true, |
||
18 | 'oneBy' => true, |
||
19 | ]; |
||
20 | |||
21 | 32 | public function __construct(Contracts\Type $type) |
|
25 | |||
26 | 32 | public function create($params = null) |
|
27 | { |
||
28 | 32 | if ($params && !is_array($params)) { |
|
29 | 32 | $params = [$params]; |
|
30 | } |
||
31 | |||
32 | 32 | $data = []; |
|
33 | 32 | foreach ($params as $k => $v) { |
|
34 | 32 | if (is_numeric($k)) { |
|
35 | 32 | if ($v instanceof Contracts\Entity) { |
|
36 | 4 | $type = $this->type->getManager()->findRepository($v)->getType(); |
|
37 | 4 | $k = $this->type->getReferenceProperty($type); |
|
38 | } else { |
||
39 | 32 | $primitive = []; |
|
40 | 32 | foreach ($this->type->getProperties() as $property) { |
|
41 | 32 | if (!$this->type->isReference($property)) { |
|
42 | 32 | $primitive[] = $property; |
|
43 | } |
||
44 | } |
||
45 | 32 | if (count($primitive) == 2) { |
|
46 | 32 | $k = $primitive[1]; |
|
47 | } else { |
||
48 | 1 | throw new \Exception("Can't calculate key name"); |
|
49 | } |
||
50 | } |
||
51 | } |
||
52 | 32 | if (!$this->type->hasProperty($k)) { |
|
53 | 1 | throw new \Exception("Unknown property $k"); |
|
54 | } |
||
55 | 32 | $data[$k] = $this->type->encodeProperty($k, $v); |
|
56 | } |
||
57 | |||
58 | 32 | return $this->register(new Entity($data)); |
|
59 | } |
||
60 | |||
61 | 32 | public function __call($method, $arguments) |
|
62 | { |
||
63 | 32 | foreach ($this->magicMethodRules as $prefix => $oneItem) { |
|
64 | 32 | if (substr($method, 0, strlen($prefix)) == $prefix) { |
|
65 | 32 | $tail = substr($method, strlen($prefix)); |
|
66 | 32 | $fields = array_map('strtolower', explode('And', $tail)); |
|
67 | |||
68 | 32 | return $this->find(array_combine($fields, $arguments), $oneItem); |
|
69 | } |
||
70 | } |
||
71 | |||
72 | 1 | throw new BadMethodCallException("Method $method not found"); |
|
73 | } |
||
74 | |||
75 | 7 | public function findOne($params) |
|
76 | { |
||
77 | 7 | return $this->find($params, true); |
|
|
|||
78 | } |
||
79 | |||
80 | 32 | public function find($params = [], $oneItem = false) |
|
81 | { |
||
82 | 32 | $query = []; |
|
83 | |||
84 | 32 | if (is_string($params) && 1 * $params == $params) { |
|
85 | 1 | $params = 1 * $params; |
|
86 | } |
||
87 | |||
88 | 32 | if (is_int($params)) { |
|
89 | 8 | if (isset($this->keyMap[$params])) { |
|
90 | 2 | return $this->entities[$this->keyMap[$params]]; |
|
91 | } |
||
92 | $query = [ |
||
93 | 6 | 'id' => $params, |
|
94 | ]; |
||
95 | 6 | $oneItem = true; |
|
96 | } |
||
97 | |||
98 | 32 | if ($params instanceof Contracts\Entity) { |
|
99 | 2 | $params = [$params]; |
|
100 | } |
||
101 | |||
102 | 32 | if (is_array($params)) { |
|
103 | 32 | foreach ($params as $key => $value) { |
|
104 | 32 | if (is_numeric($key) && $value instanceof Contracts\Entity) { |
|
105 | 2 | $type = $this->type->getManager()->findRepository($value)->getType(); |
|
106 | 2 | $key = $this->type->getReferenceProperty($type); |
|
107 | } |
||
108 | 32 | if ($this->type->hasProperty($key)) { |
|
109 | 32 | $query[$key] = $this->type->encodeProperty($key, $value); |
|
110 | } |
||
111 | } |
||
112 | } |
||
113 | |||
114 | 32 | $findKey = md5(json_encode($query)); |
|
115 | 32 | if (array_key_exists($findKey, $this->findCache)) { |
|
116 | 32 | return $this->findCache[$findKey]; |
|
117 | } |
||
118 | |||
119 | 32 | $index = $this->type->findIndex(array_keys($query)); |
|
120 | 32 | if(!is_numeric($index)) { |
|
121 | 1 | throw new \Exception("No index found for " . json_encode(array_keys($query))); |
|
122 | } |
||
123 | 32 | $values = count($query) ? $this->type->getIndexTuple($index, $query) : []; |
|
124 | |||
125 | 32 | $data = $this->type->getSpace()->select($values, $index); |
|
126 | |||
127 | 32 | $result = []; |
|
128 | 32 | if (!empty($data->getData())) { |
|
129 | 32 | foreach ($data->getData() as $tuple) { |
|
130 | 32 | $data = $this->type->fromTuple($tuple); |
|
131 | 32 | if (isset($data['id']) && array_key_exists($data['id'], $this->keyMap)) { |
|
132 | 32 | $entity = $this->entities[$this->keyMap[$data['id']]]; |
|
133 | 32 | $entity->update($data); |
|
134 | } else { |
||
135 | 32 | $entity = new Entity($data); |
|
136 | 32 | $this->register($entity); |
|
137 | } |
||
138 | 32 | if ($oneItem) { |
|
139 | 32 | return $this->findCache[$findKey] = $entity; |
|
140 | } |
||
141 | 6 | $result[] = $entity; |
|
142 | } |
||
143 | } |
||
144 | 32 | if (!$oneItem) { |
|
145 | 6 | return $this->findCache[$findKey] = $result; |
|
146 | } |
||
147 | 32 | } |
|
148 | |||
149 | /** |
||
150 | * @return Entity |
||
151 | */ |
||
152 | 32 | public function knows(Contracts\Entity $entity) |
|
156 | |||
157 | 1 | public function remove(Contracts\Entity $entity) |
|
164 | |||
165 | 32 | public function save(Contracts\Entity $entity) |
|
166 | { |
||
167 | 32 | if (!$this->knows($entity)) { |
|
168 | 1 | throw new LogicException('Entity is not related with this repository'); |
|
194 | |||
195 | 32 | private function register(Contracts\Entity $entity) |
|
206 | |||
207 | 32 | private function generateId(Contracts\Entity $entity) |
|
234 | |||
235 | 4 | public function getType() |
|
239 | } |
||
240 |