Complex classes like FeedService 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 FeedService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class FeedService extends Service { |
||
33 | |||
34 | private $feedFetcher; |
||
35 | private $itemMapper; |
||
36 | private $feedMapper; |
||
37 | private $logger; |
||
38 | private $l10n; |
||
39 | private $timeFactory; |
||
40 | private $autoPurgeMinimumInterval; |
||
41 | private $purifier; |
||
42 | private $loggerParams; |
||
43 | |||
44 | public function __construct(FeedMapper $feedMapper, |
||
65 | |||
66 | /** |
||
67 | * Finds all feeds of a user |
||
68 | * @param string $userId the name of the user |
||
69 | * @return Feed[] |
||
70 | */ |
||
71 | public function findAll($userId){ |
||
74 | |||
75 | |||
76 | /** |
||
77 | * Finds all feeds from all users |
||
78 | * @return array of feeds |
||
79 | */ |
||
80 | public function findAllFromAllUsers() { |
||
83 | |||
84 | |||
85 | /** |
||
86 | * Creates a new feed |
||
87 | * @param string $feedUrl the url to the feed |
||
88 | * @param int $folderId the folder where it should be put into, 0 for root |
||
89 | * folder |
||
90 | * @param string $userId for which user the feed should be created |
||
91 | * @param string $title if given, this is used for the opml feed title |
||
92 | * @param string $basicAuthUser if given, basic auth is set for this feed |
||
93 | * @param string $basicAuthPassword if given, basic auth is set for this |
||
94 | * feed. Ignored if user is null or an empty string |
||
95 | * @throws ServiceConflictException if the feed exists already |
||
96 | * @throws ServiceNotFoundException if the url points to an invalid feed |
||
97 | * @return Feed the newly created feed |
||
98 | */ |
||
99 | public function create($feedUrl, $folderId, $userId, $title=null, |
||
159 | |||
160 | |||
161 | /** |
||
162 | * Runs all the feed updates |
||
163 | */ |
||
164 | public function updateAll(){ |
||
179 | |||
180 | |||
181 | /** |
||
182 | * Updates a single feed |
||
183 | * @param int $feedId the id of the feed that should be updated |
||
184 | * @param string $userId the id of the user |
||
185 | * @param bool $forceUpdate update even if the article exists already |
||
186 | * @throws ServiceNotFoundException if the feed does not exist |
||
187 | * @return Feed the updated feed entity |
||
188 | */ |
||
189 | public function update($feedId, $userId, $forceUpdate=false){ |
||
190 | $existingFeed = $this->find($feedId, $userId); |
||
191 | |||
192 | if($existingFeed->getPreventUpdate() === true) { |
||
193 | return $existingFeed; |
||
194 | } |
||
195 | |||
196 | // for backwards compability it can be that the location is not set |
||
197 | // yet, if so use the url |
||
198 | $location = $existingFeed->getLocation(); |
||
199 | if (!$location) { |
||
200 | $location = $existingFeed->getUrl(); |
||
201 | } |
||
202 | |||
203 | try { |
||
204 | list($fetchedFeed, $items) = $this->feedFetcher->fetch( |
||
205 | $location, |
||
206 | false, |
||
207 | $existingFeed->getHttpLastModified(), |
||
208 | $existingFeed->getHttpEtag(), |
||
209 | $existingFeed->getFullTextEnabled(), |
||
210 | $existingFeed->getBasicAuthUser(), |
||
211 | $existingFeed->getBasicAuthPassword() |
||
212 | ); |
||
213 | |||
214 | // if there is no feed it means that no update took place |
||
215 | if (!$fetchedFeed) { |
||
216 | return $existingFeed; |
||
217 | } |
||
218 | |||
219 | // update number of articles on every feed update |
||
220 | $itemCount = count($items); |
||
221 | |||
222 | // this is needed to adjust to updates that add more items |
||
223 | // than when the feed was created. You can't update the count |
||
224 | // if it's lower because it may be due to the caching headers |
||
225 | // that were sent as the request and it might cause unwanted |
||
226 | // deletion and reappearing of feeds |
||
227 | if ($itemCount > $existingFeed->getArticlesPerUpdate()) { |
||
228 | $existingFeed->setArticlesPerUpdate($itemCount); |
||
229 | } |
||
230 | |||
231 | $existingFeed->setHttpLastModified( |
||
232 | $fetchedFeed->getHttpLastModified()); |
||
233 | $existingFeed->setHttpEtag($fetchedFeed->getHttpEtag()); |
||
234 | $existingFeed->setLocation($fetchedFeed->getLocation()); |
||
235 | |||
236 | // insert items in reverse order because the first one is |
||
237 | // usually the newest item |
||
238 | for($i=$itemCount-1; $i>=0; $i--){ |
||
239 | $item = $items[$i]; |
||
240 | $item->setFeedId($existingFeed->getId()); |
||
241 | |||
242 | try { |
||
243 | $dbItem = $this->itemMapper->findByGuidHash( |
||
244 | $item->getGuidHash(), $feedId, $userId |
||
245 | ); |
||
246 | |||
247 | // in case of update |
||
248 | if ($forceUpdate || |
||
249 | $item->getPubDate() > $dbItem->getPubDate()) { |
||
250 | |||
251 | $dbItem->setTitle($item->getTitle()); |
||
252 | $dbItem->setUrl($item->getUrl()); |
||
253 | $dbItem->setAuthor($item->getAuthor()); |
||
254 | $dbItem->setSearchIndex($item->getSearchIndex()); |
||
255 | $dbItem->setRtl($item->getRtl()); |
||
256 | $dbItem->setLastModified($item->getLastModified()); |
||
257 | $dbItem->setPubDate($item->getPubDate()); |
||
258 | $dbItem->setEnclosureMime($item->getEnclosureMime()); |
||
259 | $dbItem->setEnclosureLink($item->getEnclosureLink()); |
||
260 | $dbItem->setBody( |
||
261 | $this->purifier->purify($item->getBody()) |
||
262 | ); |
||
263 | |||
264 | // update modes: 0 nothing, 1 set unread |
||
265 | if ($existingFeed->getUpdateMode() === 1) { |
||
266 | $dbItem->setUnread(); |
||
267 | } |
||
268 | |||
269 | $this->itemMapper->update($dbItem); |
||
270 | } |
||
271 | } catch(DoesNotExistException $ex){ |
||
272 | $item->setBody( |
||
273 | $this->purifier->purify($item->getBody()) |
||
274 | ); |
||
275 | $this->itemMapper->insert($item); |
||
276 | } |
||
277 | } |
||
278 | |||
279 | // mark feed as successfully updated |
||
280 | $existingFeed->setUpdateErrorCount(0); |
||
281 | $existingFeed->setLastUpdateError(''); |
||
282 | |||
283 | } catch(FetcherException $ex){ |
||
284 | $existingFeed->setUpdateErrorCount( |
||
285 | $existingFeed->getUpdateErrorCount()+1 |
||
286 | ); |
||
287 | $existingFeed->setLastUpdateError($ex->getMessage()); |
||
288 | } |
||
289 | |||
290 | $this->feedMapper->update($existingFeed); |
||
291 | |||
292 | return $this->find($feedId, $userId); |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * Import articles |
||
297 | * @param array $json the array with json |
||
298 | * @param string $userId the username |
||
299 | * @return Feed if one had to be created for nonexistent feeds |
||
300 | */ |
||
301 | public function importArticles($json, $userId) { |
||
362 | |||
363 | |||
364 | /** |
||
365 | * Use this to mark a feed as deleted. That way it can be un-deleted |
||
366 | * @param int $feedId the id of the feed that should be deleted |
||
367 | * @param string $userId the name of the user for security reasons |
||
368 | * @throws ServiceNotFoundException when feed does not exist |
||
369 | */ |
||
370 | public function markDeleted($feedId, $userId) { |
||
375 | |||
376 | |||
377 | /** |
||
378 | * Use this to undo a feed deletion |
||
379 | * @param int $feedId the id of the feed that should be restored |
||
380 | * @param string $userId the name of the user for security reasons |
||
381 | * @throws ServiceNotFoundException when feed does not exist |
||
382 | */ |
||
383 | public function unmarkDeleted($feedId, $userId) { |
||
388 | |||
389 | |||
390 | /** |
||
391 | * Deletes all deleted feeds |
||
392 | * @param string $userId if given it purges only feeds of that user |
||
393 | * @param boolean $useInterval defaults to true, if true it only purges |
||
394 | * entries in a given interval to give the user a chance to undo the |
||
395 | * deletion |
||
396 | */ |
||
397 | public function purgeDeleted($userId=null, $useInterval=true) { |
||
411 | |||
412 | |||
413 | /** |
||
414 | * Deletes all feeds of a user, delete items first since the user_id |
||
415 | * is not defined in there |
||
416 | * @param string $userId the name of the user |
||
417 | */ |
||
418 | public function deleteUser($userId) { |
||
421 | |||
422 | /** |
||
423 | * @param $feedId |
||
424 | * @param $userId |
||
425 | * @param $diff an array containing the fields to update, e.g.: |
||
426 | * [ |
||
427 | * 'ordering' => 1, |
||
428 | * 'fullTextEnabled' => true, |
||
429 | * 'pinned' => true, |
||
430 | * 'updateMode' => 0, |
||
431 | * 'title' => 'title' |
||
432 | * ] |
||
433 | * @throws ServiceNotFoundException if feed does not exist |
||
434 | */ |
||
435 | public function patch($feedId, $userId, $diff=[]) { |
||
454 | |||
455 | } |
||
456 |