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 |