Complex classes like FileRepository 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 FileRepository, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class FileRepository implements RepositoryInterface |
||
29 | { |
||
30 | /** |
||
31 | * File where all rates are persisted. |
||
32 | * |
||
33 | * @var string |
||
34 | */ |
||
35 | protected $pathToFile; |
||
36 | |||
37 | /** |
||
38 | * Collection of loaded rates. |
||
39 | * |
||
40 | * @var array |
||
41 | */ |
||
42 | protected $rates; |
||
43 | |||
44 | /** |
||
45 | * Collection of latest rates (to speed up search process). |
||
46 | * |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $latest; |
||
50 | |||
51 | 10 | public function __construct($pathToFile) |
|
52 | { |
||
53 | 10 | $this->pathToFile = $pathToFile; |
|
54 | 10 | $this->initStorage(); |
|
55 | 10 | $this->load(); |
|
56 | 10 | } |
|
57 | |||
58 | /** |
||
59 | * {@inheritdoc} |
||
60 | */ |
||
61 | 10 | public function save(array $rates) |
|
62 | { |
||
63 | /** |
||
64 | * @var RateInterface $rate |
||
65 | */ |
||
66 | 10 | foreach ($rates as $rate) { |
|
67 | 10 | $this->rates[$this->getRateKey($rate)] = $rate; |
|
68 | 10 | } |
|
69 | |||
70 | 10 | usort($this->rates, function(RateInterface $rate1, RateInterface $rate2) { |
|
71 | 6 | return ($rate1->getDate() > $rate2->getDate()) ? -1 : 1; |
|
72 | 10 | }); |
|
73 | |||
74 | 10 | $data = ''; |
|
75 | |||
76 | /** |
||
77 | * @var RateInterface $rate |
||
78 | */ |
||
79 | 10 | foreach ($this->rates as $rate) { |
|
80 | 10 | $data .= $this->toJson($rate) . "\n"; |
|
81 | 10 | } |
|
82 | |||
83 | 10 | file_put_contents($this->pathToFile, $data, LOCK_EX); |
|
84 | |||
85 | 10 | $this->load(); |
|
86 | 10 | } |
|
87 | |||
88 | /** |
||
89 | * {@inheritdoc} |
||
90 | */ |
||
91 | 2 | public function delete(array $rates) |
|
92 | { |
||
93 | /** |
||
94 | * @var RateInterface $rate |
||
95 | */ |
||
96 | 2 | foreach ($rates as $rate) { |
|
97 | 2 | unset($this->rates[$this->getRateKey($rate)]); |
|
98 | 2 | } |
|
99 | |||
100 | 2 | $this->save(array()); |
|
101 | 2 | } |
|
102 | |||
103 | /** |
||
104 | * {@inheritdoc} |
||
105 | */ |
||
106 | 4 | public function has($sourceName, $currencyCode, \DateTime $date = null, $rateType = 'default') |
|
107 | { |
||
108 | 4 | if ($date === null) { |
|
109 | 4 | $date = new \DateTime('now'); |
|
110 | 4 | } |
|
111 | |||
112 | 4 | return array_key_exists($this->getRateKey($currencyCode, $date, $rateType, $sourceName), $this->rates); |
|
113 | } |
||
114 | |||
115 | /** |
||
116 | * {@inheritdoc} |
||
117 | */ |
||
118 | 2 | public function get($sourceName, $currencyCode, \DateTime $date = null, $rateType = 'default') |
|
119 | { |
||
120 | 2 | if ($date === null) { |
|
121 | 2 | $date = new \DateTime('now'); |
|
122 | 2 | } |
|
123 | |||
124 | 2 | if ($this->has($sourceName, $currencyCode, $date, $rateType)) { |
|
125 | 2 | return $this->rates[$this->getRateKey($currencyCode, $date, $rateType, $sourceName)]; |
|
126 | } |
||
127 | |||
128 | throw new ExchangeRateException(sprintf('Could not fetch rate for rate currency code "%s" and rate type "%s" on date "%s".', $currencyCode, $rateType, $date->format('Y-m-d'))); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * {@inheritdoc} |
||
133 | */ |
||
134 | 2 | public function latest($sourceName, $currencyCode, $rateType = 'default') |
|
135 | { |
||
136 | /** |
||
137 | * @var RateInterface $rate |
||
138 | */ |
||
139 | 2 | foreach ($this->rates as $rate) { |
|
140 | |||
141 | if ( |
||
142 | 2 | $rate->getSourceName() === $sourceName |
|
143 | 2 | && |
|
144 | 2 | $rate->getCurrencyCode() === $currencyCode |
|
145 | 2 | && |
|
146 | 2 | $rate->getRateType() === $rateType |
|
147 | 2 | ) { |
|
148 | 2 | return $rate; |
|
149 | } |
||
150 | } |
||
151 | |||
152 | throw new ExchangeRateException(sprintf('Could not fetch latest rate for rate currency code "%s" and rate type "%s" from source "%s".', $currencyCode, $rateType, $sourceName)); |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * {@inheritdoc} |
||
157 | */ |
||
158 | 4 | public function all(array $criteria = array()) |
|
178 | |||
179 | /** |
||
180 | * {@inheritdoc} |
||
181 | */ |
||
182 | 2 | public function count() |
|
186 | |||
187 | /** |
||
188 | * Load all rates from file. |
||
189 | * |
||
190 | * @return RateInterface[] |
||
191 | */ |
||
192 | 10 | protected function load() |
|
222 | |||
223 | /** |
||
224 | * Builds rate key to speed up search. |
||
225 | * |
||
226 | * @param null $currencyCode |
||
227 | * @param null $date |
||
228 | * @param null $rateType |
||
229 | * @param null $sourceName |
||
230 | * @return string |
||
231 | */ |
||
232 | 10 | protected function getRateKey($currencyCode = null, $date = null, $rateType = null, $sourceName = null) |
|
233 | { |
||
234 | 10 | if ($currencyCode instanceof RateInterface) { |
|
235 | 10 | $date = $currencyCode->getDate(); |
|
236 | 10 | $rateType = $currencyCode->getRateType(); |
|
237 | 10 | $sourceName = $currencyCode->getSourceName(); |
|
238 | 10 | $currencyCode = $currencyCode->getCurrencyCode(); |
|
239 | 10 | } |
|
240 | |||
241 | 10 | return str_replace( |
|
242 | 10 | array('%currency_code%', '%date%', '%rate_type%', '%source_name%'), |
|
243 | 10 | array($currencyCode, $date->format('Y-m-d'), $rateType, $sourceName), |
|
244 | '%currency_code%_%date%_%rate_type%_%source_name%' |
||
245 | 10 | ); |
|
246 | } |
||
247 | |||
248 | /** |
||
249 | * Initializes file storage. |
||
250 | */ |
||
251 | 10 | protected function initStorage() |
|
270 | |||
271 | /** |
||
272 | * Serialize rate to JSON string. |
||
273 | * |
||
274 | * @param RateInterface $rate Rate to serialize. |
||
275 | * @return string JSON representation of rate. |
||
276 | */ |
||
277 | 10 | protected function toJson(RateInterface $rate) |
|
290 | |||
291 | /** |
||
292 | * Deserialize JSON string to Rate |
||
293 | * |
||
294 | * @param string $json Serialized rate. |
||
295 | * @return Rate Deserialized rate. |
||
296 | */ |
||
297 | 10 | protected function fromJson($json) |
|
312 | |||
313 | /** |
||
314 | * Extract requested page from filter criteria. |
||
315 | * |
||
316 | * @param array $rates Rates to filter for pagination. |
||
317 | * @param array $criteria Filter criteria. |
||
318 | * @return RateInterface[] Paginated rates. |
||
319 | */ |
||
320 | 4 | protected function paginate(array $rates, $criteria) |
|
336 | } |
||
337 |