*/ protected $guarded = []; /** * @return HasMany */ public function releases(): HasMany { return $this->hasMany(Release::class, 'categories_id'); } /** * @return BelongsTo */ public function parent(): BelongsTo { return $this->belongsTo(RootCategory::class, 'root_categories_id'); } public static function getRecentlyAdded(): mixed { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); $result = Cache::get(md5('RecentlyAdded')); if ($result !== null) { return $result; } $result = self::query() ->with('parent') ->where('r.adddate', '>', now()->subWeek()) ->select(['root_categories_id', DB::raw('COUNT(r.id) as count'), 'title']) ->join('releases as r', 'r.categories_id', '=', 'categories.id') ->groupBy('title') ->orderByDesc('count') ->get(); Cache::put(md5('RecentlyAdded'), $result, $expiresAt); return $result; } /** * @param array $cat Category IDs (list or associative) * @return array|string|null */ public static function getCategorySearch(array $cat = [], ?string $searchType = null, mixed $builder = false): string|array|null { // Generate a cache key based on the input parameters $cacheKey = 'cat_search_'.md5(serialize($cat).(string) ($searchType ?? '').($builder ? '1' : '0')); $cached = Cache::get($cacheKey); if ($cached !== null) { return $cached; } $categories = []; // if searchType is tv return TV categories (only if no specific categories provided) if ($searchType === 'tv' && (empty($cat) || $cat === [-1] || $cat === ['-1'])) { $cat = self::TV_GROUP; } // is searchType is movies return MOVIES categories (only if no specific categories provided) if ($searchType === 'movies' && (empty($cat) || $cat === [-1] || $cat === ['-1'])) { $cat = self::MOVIES_GROUP; } // Anime API/search: default to TV anime category when client sends cat=-1 (avoid unrestricted browse) if ($searchType === 'anime' && (empty($cat) || $cat === [-1] || $cat === ['-1'])) { $cat = [self::TV_ANIME]; } // If multiple categories were sent in a single array position, slice and add them if (isset($cat[0]) && is_string($cat[0]) && str_contains($cat[0], ',')) { $tmpcats = explode(',', $cat[0]); // Reset the category to the first comma separated value in the string $cat[0] = $tmpcats[0]; // Add the remaining categories in the string to the original array foreach (array_slice($tmpcats, 1) as $tmpcat) { $cat[] = $tmpcat; } } foreach ($cat as $category) { $categoryInt = (int) $category; if (is_numeric($category) && $categoryInt !== -1 && self::isParent($categoryInt)) { // Cache child category IDs for parent categories $childCacheKey = 'cat_children_'.$categoryInt; $children = Cache::get($childCacheKey); if ($children === null) { $children = RootCategory::find($categoryInt)->categories->pluck('id')->toArray(); Cache::put($childCacheKey, $children, now()->addHours(24)); } $categories = array_merge($categories, $children); } elseif (is_numeric($category) && $categoryInt > 0) { $categories[] = $categoryInt; } } $catCount = count($categories); if ($builder) { $result = match ($catCount) { 0 => null, 1 => $categories[0] !== -1 ? $categories : null, default => $categories, }; Cache::put($cacheKey, $result, now()->addHours(24)); return $result; } $result = match ($catCount) { 0 => 'AND 1=1', 1 => $categories[0] !== -1 ? ' AND r.categories_id = '.$categories[0] : '', default => ' AND r.categories_id IN ('.implode(', ', $categories).') ', }; Cache::put($cacheKey, $result, now()->addHours(24)); return $result; } /** * Returns a concatenated list of other categories. */ public static function getCategoryOthersGroup(): string { return implode( ',', self::OTHERS_GROUP ); } /** * @return mixed */ public static function getCategoryValue(mixed $category) { return \constant('self::'.$category); } /** * Check if category is parent. */ public static function isParent(mixed $cid): bool { // Cache the parent category check to avoid repeated DB queries $cacheKey = 'cat_is_parent_'.$cid; $cached = Cache::get($cacheKey); if ($cached !== null) { return $cached; } $ret = RootCategory::query()->where(['id' => $cid])->first(); $isParent = $ret !== null; // Cache for a long time since parent categories rarely change Cache::put($cacheKey, $isParent, now()->addHours(24)); return $isParent; } public static function getFlat(): mixed { return self::query()->get(); } /** * Get children of a parent category. * * * @return mixed */ public static function getChildren(mixed $categoryId) { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); $cacheKey = md5((string) $categoryId); $result = Cache::get($cacheKey); if ($result !== null) { return $result; } $result = RootCategory::find($categoryId)->categories; Cache::put($cacheKey, $result, $expiresAt); return $result; } /** * Get names of enabled parent categories. */ public static function getEnabledParentNames(): mixed { return RootCategory::query()->where('status', '=', 1)->get(['title']); } /** * Returns category ID's for site disabled categories. */ public static function getDisabledIDs(): mixed { return self::query() ->where('status', '=', 2) ->get(['id']); } /** * Get multiple categories. */ public static function getByIds(mixed $ids): mixed { if (\count($ids) > 0) { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); $idsKey = implode(',', array_map('strval', (array) $ids)); $result = Cache::get(md5($idsKey)); if ($result !== null) { return $result; } $result = self::query()->whereIn('id', $ids)->get(); Cache::put(md5($idsKey), $result, $expiresAt); return $result; } return false; } /** * Return the parent and category name from the supplied categoryID. */ public static function getNameByID(mixed $categoryId): string { $cat = self::query()->where('id', $categoryId)->first(); return $cat !== null ? $cat->parent->title.' -> '.$cat->title : ''; } /** * @return bool|mixed */ public static function getIdByName(mixed $title, mixed $parent) { $cat = self::query()->where('title', $title)->with('parent.'.$parent)->first(['id']); return $cat !== null ? $cat->id : false; } /** * Update a category. */ public static function updateCategory(mixed $id, mixed $status, mixed $desc, mixed $disablepreview, mixed $minsize, mixed $maxsize): int { return self::query()->where('id', $id)->update( [ 'disablepreview' => $disablepreview, 'status' => $status, 'minsizetoformrelease' => $minsize, 'maxsizetoformrelease' => $maxsize, 'description' => $desc, ] ); } /** * @param array $excludedCats * @return array */ public static function getForMenu(array $excludedCats = []): array { $categoriesResult = []; $categoriesArray = RootCategory::query()->with(['categories' => function ($query) use ($excludedCats) { if (! empty($excludedCats)) { $query->whereNotIn('id', $excludedCats); } $query->select(['id', 'title', 'root_categories_id', 'description']); }])->select(['id', 'title'])->get()->toArray(); foreach ($categoriesArray as $category) { if (! empty($category['categories'])) { $categoriesResult[] = $category; } } return $categoriesResult; } /** * @return Collection */ public static function getForApi(): Collection { $expiresAt = now()->addMinutes(config('nntmux.cache_expiry_long')); /** @var Collection|null $result */ $result = Cache::get(md5('ForApi')); if ($result !== null) { return $result; } $result = RootCategory::query()->select(['id', 'title'])->where('status', '=', self::STATUS_ACTIVE)->get(); Cache::put(md5('ForApi'), $result, $expiresAt); return $result; } /** * Return a list of categories for use in a dropdown. * * @return array */ public static function getForSelect(bool $blnIncludeNoneSelected = true): array { $categories = self::getCategories(); $temp_array = []; if ($blnIncludeNoneSelected) { $temp_array[-1] = '--Please Select--'; } foreach ($categories as $category) { /** @var Category $category */ $temp_array[$category->id] = $category->parent->title.' > '.$category->title; } return $temp_array; } /** * @param array $excludedCats * @return array */ public static function getCategories(bool $activeOnly = false, array $excludedCats = []): Collection // @phpstan-ignore missingType.generics { $sql = self::query() ->with('parent') ->select(['id', 'status', 'title', 'root_categories_id']) ->orderBy('id'); if ($activeOnly) { $sql->where('status', '=', self::STATUS_ACTIVE); } if (! empty($excludedCats)) { $sql->whereNotIn('id', $excludedCats); } return $sql->get(); } }