feat: increase gallery grid from 4 to 5 columns per row on desktopfeat: increase gallery grid from 4 to 5 columns per row on desktop

This commit is contained in:
2026-02-25 19:11:23 +01:00
parent 5c97488e80
commit 0032aec02f
131 changed files with 15674 additions and 597 deletions

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\Search;
use App\Http\Controllers\Controller;
use App\Http\Resources\ArtworkListResource;
use App\Models\Artwork;
use App\Models\Tag;
use App\Services\ArtworkSearchService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* Artwork search endpoints powered by Meilisearch.
*
* GET /api/search/artworks?q=&tags[]=&category=&orientation=&sort=
* GET /api/search/artworks/tag/{slug}
* GET /api/search/artworks/category/{cat}
* GET /api/search/artworks/related/{id}
*/
class ArtworkSearchController extends Controller
{
public function __construct(
private readonly ArtworkSearchService $search
) {}
/**
* GET /api/search/artworks
*/
public function index(Request $request): JsonResponse
{
$validated = $request->validate([
'q' => ['nullable', 'string', 'max:200'],
'tags' => ['nullable', 'array', 'max:10'],
'tags.*' => ['string', 'max:80'],
'category' => ['nullable', 'string', 'max:80'],
'orientation' => ['nullable', 'in:landscape,portrait,square'],
'resolution' => ['nullable', 'string', 'max:20'],
'author_id' => ['nullable', 'integer', 'min:1'],
'sort' => ['nullable', 'string', 'regex:/^(created_at|downloads|likes|views):(asc|desc)$/'],
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
]);
$results = $this->search->search(
q: (string) ($validated['q'] ?? ''),
filters: array_filter([
'tags' => $validated['tags'] ?? [],
'category' => $validated['category'] ?? null,
'orientation' => $validated['orientation'] ?? null,
'resolution' => $validated['resolution'] ?? null,
'author_id' => $validated['author_id'] ?? null,
'sort' => $validated['sort'] ?? null,
]),
perPage: (int) ($validated['per_page'] ?? 24),
);
// Eager-load relations needed by ArtworkListResource
$results->getCollection()->loadMissing(['user', 'categories.contentType']);
return ArtworkListResource::collection($results)->response();
}
/**
* GET /api/search/artworks/tag/{slug}
*/
public function byTag(Request $request, string $slug): JsonResponse
{
$tag = Tag::where('slug', $slug)->first();
if (! $tag) {
return response()->json(['message' => 'Tag not found.'], 404);
}
$results = $this->search->byTag($slug, (int) $request->query('per_page', 24));
return response()->json([
'tag' => ['id' => $tag->id, 'name' => $tag->name, 'slug' => $tag->slug],
'results' => $results,
]);
}
/**
* GET /api/search/artworks/category/{cat}
*/
public function byCategory(Request $request, string $cat): JsonResponse
{
$results = $this->search->byCategory($cat, (int) $request->query('per_page', 24));
return response()->json($results);
}
/**
* GET /api/search/artworks/related/{id}
*/
public function related(int $id): JsonResponse
{
$artwork = Artwork::with(['tags'])->find($id);
if (! $artwork) {
return response()->json(['message' => 'Artwork not found.'], 404);
}
$results = $this->search->related($artwork, 12);
return response()->json($results);
}
}