refactor search functionality; remove deprecated services and implement new action and task classes for improved structure and maintainability
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Containers\Search\Actions;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ClearStaticSearchCacheAction
|
||||
{
|
||||
const CACHE_KEY = 'static_html_index_v2';
|
||||
|
||||
public function run(): bool
|
||||
{
|
||||
try {
|
||||
Cache::forget(self::CACHE_KEY);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Cache clear error: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Containers\Search\Actions;
|
||||
|
||||
use App\Containers\AdditionalEducation\Models\AdditionalEducation;
|
||||
use App\Containers\AppStructure\Models\Page;
|
||||
use App\Containers\Article\Models\Post;
|
||||
use App\Containers\Education\Models\EducationalProgram;
|
||||
use App\Containers\Event\Models\Event;
|
||||
use App\Containers\InstituteStructure\Models\Faculty;
|
||||
use App\Containers\Schedule\Models\EducationalGroup;
|
||||
use App\Containers\Search\UI\API\Transformers\AdditionalEducationSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\EducationalProgramSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\EducationGroupSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\EventSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\FacultySearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\PageSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\PostSearchResource;
|
||||
use App\Containers\Search\UI\API\Transformers\UserSearchResource;
|
||||
use App\Containers\User\Models\User;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Str;
|
||||
use ProtoneMedia\LaravelCrossEloquentSearch\Search;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class PerformCrossEloquentSearchAction
|
||||
{
|
||||
private array $resourceMap = [
|
||||
Post::class => PostSearchResource::class,
|
||||
Page::class => PageSearchResource::class,
|
||||
EducationalGroup::class => EducationGroupSearchResource::class,
|
||||
EducationalProgram::class => EducationalProgramSearchResource::class,
|
||||
Event::class => EventSearchResource::class,
|
||||
AdditionalEducation::class => AdditionalEducationSearchResource::class, //??
|
||||
User::class => UserSearchResource::class, //??
|
||||
Faculty::class => FacultySearchResource::class, //??
|
||||
];
|
||||
|
||||
public function run(string $query): Collection
|
||||
{
|
||||
$results = Search::new()
|
||||
->add(Post::where('status', '=', 'published'), ['title', 'search_data'], 'publish_at')
|
||||
->add(Page::with('section')->where('searchable', '=', true), ['title', 'search_data'])
|
||||
->add(Event::where('event_date_start', '>', Date::now()), 'title', 'created_at')
|
||||
->add(AdditionalEducation::where('is_active', '=', true), 'title')
|
||||
->add(EducationalGroup::with('schedules'), 'title')
|
||||
->add(EducationalProgram::where('status', '=', true)->whereHas('admission_plans'), 'name')
|
||||
->add(Faculty::where('is_active', '=', true), 'title')
|
||||
->add(User::whereHas('userDetail'), 'name')
|
||||
->orderByDesc()
|
||||
->beginWithWildcard()
|
||||
->includeModelType()
|
||||
->ignoreCase(true)
|
||||
->search("$query");
|
||||
|
||||
$resourceMap = $this->resourceMap;
|
||||
$resources = collect($results)->map(function ($result) use ($resourceMap) {
|
||||
$resourceClass = $resourceMap[get_class($result)] ?? null;
|
||||
return $resourceClass ? new $resourceClass($result) : null;
|
||||
})->filter(); // Filter out nulls from resources
|
||||
|
||||
return $resources;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Containers\Search\Actions;
|
||||
|
||||
use App\Containers\Search\Tasks\BuildStaticFileIndexTask;
|
||||
|
||||
class RebuildStaticSearchIndexAction
|
||||
{
|
||||
public function run(): array
|
||||
{
|
||||
// Clear existing cache (optional, depending on rebuild logic, but typical for full rebuilds)
|
||||
// app(ClearStaticSearchCacheAction::class)->run();
|
||||
|
||||
return app(BuildStaticFileIndexTask::class)->run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Containers\Search\Actions;
|
||||
|
||||
use App\Containers\Search\Tasks\BuildStaticFileIndexTask;
|
||||
use App\Containers\Search\Tasks\PaginateSearchResultsTask;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SearchStaticFilesAction
|
||||
{
|
||||
const PER_PAGE = 10; // Количество результатов на страницу
|
||||
|
||||
public function run(Request $request): array
|
||||
{
|
||||
$query = $request->input('search');
|
||||
$category = $request->input('category');
|
||||
$page = $request->input('page', 1);
|
||||
|
||||
try {
|
||||
$index = app(BuildStaticFileIndexTask::class)->run();
|
||||
$results = [];
|
||||
$normalizedQuery = $this->normalizeText(Str::lower($query));
|
||||
|
||||
foreach ($index as $filePath => $content) {
|
||||
if (stripos($content['content'], $normalizedQuery) !== false) {
|
||||
$relativePath = str_replace(public_path() . '/', '', $filePath);
|
||||
$results[] = [
|
||||
'file' => $relativePath,
|
||||
'content' => $content['title'],
|
||||
'category' => trim($content['category']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$categories = array_values(array_unique(array_filter(array_column($results, 'category'))));
|
||||
|
||||
if ($category !== null) {
|
||||
$results = array_filter($results, function($item) use ($category) {
|
||||
return $item['category'] === $category;
|
||||
});
|
||||
|
||||
// Переиндексировать массив
|
||||
$results = array_values($results);
|
||||
}
|
||||
|
||||
return app(PaginateSearchResultsTask::class)->run($results, $page, self::PER_PAGE, $categories);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Search error: ' . $e->getMessage());
|
||||
return [
|
||||
'data' => [],
|
||||
'meta' => [
|
||||
'current_page' => 1,
|
||||
'total' => 0,
|
||||
'per_page' => self::PER_PAGE,
|
||||
'last_page' => 1
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected function normalizeText(string $text): string
|
||||
{
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = preg_replace('/\s+/u', ' ', $text);
|
||||
$text = trim($text);
|
||||
return Str::lower($text);
|
||||
}
|
||||
|
||||
protected function getFirstH1Content(string $content): ?string
|
||||
{
|
||||
try {
|
||||
if (preg_match('/<h1[^>]*>(.*?)<\/h1>/is', $content, $matches)) {
|
||||
return $this->normalizeText($matches[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Failed to get H1: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user