fedi-feed-router/app/Services/Article/ValidationService.php

114 lines
3.4 KiB
PHP
Raw Normal View History

2025-06-29 19:46:50 +02:00
<?php
namespace App\Services\Article;
use App\Enums\ApprovalStatusEnum;
2025-06-29 19:46:50 +02:00
use App\Models\Article;
use App\Models\Keyword;
use App\Models\Route;
use App\Models\RouteArticle;
use App\Models\Setting;
use Illuminate\Support\Collection;
2025-06-29 19:46:50 +02:00
class ValidationService
{
2025-08-15 02:50:42 +02:00
public function __construct(
private ArticleFetcher $articleFetcher
) {}
public function validate(Article $article): Article
2025-06-29 19:46:50 +02:00
{
logger('Validating article for routes: '.$article->id);
2025-06-29 19:46:50 +02:00
2025-08-15 02:50:42 +02:00
$articleData = $this->articleFetcher->fetchArticleData($article);
2025-08-10 21:18:20 +02:00
$updateData = [];
2025-08-09 18:34:19 +02:00
if (! empty($articleData)) {
2025-08-10 21:18:20 +02:00
$updateData['title'] = $articleData['title'] ?? $article->title;
$updateData['description'] = $articleData['description'] ?? $article->description;
$updateData['content'] = $articleData['full_article'] ?? null;
2025-08-09 18:34:19 +02:00
}
if (! isset($articleData['full_article']) || empty($articleData['full_article'])) {
2025-08-09 21:32:46 +02:00
logger()->warning('Article data missing full_article content', [
2025-06-30 21:28:15 +02:00
'article_id' => $article->id,
'url' => $article->url,
2025-06-30 21:28:15 +02:00
]);
$updateData['validated_at'] = now();
2025-08-09 18:34:19 +02:00
$article->update($updateData);
2025-06-30 21:28:15 +02:00
return $article->refresh();
}
$updateData['validated_at'] = now();
2025-08-09 18:34:19 +02:00
$article->update($updateData);
2025-06-29 19:46:50 +02:00
$this->createRouteArticles($article, $articleData['full_article']);
2025-06-29 19:46:50 +02:00
return $article->refresh();
}
2025-06-29 21:33:18 +02:00
private function createRouteArticles(Article $article, string $content): void
{
$activeRoutes = Route::where('feed_id', $article->feed_id)
->where('is_active', true)
->get();
// Batch-load all active keywords for this feed, grouped by channel
$keywordsByChannel = Keyword::where('feed_id', $article->feed_id)
->where('is_active', true)
->get()
->groupBy('platform_channel_id');
foreach ($activeRoutes as $route) {
$routeKeywords = $keywordsByChannel->get($route->platform_channel_id, collect());
$status = $this->evaluateKeywords($routeKeywords, $content);
if ($status === ApprovalStatusEnum::PENDING && $this->shouldAutoApprove($route)) {
$status = ApprovalStatusEnum::APPROVED;
}
RouteArticle::firstOrCreate(
[
'feed_id' => $route->feed_id,
'platform_channel_id' => $route->platform_channel_id,
'article_id' => $article->id,
],
[
'approval_status' => $status,
'validated_at' => now(),
]
);
}
}
/**
* @param Collection<int, Keyword> $keywords
*/
private function evaluateKeywords(Collection $keywords, string $content): ApprovalStatusEnum
2025-06-29 21:33:18 +02:00
{
if ($keywords->isEmpty()) {
return ApprovalStatusEnum::PENDING;
}
2025-06-29 21:33:18 +02:00
foreach ($keywords as $keyword) {
if (stripos($content, $keyword->keyword) !== false) {
return ApprovalStatusEnum::PENDING;
2025-06-29 21:33:18 +02:00
}
}
return ApprovalStatusEnum::REJECTED;
}
private function shouldAutoApprove(Route $route): bool
{
if ($route->auto_approve !== null) {
return $route->auto_approve;
}
return ! Setting::isPublishingApprovalsEnabled();
2025-06-29 21:33:18 +02:00
}
2025-06-29 19:46:50 +02:00
}