fn_b/app/Services/Editorial/PublicationEffects.php

81 lines
3.7 KiB
PHP

<?php
namespace App\Services\Editorial;
use App\Models\Articles;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Meilisearch\Client;
use Spatie\ResponseCache\Facades\ResponseCache;
final class PublicationEffects
{
public function synchronize(string $id): array
{
DB::transaction(function () use ($id) {
$row = DB::table('editorial_publications')->where('id', $id)->lockForUpdate()->first();
abort_unless($row, 404);
$receipt = json_decode($row->receipt, true);
$effects = json_decode($row->effects ?? '{}', true) ?: [];
if (($effects['cache']['status'] ?? '') !== 'done') {
try {
ResponseCache::clear();
Cache::forget('article_order_likes');
$effects['cache'] = ['status' => 'done'];
} catch (\Throwable) {
$effects['cache'] = ['status' => 'pending', 'reason' => 'Cache refresh failed; retry synchronization'];
}
}
if (($effects['search']['status'] ?? '') !== 'done') {
try {
$ids = array_column($receipt['articles'], 'article_id');
$effects['search'] = $this->index($ids);
} catch (\Throwable) {
$effects['search'] = ['status' => 'pending', 'reason' => 'Search synchronization failed; retry synchronization'];
}
}
// Sitemap is generated from current DB rows, no external submission is performed.
$effects['sitemap'] = ['status' => ($effects['cache']['status'] ?? '') === 'done' ? 'done' : 'pending', 'method' => 'database-driven'];
DB::table('editorial_publications')->where('id', $id)->update(['effects' => json_encode($effects), 'updated_at' => now()]);
});
return app(Publisher::class)->receipt($id);
}
public function index(?array $ids = null): array
{
$driver = config('scout.driver');
if (in_array($driver, ['collection', 'database'])) {
return ['status' => 'done', 'method' => 'live-database'];
}
if ($driver !== 'meilisearch') {
return ['status' => 'pending', 'reason' => 'Configure a supported site search driver'];
}
$name = config('editorial.search_index');
$host = config('scout.meilisearch.host');
if (! $name || (app()->environment('local') && (! in_array(parse_url($host, PHP_URL_HOST), ['localhost', '127.0.0.1', '::1']) || ! str_starts_with($name, 'fna_v3_')))) {
return ['status' => 'pending', 'reason' => 'An isolated search index is required'];
}
$client = new Client($host, config('scout.meilisearch.key'));
$index = $client->index($name);
$wait = function ($task) use ($client) {
$result = $client->waitForTask($task['taskUid'], 10000, 50);
if (($result['status'] ?? '') !== 'succeeded') {
throw new \RuntimeException('Index task failed');
}
};
$wait($index->updateSortableAttributes(['iIsTop', 'created_at']));
$wait($index->updateFilterableAttributes(['iStatus', 'iCategoryId']));
$query = Articles::query()->where('iStatus', 1);
if ($ids !== null) {
$query->whereIn('id', $ids);
}
$documents = $query->get()->map(fn ($a) => $a->toSearchableArray() + ['created_at' => $a->created_at?->timestamp])->all();
if ($documents) {
$wait($index->addDocuments($documents, 'id'));
}
return ['status' => 'done', 'method' => 'meilisearch', 'index' => $name, 'documents' => count($documents)];
}
}