fn_b/app/Services/SeoNotifications.php

55 lines
3.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\Articles;
use App\Services\Editorial\IndexNowNotification;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
final class SeoNotifications
{
public function changed(Articles $a, bool $deleted = false): void
{
if (!Schema::hasTable('seo_notifications')) return;
$urls = [];
if ($deleted || !Seo::public($a)) $urls[] = Seo::articleUrl($a);
elseif (Seo::indexable($a)) $urls[] = Seo::articleUrl($a);
if ($a->oCategory?->sSlug === 'freenode') $urls[] = Seo::url();
if ($a->oCategory) $urls[] = Seo::url(Seo::categoryPath($a->oCategory));
$event = $deleted || !Seo::public($a) ? 'delete' : 'update';
$key = hash('sha256',json_encode([$a->id,$event,$a->sContentUpdatedAt,$a->sTitle,$a->sContent,$a->sThumb,$a->iStatus]));
$this->enqueue($key,$urls,$event);
}
public function enqueue(string $key, array $urls, string $event = 'update'): void
{
if (!app()->environment(['production','testing']) || !config('editorial.indexnow_enabled',app()->environment('production'))) return;
DB::table('seo_notifications')->insertOrIgnore(['event_key'=>$key,'urls'=>json_encode(array_values(array_unique($urls))),
'event'=>$event,'state'=>json_encode(['status'=>'pending','attempts'=>0]),'created_at'=>now(),'updated_at'=>now()]);
}
public function retry(int $limit = 10): int
{
$processed = 0;
foreach (DB::table('seo_notifications')->orderBy('id')->cursor() as $candidate) {
$s = json_decode($candidate->state,true);
if (($s['status'] ?? '') !== 'pending' || ($s['next_attempt_at'] ?? 0) > now()->timestamp) continue;
DB::transaction(function () use ($candidate,&$processed) {
$row = DB::table('seo_notifications')->where('id',$candidate->id)->lockForUpdate()->first();
$state = json_decode($row->state,true);
if (($state['status'] ?? '') !== 'pending' || ($state['next_attempt_at'] ?? 0) > now()->timestamp) return;
$urls = json_decode($row->urls,true);
if ($row->event !== 'delete') $urls = array_values(array_filter($urls,function($url) {
$path = parse_url($url,PHP_URL_PATH);
if (preg_match('~^/a/([a-zA-Z0-9_-]+)\.html$~',$path,$m)) {
$a = Articles::where('sSlug',$m[1])->first(); return $a && Seo::indexable($a);
}
if ($path === '/') return app(ArticlesService::class)->getLatestFreeNodeSlug() !== null;
return true;
}));
$state = app(IndexNowNotification::class)->submit($state,$urls);
DB::table('seo_notifications')->where('id',$row->id)->update(['state'=>json_encode($state),'updated_at'=>now()]);
$processed++;
});
if ($processed >= $limit) break;
}
return $processed;
}
}