This commit is contained in:
F4ilji
2026-04-07 17:54:26 +05:00
parent 04adba6682
commit 936b1fb5b0
468 changed files with 43498 additions and 4176 deletions
@@ -0,0 +1,24 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
use App\Containers\AppStructure\Models\SubSection;
class AttachPageToSubSectionAction
{
public function run(Page $page, SubSection $subSection): Page
{
// Pessimistic lock to prevent race conditions
$page = Page::lockForUpdate()->findOrFail($page->id);
if ($page->sub_section_id !== null) {
throw new \InvalidArgumentException('Страница уже принадлежит другому подразделу');
}
$page->section()->associate($subSection);
$page->save();
return $page;
}
}
@@ -0,0 +1,38 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
use App\Containers\Dashboard\Tasks\Content\GenerateSearchDataTask;
class CreatePageAction
{
public function __construct(
private readonly GeneratePagePathAction $generatePagePathAction,
private readonly GenerateSearchDataTask $generateSearchDataTask,
) {}
/**
* Создает новую страницу
*
* @param array $data Валидированные данные формы (title, slug, sub_section_id, code, searchable, icon, content, settings)
* @return Page Созданная страница
*/
public function run(array $data): Page
{
$subSectionId = $data['sub_section_id'] ?? null;
// Генерируем path на основе subSection
$data['path'] = $this->generatePagePathAction->run($data['slug'], $subSectionId);
// Генерируем search_data из контента
if (!empty($data['content'])) {
$data['search_data'] = $this->generateSearchDataTask->run($data['content']);
}
// Удаляем sub_section_id — он не является полем модели Page
unset($data['sub_section_id']);
return Page::create($data);
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
class DeletePageAction
{
/**
* Удаляет страницу
*
* @param Page $page Страница для удаления
* @return bool Результат удаления
*/
public function run(Page $page): bool
{
return $page->delete();
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
class DetachPageFromSubSectionAction
{
public function run(Page $page): Page
{
$page->section()->dissociate();
$page->save();
return $page;
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\SubSection;
class GeneratePagePathAction
{
public function run(string $slug, ?int $subSectionId = null): string
{
if ($subSectionId === null) {
return $slug;
}
$subSection = SubSection::with('mainSection')->find($subSectionId);
if ($subSection === null) {
return $slug;
}
if ($subSection->mainSection === null) {
return $subSection->slug . '/' . $slug;
}
return $subSection->mainSection->slug . '/' . $subSection->slug . '/' . $slug;
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
use App\Containers\AppStructure\Models\SubSection;
class ListPagesAction
{
public function run(array $filters): array
{
$query = Page::query()->with(['section.mainSection']);
// Search
if (!empty($filters['search'])) {
$query->where(function ($q) use ($filters) {
$q->where('title', 'like', '%' . $filters['search'] . '%')
->orWhere('path', 'like', '%' . $filters['search'] . '%');
});
}
// Tab filter
if (!empty($filters['tab'])) {
if ($filters['tab'] === 'is_registered') {
$query->where('is_registered', true)->where('is_url', false);
} elseif ($filters['tab'] === 'is_url') {
$query->where('is_url', true);
} else {
$query->where('is_registered', false)->where('is_url', false);
}
} else {
// Default: show created pages
$query->where('is_registered', false)->where('is_url', false);
}
// SubSection filter
if (!empty($filters['sub_section_id'])) {
$query->where('sub_section_id', $filters['sub_section_id']);
}
$pages = $query->orderBy('created_at', 'desc')->paginate(15);
$subSections = SubSection::with('mainSection')
->whereNotNull('title')
->orderBy('title')
->get();
return [
'pages' => $pages,
'subSections' => $subSections,
];
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Containers\Dashboard\Actions\Pages;
use App\Containers\AppStructure\Models\Page;
use App\Containers\Dashboard\Tasks\Content\GenerateSearchDataTask;
class UpdatePageAction
{
public function __construct(
private readonly GeneratePagePathAction $generatePagePathAction,
private readonly GenerateSearchDataTask $generateSearchDataTask,
) {}
/**
* Обновляет страницу
*
* @param Page $page Страница для обновления
* @param array $data Валидированные данные формы
* @return Page Обновленная страница
*/
public function run(Page $page, array $data): Page
{
// Если контент изменился, перегенерируем search_data
if (isset($data['content']) && json_encode($data['content']) !== json_encode($page->content)) {
$data['search_data'] = $this->generateSearchDataTask->run($data['content']);
}
// Если страница не зарегистрирована, перегенерируем path
if ($page->is_registered == false) {
$subSectionId = $data['sub_section_id'] ?? $page->sub_section_id;
$slug = $data['slug'] ?? $page->slug;
$data['path'] = $this->generatePagePathAction->run($slug, $subSectionId);
}
// Удаляем sub_section_id — он не является полем модели Page
unset($data['sub_section_id']);
$page->update($data);
return $page->fresh();
}
}