| Total Complexity | 53 |
| Total Lines | 393 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like ReleaseBrowseService 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.
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 ReleaseBrowseService, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 15 | class ReleaseBrowseService |
||
| 16 | { |
||
| 17 | private const CACHE_VERSION_KEY = 'releases:cache_version'; |
||
| 18 | |||
| 19 | // RAR/ZIP Password indicator. |
||
| 20 | public const PASSWD_NONE = 0; // No password. |
||
| 21 | public const PASSWD_RAR = 1; // Definitely passworded. |
||
| 22 | |||
| 23 | public function __construct() |
||
| 24 | { |
||
| 25 | } |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Used for Browse results. |
||
| 29 | * |
||
| 30 | * @return Collection|mixed |
||
| 31 | */ |
||
| 32 | public function getBrowseRange($page, $cat, $start, $num, $orderBy, int $maxAge = -1, array $excludedCats = [], int|string $groupName = -1, int $minSize = 0): mixed |
||
| 33 | { |
||
| 34 | $cacheVersion = $this->getCacheVersion(); |
||
| 35 | $page = max(1, $page); |
||
| 36 | $start = max(0, $start); |
||
| 37 | |||
| 38 | $orderBy = $this->getBrowseOrder($orderBy); |
||
| 39 | |||
| 40 | $qry = sprintf( |
||
| 41 | "SELECT r.id, r.searchname, r.groups_id, r.guid, r.postdate, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, r.nfostatus, cp.title AS parent_category, c.title AS sub_category, r.group_name, |
||
| 42 | CONCAT(cp.title, ' > ', c.title) AS category_name, |
||
| 43 | CONCAT(cp.id, ',', c.id) AS category_ids, |
||
| 44 | df.failed AS failed, |
||
| 45 | rn.releases_id AS nfoid, |
||
| 46 | re.releases_id AS reid, |
||
| 47 | v.tvdb, v.trakt, v.tvrage, v.tvmaze, v.imdb, v.tmdb, |
||
| 48 | m.imdbid, m.tmdbid, m.traktid, |
||
| 49 | tve.title, tve.firstaired |
||
| 50 | FROM |
||
| 51 | ( |
||
| 52 | SELECT r.id, r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, r.nfostatus, g.name AS group_name, r.movieinfo_id |
||
| 53 | FROM releases r |
||
| 54 | LEFT JOIN usenet_groups g ON g.id = r.groups_id |
||
| 55 | WHERE r.passwordstatus %s |
||
| 56 | %s %s %s %s %s |
||
| 57 | ORDER BY %s %s %s |
||
| 58 | ) r |
||
| 59 | LEFT JOIN categories c ON c.id = r.categories_id |
||
| 60 | LEFT JOIN root_categories cp ON cp.id = c.root_categories_id |
||
| 61 | LEFT OUTER JOIN videos v ON r.videos_id = v.id |
||
| 62 | LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id |
||
| 63 | LEFT OUTER JOIN movieinfo m ON m.id = r.movieinfo_id |
||
| 64 | LEFT OUTER JOIN video_data re ON re.releases_id = r.id |
||
| 65 | LEFT OUTER JOIN release_nfos rn ON rn.releases_id = r.id |
||
| 66 | LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id |
||
| 67 | GROUP BY r.id |
||
| 68 | ORDER BY %7\$s %8\$s", |
||
| 69 | $this->showPasswords(), |
||
| 70 | Category::getCategorySearch($cat), |
||
| 71 | ($maxAge > 0 ? (' AND postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''), |
||
| 72 | (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : ''), |
||
| 73 | ((int) $groupName !== -1 ? sprintf(' AND g.name = %s ', escapeString($groupName)) : ''), |
||
| 74 | ($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''), |
||
| 75 | $orderBy[0], |
||
| 76 | $orderBy[1], |
||
| 77 | ($start === 0 ? ' LIMIT '.$num : ' LIMIT '.$num.' OFFSET '.$start) |
||
| 78 | ); |
||
| 79 | |||
| 80 | $cacheKey = md5($cacheVersion.$qry.$page); |
||
| 81 | $releases = Cache::get($cacheKey); |
||
| 82 | if ($releases !== null) { |
||
| 83 | return $releases; |
||
| 84 | } |
||
| 85 | $sql = DB::select($qry); |
||
| 86 | if (\count($sql) > 0) { |
||
| 87 | $possibleRows = $this->getBrowseCount($cat, $maxAge, $excludedCats, $groupName); |
||
| 88 | $sql[0]->_totalcount = $sql[0]->_totalrows = $possibleRows; |
||
| 89 | } |
||
| 90 | $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_medium')); |
||
| 91 | Cache::put($cacheKey, $sql, $expiresAt); |
||
| 92 | |||
| 93 | return $sql; |
||
| 94 | } |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Used for pager on browse page. |
||
| 98 | */ |
||
| 99 | public function getBrowseCount(array $cat, int $maxAge = -1, array $excludedCats = [], int|string $groupName = ''): int |
||
| 100 | { |
||
| 101 | return $this->getPagerCount(sprintf( |
||
| 102 | 'SELECT COUNT(r.id) AS count |
||
| 103 | FROM releases r |
||
| 104 | %s |
||
| 105 | WHERE r.passwordstatus %s |
||
| 106 | %s |
||
| 107 | %s %s %s ', |
||
| 108 | ($groupName !== -1 ? 'LEFT JOIN usenet_groups g ON g.id = r.groups_id' : ''), |
||
| 109 | $this->showPasswords(), |
||
| 110 | ($groupName !== -1 ? sprintf(' AND g.name = %s', escapeString($groupName)) : ''), |
||
| 111 | Category::getCategorySearch($cat), |
||
| 112 | ($maxAge > 0 ? (' AND r.postdate > NOW() - INTERVAL '.$maxAge.' DAY ') : ''), |
||
| 113 | (\count($excludedCats) ? (' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')') : '') |
||
| 114 | )); |
||
| 115 | } |
||
| 116 | |||
| 117 | /** |
||
| 118 | * Get the passworded releases clause. |
||
| 119 | */ |
||
| 120 | public function showPasswords(): string |
||
| 121 | { |
||
| 122 | $show = (int) Settings::settingValue('showpasswordedrelease'); |
||
| 123 | $setting = $show ?? 0; |
||
| 124 | |||
| 125 | return match ($setting) { |
||
| 126 | 1 => '<= '.self::PASSWD_RAR, |
||
| 127 | default => '= '.self::PASSWD_NONE, |
||
| 128 | }; |
||
| 129 | } |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Use to order releases on site. |
||
| 133 | */ |
||
| 134 | public function getBrowseOrder(array|string $orderBy): array |
||
| 135 | { |
||
| 136 | $orderArr = explode('_', ($orderBy === '' ? 'posted_desc' : $orderBy)); |
||
| 137 | $orderField = match ($orderArr[0]) { |
||
| 138 | 'cat' => 'categories_id', |
||
| 139 | 'name' => 'searchname', |
||
| 140 | 'size' => 'size', |
||
| 141 | 'files' => 'totalpart', |
||
| 142 | 'stats' => 'grabs', |
||
| 143 | default => 'postdate', |
||
| 144 | }; |
||
| 145 | |||
| 146 | return [$orderField, isset($orderArr[1]) && preg_match('/^(asc|desc)$/i', $orderArr[1]) ? $orderArr[1] : 'desc']; |
||
| 147 | } |
||
| 148 | |||
| 149 | /** |
||
| 150 | * Return ordering types usable on site. |
||
| 151 | * |
||
| 152 | * @return string[] |
||
| 153 | */ |
||
| 154 | public function getBrowseOrdering(): array |
||
| 155 | { |
||
| 156 | return [ |
||
| 157 | 'name_asc', |
||
| 158 | 'name_desc', |
||
| 159 | 'cat_asc', |
||
| 160 | 'cat_desc', |
||
| 161 | 'posted_asc', |
||
| 162 | 'posted_desc', |
||
| 163 | 'size_asc', |
||
| 164 | 'size_desc', |
||
| 165 | 'files_asc', |
||
| 166 | 'files_desc', |
||
| 167 | 'stats_asc', |
||
| 168 | 'stats_desc', |
||
| 169 | ]; |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * @return \Illuminate\Cache\|\Illuminate\Database\Eloquent\Collection|mixed |
||
| 174 | */ |
||
| 175 | public function getShowsRange($userShows, $offset, $limit, $orderBy, int $maxAge = -1, array $excludedCats = []) |
||
| 176 | { |
||
| 177 | $orderBy = $this->getBrowseOrder($orderBy); |
||
| 178 | $sql = sprintf( |
||
| 179 | "SELECT r.id, r.searchname, r.guid, r.postdate, r.groups_id, r.categories_id, r.size, r.totalpart, r.fromname, r.passwordstatus, r.grabs, r.comments, r.adddate, r.videos_id, r.tv_episodes_id, r.haspreview, r.jpgstatus, cp.title AS parent_category, c.title AS sub_category, |
||
| 180 | CONCAT(cp.title, '->', c.title) AS category_name |
||
| 181 | FROM releases r |
||
| 182 | LEFT JOIN categories c ON c.id = r.categories_id |
||
| 183 | LEFT JOIN root_categories cp ON cp.id = c.root_categories_id |
||
| 184 | WHERE %s %s |
||
| 185 | AND r.categories_id BETWEEN %d AND %d |
||
| 186 | AND r.passwordstatus %s |
||
| 187 | %s |
||
| 188 | GROUP BY r.id |
||
| 189 | ORDER BY %s %s %s", |
||
| 190 | $this->uSQL($userShows, 'videos_id'), |
||
| 191 | (! empty($excludedCats) ? ' AND r.categories_id NOT IN ('.implode(',', $excludedCats).')' : ''), |
||
| 192 | Category::TV_ROOT, |
||
| 193 | Category::TV_OTHER, |
||
| 194 | $this->showPasswords(), |
||
| 195 | ($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : ''), |
||
| 196 | $orderBy[0], |
||
| 197 | $orderBy[1], |
||
| 198 | ($offset === false ? '' : (' LIMIT '.$limit.' OFFSET '.$offset)) |
||
| 199 | ); |
||
| 200 | $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); |
||
| 201 | $result = Cache::get(md5($sql)); |
||
| 202 | if ($result !== null) { |
||
| 203 | return $result; |
||
| 204 | } |
||
| 205 | $result = Release::fromQuery($sql); |
||
| 206 | Cache::put(md5($sql), $result, $expiresAt); |
||
| 207 | |||
| 208 | return $result; |
||
| 209 | } |
||
| 210 | |||
| 211 | public function getShowsCount($userShows, int $maxAge = -1, array $excludedCats = []): int |
||
| 227 | ) |
||
| 228 | ); |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * Creates part of a query for some functions. |
||
| 233 | */ |
||
| 234 | public function uSQL(Collection|array $userQuery, string $type): string |
||
| 235 | { |
||
| 236 | $sql = '(1=2 '; |
||
| 237 | foreach ($userQuery as $query) { |
||
| 238 | $sql .= sprintf('OR (r.%s = %d', $type, $query->$type); |
||
| 239 | if (! empty($query->categories)) { |
||
| 240 | $catsArr = explode('|', $query->categories); |
||
| 241 | if (\count($catsArr) > 1) { |
||
| 242 | $sql .= sprintf(' AND r.categories_id IN (%s)', implode(',', $catsArr)); |
||
| 243 | } else { |
||
| 244 | $sql .= sprintf(' AND r.categories_id = %d', $catsArr[0]); |
||
| 245 | } |
||
| 246 | } |
||
| 247 | $sql .= ') '; |
||
| 248 | } |
||
| 249 | $sql .= ') '; |
||
| 250 | |||
| 251 | return $sql; |
||
| 252 | } |
||
| 253 | |||
| 254 | public static function bumpCacheVersion(): void |
||
| 255 | { |
||
| 256 | $current = Cache::get(self::CACHE_VERSION_KEY, 1); |
||
| 257 | Cache::forever(self::CACHE_VERSION_KEY, $current + 1); |
||
| 258 | } |
||
| 259 | |||
| 260 | private function getCacheVersion(): int |
||
| 261 | { |
||
| 262 | return Cache::get(self::CACHE_VERSION_KEY, 1); |
||
| 263 | } |
||
| 264 | |||
| 265 | /** |
||
| 266 | * Get the count of releases for pager. |
||
| 267 | * |
||
| 268 | * @param string $query The query to get the count from. |
||
| 269 | */ |
||
| 270 | private function getPagerCount(string $query): int |
||
| 408 | } |
||
| 409 | } |
||
| 410 | } |
||
| 411 | |||
| 412 |