query('sort', 'latest'); $perPage = $this->resolvePerPage($request); $artworks = $this->artworks->browsePublicArtworks($perPage, $sort); $seo = $this->buildPaginationSeo($request, url('/browse'), $artworks); $mainCategories = $this->mainCategories(); return view('gallery.index', [ 'gallery_type' => 'browse', 'mainCategories' => $mainCategories, 'subcategories' => $mainCategories, 'contentType' => null, 'category' => null, 'artworks' => $artworks, 'hero_title' => 'Browse Artworks', 'hero_description' => 'List of all uploaded artworks across Skins, Wallpapers, Photography, and Other.', 'breadcrumbs' => collect(), 'page_title' => 'Browse Uploaded Artworks - Photography, Wallpapers and Skins at SkinBase', 'page_meta_description' => "Browse Uploaded Photography, Wallpapers and Skins to one of the world's oldest online social community for artists and art enthusiasts.", 'page_meta_keywords' => 'photography, wallpapers, skins, stock, browse, social, community, artist, picture, photo', 'page_canonical' => $seo['canonical'], 'page_rel_prev' => $seo['prev'], 'page_rel_next' => $seo['next'], 'page_robots' => 'index,follow', ]); } public function content(Request $request, string $contentTypeSlug, ?string $path = null) { $contentSlug = strtolower($contentTypeSlug); if (! in_array($contentSlug, self::CONTENT_TYPE_SLUGS, true)) { abort(404); } $contentType = ContentType::where('slug', $contentSlug)->first(); if (! $contentType) { abort(404); } $sort = (string) $request->query('sort', 'latest'); $perPage = $this->resolvePerPage($request); $mainCategories = $this->mainCategories(); $rootCategories = $contentType->rootCategories()->orderBy('sort_order')->orderBy('name')->get(); $normalizedPath = trim((string) $path, '/'); if ($normalizedPath === '') { $artworks = $this->artworks->getArtworksByContentType($contentSlug, $perPage, $sort); $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug), $artworks); return view('gallery.index', [ 'gallery_type' => 'content-type', 'mainCategories' => $mainCategories, 'subcategories' => $rootCategories, 'contentType' => $contentType, 'category' => null, 'artworks' => $artworks, 'hero_title' => $contentType->name, 'hero_description' => $contentType->description ?? ($contentType->name . ' artworks on Skinbase.'), 'breadcrumbs' => collect([(object) ['name' => $contentType->name, 'url' => '/' . $contentSlug]]), 'page_title' => $contentType->name, 'page_meta_description' => $contentType->description ?? ($contentType->name . ' artworks on Skinbase'), 'page_meta_keywords' => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography', 'page_canonical' => $seo['canonical'], 'page_rel_prev' => $seo['prev'], 'page_rel_next' => $seo['next'], 'page_robots' => 'index,follow', ]); } $segments = array_values(array_filter(explode('/', $normalizedPath))); $category = Category::findByPath($contentSlug, $segments); if (! $category) { abort(404); } $artworks = $this->artworks->getArtworksByCategoryPath(array_merge([$contentSlug], $segments), $perPage, $sort); $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), $artworks); $subcategories = $category->children()->orderBy('sort_order')->orderBy('name')->get(); if ($subcategories->isEmpty()) { $subcategories = $rootCategories; } $breadcrumbs = collect($category->breadcrumbs) ->map(function (Category $crumb) { return (object) [ 'name' => $crumb->name, 'url' => $crumb->url, ]; }); return view('gallery.index', [ 'gallery_type' => 'category', 'mainCategories' => $mainCategories, 'subcategories' => $subcategories, 'contentType' => $contentType, 'category' => $category, 'artworks' => $artworks, 'hero_title' => $category->name, 'hero_description' => $category->description ?? ($contentType->name . ' artworks on Skinbase.'), 'breadcrumbs' => $breadcrumbs, 'page_title' => $category->name, 'page_meta_description' => $category->description ?? ($contentType->name . ' artworks on Skinbase'), 'page_meta_keywords' => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography', 'page_canonical' => $seo['canonical'], 'page_rel_prev' => $seo['prev'], 'page_rel_next' => $seo['next'], 'page_robots' => 'index,follow', ]); } public function showArtwork(...$params) { $req = request(); $pathSegments = array_values(array_filter(explode('/', trim($req->path(), '/')))); $contentTypeSlug = $params[0] ?? ($pathSegments[0] ?? null); $categoryPath = $params[1] ?? null; $artwork = $params[2] ?? null; // If artwork wasn't provided (some route invocations supply fewer args), // derive it from the request path's last segment. if ($artwork === null) { $artwork = end($pathSegments) ?: null; } $contentTypeSlug = strtolower((string) $contentTypeSlug); $categoryPath = $categoryPath !== null ? trim((string) $categoryPath, '/') : (isset($pathSegments[1]) ? implode('/', array_slice($pathSegments, 1, max(0, count($pathSegments) - 2))) : ''); // Normalize artwork param if route-model binding returned an Artwork model $artworkSlug = $artwork instanceof Artwork ? (string) $artwork->slug : (string) $artwork; return app(\App\Http\Controllers\ArtworkController::class)->show( $req, $contentTypeSlug, $categoryPath, $artworkSlug ); } public function legacyCategory(Request $request, ?string $group = null, ?string $slug = null, ?string $id = null) { if ($id !== null && ctype_digit((string) $id)) { $category = Category::with('contentType')->find((int) $id); if (! $category || ! $category->contentType) { abort(404); } return redirect($category->url, 301); } $contentSlug = strtolower((string) $group); if (! in_array($contentSlug, self::CONTENT_TYPE_SLUGS, true)) { abort(404); } $target = '/' . $contentSlug; $normalizedSlug = trim((string) $slug, '/'); if ($normalizedSlug !== '') { $target .= '/' . strtolower($normalizedSlug); } if ($request->query()) { $target .= '?' . http_build_query($request->query()); } return redirect($target, 301); } private function resolvePerPage(Request $request): int { $limit = (int) $request->query('limit', 0); $perPage = (int) $request->query('per_page', 0); $value = $limit > 0 ? $limit : ($perPage > 0 ? $perPage : 40); return max(12, min($value, 80)); } private function mainCategories(): Collection { return ContentType::orderBy('id') ->get(['name', 'slug']) ->map(function (ContentType $type) { return (object) [ 'id' => $type->id, 'name' => $type->name, 'slug' => $type->slug, 'url' => '/' . strtolower($type->slug), ]; }); } private function buildPaginationSeo(Request $request, string $canonicalBaseUrl, mixed $paginator): array { $canonicalQuery = $request->query(); unset($canonicalQuery['grid']); if (($canonicalQuery['page'] ?? null) !== null && (int) $canonicalQuery['page'] <= 1) { unset($canonicalQuery['page']); } $canonical = $canonicalBaseUrl; if ($canonicalQuery !== []) { $canonical .= '?' . http_build_query($canonicalQuery); } $prev = null; $next = null; if ($paginator instanceof AbstractPaginator || $paginator instanceof AbstractCursorPaginator) { $prev = $this->stripQueryParamFromUrl($paginator->previousPageUrl(), 'grid'); $next = $this->stripQueryParamFromUrl($paginator->nextPageUrl(), 'grid'); } return [ 'canonical' => $canonical, 'prev' => $prev, 'next' => $next, ]; } private function stripQueryParamFromUrl(?string $url, string $queryParam): ?string { if ($url === null || $url === '') { return null; } $parts = parse_url($url); if (!is_array($parts)) { return $url; } $query = []; if (!empty($parts['query'])) { parse_str($parts['query'], $query); unset($query[$queryParam]); } $rebuilt = ''; if (isset($parts['scheme'])) { $rebuilt .= $parts['scheme'] . '://'; } if (isset($parts['user'])) { $rebuilt .= $parts['user']; if (isset($parts['pass'])) { $rebuilt .= ':' . $parts['pass']; } $rebuilt .= '@'; } if (isset($parts['host'])) { $rebuilt .= $parts['host']; } if (isset($parts['port'])) { $rebuilt .= ':' . $parts['port']; } $rebuilt .= $parts['path'] ?? ''; if ($query !== []) { $rebuilt .= '?' . http_build_query($query); } if (isset($parts['fragment'])) { $rebuilt .= '#' . $parts['fragment']; } return $rebuilt; } }