Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -6,6 +6,8 @@ namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Models\Group;
use App\Models\User;
use App\Services\ArtworkSearchService;
use App\Services\ContentTypes\ContentTypeSlugResolver;
use App\Services\EarlyGrowth\EarlyGrowth;
@@ -77,10 +79,12 @@ final class ExploreController extends Controller
$page = max(1, (int) $request->query('page', 1));
$ttl = self::SORT_TTL[$sort] ?? 300;
$cacheVersion = $this->cacheVersion();
$filter = $this->buildExploreFilterExpression($request);
$cacheSuffix = $this->requestCacheSuffix($request);
$artworks = Cache::remember("explore.all.v{$cacheVersion}.{$sort}.{$page}", $ttl, fn () =>
$artworks = Cache::remember("explore.all.v{$cacheVersion}.{$cacheSuffix}.{$page}", $ttl, fn () =>
$this->search->searchWithThumbnailPreference([
'filter' => 'is_public = true AND is_approved = true',
'filter' => $filter,
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
], $perPage, false, $page)
);
@@ -148,13 +152,10 @@ final class ExploreController extends Controller
$page = max(1, (int) $request->query('page', 1));
$ttl = self::SORT_TTL[$sort] ?? 300;
$cacheVersion = $this->cacheVersion();
$filter = $this->buildExploreFilterExpression($request, $isAll ? null : $resolvedTypeSlug);
$cacheSuffix = $this->requestCacheSuffix($request);
$filter = 'is_public = true AND is_approved = true';
if (!$isAll) {
$filter .= ' AND content_type = "' . $type . '"';
}
$artworks = Cache::remember("explore.{$resolvedTypeSlug}.v{$cacheVersion}.{$sort}.{$page}", $ttl, fn () =>
$artworks = Cache::remember("explore.{$resolvedTypeSlug}.v{$cacheVersion}.{$cacheSuffix}.{$page}", $ttl, fn () =>
$this->search->searchWithThumbnailPreference([
'filter' => $filter,
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
@@ -288,11 +289,122 @@ final class ExploreController extends Controller
return max(12, min($v, 80));
}
private function requestCacheSuffix(Request $request): string
{
$query = $request->query();
unset($query['grid'], $query['page']);
ksort($query);
return md5(json_encode($query, JSON_THROW_ON_ERROR));
}
private function cacheVersion(): int
{
return max(1, (int) Cache::get('explore.cache.version', 1));
}
private function buildExploreFilterExpression(Request $request, ?string $contentType = null): string
{
$filterParts = [
'is_public = true',
'is_approved = true',
];
if ($contentType !== null && $contentType !== '') {
$filterParts[] = 'content_type = "' . addslashes($contentType) . '"';
}
$orientation = strtolower(trim((string) $request->query('orientation', '')));
if (in_array($orientation, ['landscape', 'portrait', 'square'], true)) {
$filterParts[] = 'orientation = "' . addslashes($orientation) . '"';
}
$resolution = $this->resolutionFilterValue((string) $request->query('resolution', ''));
if ($resolution !== null) {
$filterParts[] = 'resolution = "' . addslashes($resolution) . '"';
}
$dateFrom = $this->normalizeDateQuery((string) $request->query('date_from', ''));
if ($dateFrom !== null) {
$filterParts[] = 'created_at >= "' . $dateFrom . '"';
}
$dateTo = $this->normalizeDateQuery((string) $request->query('date_to', ''));
if ($dateTo !== null) {
$filterParts[] = 'created_at <= "' . $dateTo . '"';
}
$authorFilter = $this->authorFilterExpression((string) $request->query('author', ''));
if ($authorFilter !== null) {
$filterParts[] = $authorFilter;
}
return implode(' AND ', $filterParts);
}
private function resolutionFilterValue(string $resolution): ?string
{
return match (strtolower(trim($resolution))) {
'hd' => '1280x720',
'fhd' => '1920x1080',
'2k' => '2560x1440',
'4k' => '3840x2160',
default => null,
};
}
private function normalizeDateQuery(string $value): ?string
{
$value = trim($value);
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
return null;
}
return $value;
}
private function authorFilterExpression(string $author): ?string
{
$author = trim($author);
if ($author === '') {
return null;
}
$userIds = User::query()
->where(function ($query) use ($author): void {
$query->where('username', 'like', '%' . $author . '%')
->orWhere('name', 'like', '%' . $author . '%');
})
->limit(20)
->pluck('id');
$groupIds = Group::query()
->where(function ($query) use ($author): void {
$query->where('name', 'like', '%' . $author . '%')
->orWhere('slug', 'like', '%' . $author . '%');
})
->limit(20)
->pluck('id');
$clauses = [];
foreach ($userIds as $userId) {
$clauses[] = '(author_id = ' . (int) $userId . ' AND published_as_type = "user")';
}
foreach ($groupIds as $groupId) {
$clauses[] = '(author_id = ' . (int) $groupId . ' AND published_as_type = "group")';
}
if ($clauses === []) {
return 'id = 0';
}
return '(' . implode(' OR ', $clauses) . ')';
}
private function filterBrowsableArtworks(AbstractPaginator $paginator): AbstractPaginator
{
$paginator->setCollection(