Complex classes like Tracker_Admin_Model 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 Tracker_Admin_Model, and based on these observations, apply Extract Interface, too.
| 1 | <?php declare(strict_types=1); defined('BASEPATH') OR exit('No direct script access allowed'); |
||
| 3 | class Tracker_Admin_Model extends Tracker_Base_Model { |
||
| 4 | 127 | public function __construct() { |
|
| 7 | |||
| 8 | /** |
||
| 9 | * Checks for any series that haven't updated in 16 hours and updates them. |
||
| 10 | * This is ran every 4 hours via a cron job. |
||
| 11 | */ |
||
| 12 | public function updateLatestChapters() { |
||
| 13 | // @formatter:off |
||
| 14 | $query = $this->db |
||
| 15 | ->select(' |
||
| 16 | tracker_titles.id as title_id, |
||
| 17 | tracker_titles.title, |
||
| 18 | tracker_titles.title_url, |
||
| 19 | tracker_titles.status, |
||
| 20 | tracker_sites.site, |
||
| 21 | tracker_sites.site_class, |
||
| 22 | tracker_sites.status, |
||
| 23 | tracker_titles.latest_chapter, |
||
| 24 | tracker_titles.last_updated, |
||
| 25 | from_unixtime(MAX(auth_users.last_login)) AS timestamp |
||
| 26 | ') |
||
| 27 | ->from('tracker_titles') |
||
| 28 | ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left') |
||
| 29 | ->join('tracker_chapters', 'tracker_titles.id = tracker_chapters.title_id', 'left') |
||
| 30 | ->join('auth_users', 'tracker_chapters.user_id = auth_users.id', 'left') |
||
| 31 | ->where('tracker_sites.status', 'enabled') |
||
| 32 | ->group_start() |
||
| 33 | //Check if title is marked as on-going... |
||
| 34 | ->where('tracker_titles.status', 0) |
||
| 35 | //AND matches one of where queries below |
||
| 36 | ->group_start() |
||
| 37 | //Then check if it's NULL (only occurs for new series) |
||
| 38 | ->where('latest_chapter', NULL) |
||
| 39 | //OR if it hasn't updated within the past 12 hours AND isn't a custom update site |
||
| 40 | ->or_group_start() |
||
| 41 | ->where('tracker_sites.use_custom', 'N') |
||
| 42 | ->where('last_checked < DATE_SUB(NOW(), INTERVAL 12 HOUR)') |
||
| 43 | ->group_end() |
||
| 44 | //OR it is a custom update site, has more than one follower and hasn't updated within the past 72 hours. |
||
| 45 | ->or_group_start() |
||
| 46 | ->where('tracker_titles.id IN ( |
||
| 47 | SELECT title_id |
||
| 48 | FROM tracker_chapters |
||
| 49 | GROUP BY title_id |
||
| 50 | HAVING COUNT(title_id) > 1 |
||
| 51 | )', NULL, FALSE) |
||
| 52 | |||
| 53 | ->where('last_checked < DATE_SUB(NOW(), INTERVAL 72 HOUR)') |
||
| 54 | ->group_end() |
||
| 55 | //OR it is a custom update site and hasn't updated within the past 120 hours (5 days) |
||
| 56 | ->or_where('last_checked < DATE_SUB(NOW(), INTERVAL 120 HOUR)') |
||
| 57 | ->group_end() |
||
| 58 | ->group_end() |
||
| 59 | ->or_group_start() |
||
| 60 | //Check if title is marked as complete... |
||
| 61 | ->where('tracker_titles.status', 1) |
||
| 62 | //Then check if it hasn't updated within the past week |
||
| 63 | ->where('last_checked < DATE_SUB(NOW(), INTERVAL 1 WEEK)') |
||
| 64 | ->group_end() |
||
| 65 | //Status 2 (One-shot) & 255 (Ignore) are both not updated intentionally. |
||
| 66 | ->group_by('tracker_titles.id, tracker_chapters.active') |
||
| 67 | //Check if the series is actually being tracked by someone |
||
| 68 | ->having('timestamp IS NOT NULL') |
||
| 69 | //AND if it's currently marked as active by the user |
||
| 70 | ->having('tracker_chapters.active', 'Y') |
||
| 71 | //AND if they have been active in the last 120 hours (5 days) |
||
| 72 | ->having('timestamp > DATE_SUB(NOW(), INTERVAL 120 HOUR)') |
||
| 73 | ->order_by('tracker_titles.title', 'ASC') |
||
| 74 | ->get(); |
||
| 75 | // @formatter:on |
||
| 76 | |||
| 77 | if($query->num_rows() > 0) { |
||
| 78 | foreach ($query->result() as $row) { |
||
| 79 | print "> {$row->title} <{$row->site_class} - {$row->title_url}> | <{$row->title_id}>"; //Print this prior to doing anything so we can more easily find out if something went wrong |
||
| 80 | $titleData = $this->sites->{$row->site_class}->getTitleData($row->title_url); |
||
| 81 | if(is_array($titleData) && (!is_null($titleData['latest_chapter']) || $this->sites->{$row->site_class}->canHaveNoChapters)) { |
||
| 82 | if(count($titleData) === 3) { |
||
| 83 | // Normal update. |
||
| 84 | |||
| 85 | //FIXME: "At the moment" we don't seem to be doing anything with TitleData['last_updated']. |
||
| 86 | // Should we even use this? Y/N |
||
| 87 | if($this->Tracker->title->updateByID((int) $row->title_id, $titleData['latest_chapter'])) { |
||
| 88 | //Make sure last_checked is always updated on successful run. |
||
| 89 | //CHECK: Is there a reason we aren't just doing this in updateByID? |
||
| 90 | $this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE) |
||
| 91 | ->where('id', $row->title_id) |
||
| 92 | ->update('tracker_titles'); |
||
| 93 | |||
| 94 | print " - ({$titleData['latest_chapter']})\n"; |
||
| 95 | } else { |
||
| 96 | log_message('error', "{$row->title} failed to update successfully"); |
||
| 97 | |||
| 98 | print " - Something went wrong?\n"; |
||
| 99 | } |
||
| 100 | } else { |
||
| 101 | // No chapters were returned, but site allows this. |
||
| 102 | if($this->Tracker->title->updateByID((int) $row->title_id, NULL)) { |
||
| 103 | //Make sure last_checked is always updated on successful run. |
||
| 104 | //CHECK: Is there a reason we aren't just doing this in updateByID? |
||
| 105 | $this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE) |
||
| 106 | ->where('id', $row->title_id) |
||
| 107 | ->update('tracker_titles'); |
||
| 108 | |||
| 109 | print " - (No chapters found?)\n"; |
||
| 110 | } else { |
||
| 111 | log_message('error', "{$row->title} failed to update successfully"); |
||
| 112 | |||
| 113 | print " - Something went wrong?\n"; |
||
| 114 | } |
||
| 115 | } |
||
| 116 | } else { |
||
| 117 | log_message('error', "{$row->title} failed to update successfully"); |
||
| 118 | $this->Tracker->title->updateFailedChecksByID((int) $row->title_id); |
||
| 119 | |||
| 120 | print " - FAILED TO PARSE\n"; |
||
| 121 | } |
||
| 122 | } |
||
| 123 | } |
||
| 124 | } |
||
| 125 | |||
| 126 | /** |
||
| 127 | * Intended to be only used as a quick way to update all series on a site after a bug. |
||
| 128 | * |
||
| 129 | * @param string $site |
||
| 130 | * @param null|string $last_checked |
||
| 131 | */ |
||
| 132 | public function updateAllTitlesBySite(string $site, ?string $last_checked = NULL) { |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Checks for any sites which support custom updating (usually via following lists) and updates them. |
||
| 206 | * This is run hourly. |
||
| 207 | */ |
||
| 208 | public function updateCustom() { |
||
| 257 | |||
| 258 | public function refollowCustom() { |
||
| 291 | |||
| 292 | /** |
||
| 293 | * Checks every series to see if title has changed, and update if so. |
||
| 294 | * This is ran once a month via a cron job |
||
| 295 | */ |
||
| 296 | public function updateTitles() { |
||
| 347 | |||
| 348 | public function incrementRequests() : void { |
||
| 369 | |||
| 370 | public function getNextUpdateTime(string $format = "%H:%I:%S") : string { |
||
| 403 | } |
||
| 404 |