Wire admin studio SSR and search infrastructure

This commit is contained in:
2026-05-01 11:46:06 +02:00
parent 257b0dbef6
commit 18cea8b0f0
329 changed files with 197465 additions and 2741 deletions

View File

@@ -7,6 +7,7 @@ namespace App\Services;
use App\Jobs\DeleteArtworkFromIndexJob;
use App\Jobs\IndexArtworkJob;
use App\Models\Artwork;
use Closure;
use Illuminate\Support\Facades\Log;
/**
@@ -43,19 +44,63 @@ final class ArtworkSearchIndexer
/**
* Rebuild the entire artworks index in background chunks.
* Run via: php artisan artworks:search-rebuild
*
* @param Closure(int, int, int, int, int, int): void|null $onChunk
* @return array{total:int, dispatched:int, chunks:int}
*/
public function rebuildAll(int $chunkSize = 500): void
public function rebuildAll(int $chunkSize = 500, ?Closure $onChunk = null, bool $reverse = false, ?int $limit = null): array
{
Artwork::with(['user', 'tags', 'categories', 'stats', 'awardStat'])
$query = Artwork::query()
->public()
->published()
->orderBy('id')
->chunk($chunkSize, function ($artworks): void {
->published();
if ($reverse) {
$query->orderByDesc('id');
} else {
$query->orderBy('id');
}
if ($limit !== null) {
$query->limit($limit);
}
$total = (clone $query)->count();
$dispatched = 0;
$chunks = 0;
$query
->with(['user', 'tags', 'categories', 'stats', 'awardStat'])
->chunk($chunkSize, function ($artworks) use (&$chunks, &$dispatched, $total, $onChunk): void {
$chunks++;
$count = $artworks->count();
$firstId = (int) ($artworks->first()?->id ?? 0);
$lastId = (int) ($artworks->last()?->id ?? 0);
foreach ($artworks as $artwork) {
IndexArtworkJob::dispatch($artwork->id);
$dispatched++;
}
if ($onChunk !== null) {
$onChunk($chunks, $count, $dispatched, $total, $firstId, $lastId);
}
});
Log::info('ArtworkSearchIndexer::rebuildAll — jobs dispatched');
Log::info('ArtworkSearchIndexer::rebuildAll — jobs dispatched', [
'total' => $total,
'dispatched' => $dispatched,
'chunks' => $chunks,
'chunk_size' => $chunkSize,
'reverse' => $reverse,
'limit' => $limit,
]);
return [
'total' => $total,
'dispatched' => $dispatched,
'chunks' => $chunks,
];
}
}

View File

@@ -7,6 +7,7 @@ namespace App\Services;
use App\Models\Artwork;
use App\Models\Category;
use App\Models\Group;
use App\Jobs\IndexArtworkJob;
use App\Models\Tag;
use App\Models\User;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -392,17 +393,6 @@ class GroupArtworkReviewService
private function syncSearchIndex(Artwork $artwork): void
{
try {
if ((bool) $artwork->is_public && (bool) $artwork->is_approved && ! empty($artwork->published_at)) {
$artwork->searchable();
} else {
$artwork->unsearchable();
}
} catch (\Throwable $exception) {
Log::warning('Failed to sync artwork search index for group review workflow', [
'artwork_id' => (int) $artwork->id,
'error' => $exception->getMessage(),
]);
}
IndexArtworkJob::dispatch((int) $artwork->id);
}
}

View File

@@ -6,6 +6,7 @@ namespace App\Services\Studio;
use App\Models\Artwork;
use App\Models\Tag;
use App\Jobs\IndexArtworkJob;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -156,12 +157,8 @@ final class StudioBulkActionService
*/
private function reindexArtworks(\Illuminate\Database\Eloquent\Collection $artworks): void
{
try {
$artworks->each->searchable();
} catch (\Throwable $e) {
Log::warning('Studio: Failed to reindex artworks after bulk action', [
'error' => $e->getMessage(),
]);
foreach ($artworks as $artwork) {
IndexArtworkJob::dispatch((int) $artwork->id);
}
}
}