resolveArtworkId(); if ($artworkId === null) { $this->error('An artwork ID is required.'); return self::FAILURE; } $isDryRun = (bool) $this->option('dry-run'); $forceIndex = (bool) $this->option('force'); $this->line(sprintf( '%sForce-indexing artwork #%d into Meilisearch%s…', $isDryRun ? '[DRY RUN] ' : '', $artworkId, $forceIndex ? ' (--force: eligibility check bypassed)' : '', )); // ── 1. Load artwork with all relations required for toSearchableArray() ── $artwork = Artwork::query() ->with(['user', 'group', 'tags', 'categories.contentType', 'stats', 'awardStat']) ->find($artworkId); if ($artwork === null) { $this->error("Artwork #{$artworkId} was not found in the database."); return self::FAILURE; } $this->comment('Artwork'); $this->line(sprintf( ' id=%d title="%s" public=%s approved=%s published_at=%s', (int) $artwork->id, (string) ($artwork->title ?? ''), $artwork->is_public ? 'yes' : 'no', $artwork->is_approved ? 'yes' : 'no', $artwork->published_at?->toIso8601String() ?? 'null', )); // ── 2. Eligibility check ───────────────────────────────────────────────── $shouldBeIndexed = $artwork->is_public && $artwork->is_approved && $artwork->published_at !== null; if (! $shouldBeIndexed && ! $forceIndex) { $this->warn(sprintf( 'Artwork #%d is not eligible for the public index (is_public=%s, is_approved=%s, published_at=%s). ' . 'Use --force to index it anyway, or fix the artwork status first.', $artworkId, $artwork->is_public ? 'true' : 'false', $artwork->is_approved ? 'true' : 'false', $artwork->published_at?->toIso8601String() ?? 'null', )); return self::FAILURE; } if (! $shouldBeIndexed && $forceIndex) { $this->warn('Artwork is not normally eligible but --force was passed; indexing anyway.'); } // ── 3. Build the Meilisearch document ──────────────────────────────────── $document = $artwork->toSearchableArray(); $this->comment('Generated document'); $this->line(json_encode($document, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); $this->newLine(); // ── 4. Resolve index name ──────────────────────────────────────────────── $indexName = $this->resolveIndexName($artwork); $this->line("Target index: {$indexName}"); if ($isDryRun) { $this->info('[DRY RUN] Document was NOT written to Meilisearch. Remove --dry-run to execute.'); return self::SUCCESS; } // ── 5. Write directly to Meilisearch (no queue) ────────────────────────── try { $taskResult = $client->index($indexName)->addDocuments([$document]); $taskUid = $taskResult['taskUid'] ?? $taskResult['uid'] ?? 'n/a'; $this->info(sprintf( 'Document written to Meilisearch. Task uid: %s', is_scalar($taskUid) ? (string) $taskUid : json_encode($taskUid), )); } catch (\Throwable $e) { $this->error('Meilisearch write failed: ' . $e->getMessage()); return self::FAILURE; } // ── 6. Bump explore cache version ──────────────────────────────────────── if (! $this->option('no-cache-bump')) { try { $newVersion = ((int) Cache::get('explore.cache.version', 1)) + 1; Cache::forever('explore.cache.version', $newVersion); $this->line("Explore cache version bumped to {$newVersion}."); } catch (\Throwable $e) { $this->warn('Could not bump explore cache version: ' . $e->getMessage()); } } // ── 7. Summary ──────────────────────────────────────────────────────────── $this->newLine(); $this->info(sprintf( 'Artwork #%d ("%s") has been pushed to index "%s" directly.', (int) $artwork->id, (string) ($artwork->title ?? ''), $indexName, )); $this->line('The artwork should now appear on browse and search pages.'); $this->line('If Meilisearch was still processing the task you can verify with:'); $this->line(sprintf(' php artisan artworks:search-inspect %d', $artworkId)); return self::SUCCESS; } private function resolveArtworkId(): ?int { $argument = $this->argument('artwork_id'); if ($argument !== null && $argument !== '') { return max(1, (int) $argument); } if (! $this->input->isInteractive()) { return null; } $answer = $this->ask('Artwork ID'); if ($answer === null || trim($answer) === '') { return null; } return max(1, (int) $answer); } private function resolveIndexName(Artwork $artwork): string { $override = trim((string) $this->option('index')); if ($override !== '') { return $override; } return $artwork->searchableAs(); } }