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 |