Migrate backend to Porto architecture and fix minor bugs

- Fully transitioned the backend to the Porto architectural pattern
- Improved code organization and maintainability
- Fixed minor bugs and inconsistencies
This commit is contained in:
F4ilji
2025-05-12 08:47:43 +05:00
parent 76deaf75f3
commit c01016a154
620 changed files with 16218 additions and 6073 deletions
@@ -0,0 +1,88 @@
<?php
namespace App\Containers\Article\Tasks;
use App\Containers\Article\Models\Category;
use App\Containers\Article\UI\WEB\Transformers\CategoryResource;
use App\Containers\Article\UI\WEB\Transformers\TagResource;
use App\Ship\Builders\FilterBuilder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class BuildFiltersTask
{
public function __construct(
private readonly FilterBuilder $filterBuilder
) {}
public function run(array $filters): array
{
// Сброс фильтров перед новым запуском
$this->filterBuilder->reset();
// 1. Поисковый фильтр
$this->filterBuilder->add(
key: 'search_filter',
type: 'search',
value: $filters['search'] ?? null,
param: 'search'
);
// 2. Категории
$categoriesContent = [];
if (!empty($filters['category'])) {
$categoriesSlugs = Arr::wrap($filters['category']);
foreach ($categoriesSlugs as $item) {
$cacheKey = 'category_content_' . $item;
$categoriesContent[$item] = Cache::remember($cacheKey, now()->addHours(1), function () use ($item) {
return new CategoryResource(Category::where('slug', $item)->first());
});
}
}
$this->filterBuilder->add(
key: 'category_filter',
type: 'category',
value: $categoriesSlugs ?? null,
param: 'category',
content: $categoriesContent
);
// 3. Теги
$tagsContent = [];
if (!empty($filters['tag'])) {
$tagsSlugs = Arr::wrap($filters['tag']);
foreach ($tagsSlugs as $item) {
$cacheKey = 'tag_content_' . $item;
$tagsContent[$item] = Cache::remember($cacheKey, now()->addHours(1), function () use ($item) {
return new TagResource(DB::table('tags')
->where(DB::raw("JSON_UNQUOTE(JSON_EXTRACT(slug, '$.ru'))"), $item)
->first());
});
}
}
$this->filterBuilder->add(
key: 'tag_filter',
type: 'tag',
value: $tagsSlugs ?? null,
param: 'tag',
content: $tagsContent
);
// 4. Сортировка
$this->filterBuilder->add(
key: 'sortingBy_filter',
type: 'sort',
value: $filters['sort'] ?? null,
param: 'sort'
);
return $this->filterBuilder->get();
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Containers\Article\Tasks;
use App\Containers\Article\Models\Category;
use App\Containers\Article\UI\WEB\Transformers\CategoryResource;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\Cache;
class GetCategoriesTask
{
public function run(): AnonymousResourceCollection
{
return Cache::remember('categories', now()->addHours(48), function () {
return CategoryResource::collection(Category::has('posts')->get());
});
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\Containers\Article\Tasks;
use App\Containers\Article\Models\Post;
use Illuminate\Support\Facades\Cache;
use Illuminate\Pagination\LengthAwarePaginator;
use Carbon\Carbon;
class GetPostsTask
{
public function run(array $filters): LengthAwarePaginator
{
$cacheKey = 'posts_' . md5(serialize($filters));
return Cache::remember($cacheKey, now()->addHours(1), function () use ($filters) {
$query = Post::query()
->with('category')
->select('title', 'slug', 'authors', 'category_id', 'preview', 'search_data', 'publish_at')
->where('status', 'published')
->where('publish_at', '<', Carbon::now())
->when(!empty($filters['tag']), function ($query) use ($filters) {
if (is_array($filters['tag'])) {
return $query->withAnyTags($filters['tag']);
}
$slugsArray = explode(',', $filters['tag']);
return $query->withAnyTags($slugsArray);
})
->when(!empty($filters['category']), function ($query) use ($filters) {
if (is_array($filters['category'])) {
$query->whereHas('category', function ($query) use ($filters) {
$query->whereIn('slug', $filters['category']);
});
}
})
->when(!empty($filters['search']), function ($query) use ($filters) {
$query->whereRaw('LOWER(title) like ?', ["%".strtolower($filters['search'])."%"]);
});
$sort = $filters['sort'] ?? 'desc';
return $query->orderBy('publish_at', $sort)->paginate(9)->withQueryString();
});
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Containers\Article\Tasks;
use App\Containers\Article\Models\Post;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Spatie\Tags\Tag;
class GetTagsTask
{
public function run()
{
return Cache::remember('tags', now()->addHours(1), function () {
$tagIds = DB::table('taggables')
->distinct()
->select('tag_id')
->where('taggable_type', Post::class)
->get()
->pluck('tag_id');
return Tag::whereIn('id', $tagIds)->get();
});
}
}