A documentation section has been added to the admin panel.
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategory\StoreUpdate;
|
||||
use App\Exceptions\Services\DocumentationCategory\ParentException;
|
||||
use App\Exceptions\Services\ServiceException;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationCategoryContent;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Models\User;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Repositories\DocumentationVersionRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\DocumentationCategory\DocumentationCategoryCommandHandler;
|
||||
use App\Services\DocumentationCategoryContent\ModelSyncCommand;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class DocumentationCategoryService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||
private readonly DocumentationCategoryCommandHandler $categoryCommandHandler,
|
||||
private readonly ModelSyncCommand $contentSaveCommand,
|
||||
) { }
|
||||
|
||||
public function index(int $projectId, int $versionId, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('viewAny', DocumentationCategory::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$defaultLanguage = $project->languages()->where('is_default', 1)->first();
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||
/** @var ?ProjectLanguage $defaultLanguage */
|
||||
$hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) {
|
||||
$query->where('language_id', $defaultLanguage->id);
|
||||
});
|
||||
}
|
||||
];
|
||||
$with = array_merge($with, $querySettingsDto->getQueryWith());
|
||||
|
||||
$categories = $this->documentationCategoryRepository->getCategories(
|
||||
$version->id,
|
||||
$documentationCategoryBuilderDto,
|
||||
$with
|
||||
)->pagination(
|
||||
$querySettingsDto->getLimit(),
|
||||
$querySettingsDto->getPage()
|
||||
);
|
||||
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'categories' => $categories,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('create', DocumentationCategory::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'category' => new DocumentationCategory(),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $projectId, int $versionId, int $categoryId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||
if (\is_null($category)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $category)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$withCategories = [];
|
||||
if ($category->parent_id) {
|
||||
$withCategories[] = $category->parent_id;
|
||||
}
|
||||
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage, $category, $withCategories),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
if ($user->cannot('create', DocumentationCategory::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
try {
|
||||
$category = DB::transaction(function () use ($data, $version, $user, $versionId, $project) {
|
||||
if ($this->documentationCategoryRepository->isExistsSlug($versionId, $data->getSlug()) !== false) {
|
||||
$error = $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
throw new ServiceException($error);
|
||||
}
|
||||
|
||||
$dataCategory = $this->getDataCategory($data);
|
||||
$category = $this->categoryCommandHandler->handleStore($version, $dataCategory);
|
||||
$this->contentSaveCommand->execute($project, $category, $data->getContents());
|
||||
|
||||
return $category;
|
||||
});
|
||||
} catch (ServiceException $e) {
|
||||
return $e->getServiceResultError();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($category, __('Category successfully created'));
|
||||
}
|
||||
|
||||
public function update(int $projectId, int $versionId, int $categoryId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||
if (\is_null($category)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $category)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($data->getParentId() === $category->id) {
|
||||
return $this->errValidate(
|
||||
__('validation.parent', ['attribute' => __('validation.attributes.parent_id')]),
|
||||
['parent_id' => __('validation.parent', ['attribute' => __('validation.attributes.parent_id')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$category = DB::transaction(function () use ($data, $category, $versionId, $project) {
|
||||
if ($this->documentationCategoryRepository->isExistsSlug($versionId, $data->getSlug(), $category->id) !== false) {
|
||||
$error = $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
throw new ServiceException($error);
|
||||
}
|
||||
|
||||
$dataCategory = $this->getDataCategory($data);
|
||||
$category = $this->categoryCommandHandler->handleUpdate($category, $dataCategory);
|
||||
$this->contentSaveCommand->execute($project, $category, $data->getContents());
|
||||
|
||||
return $category;
|
||||
});
|
||||
} catch (ServiceException $e) {
|
||||
return $e->getServiceResultError();
|
||||
} catch (ParentException $e) {
|
||||
return $this->errValidate(
|
||||
__('validation.parent_cycle_detected', ['attribute' => __('validation.attributes.parent_id')]),
|
||||
['parent_id' => __('validation.parent_cycle_detected', ['attribute' => __('validation.attributes.parent_id')])]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($category, __('Category updated successfully'));
|
||||
}
|
||||
|
||||
public function destroy(int $projectId, int $versionId, int $categoryId, User $user): ServiceResultError|ServiceResultSuccess
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||
if (\is_null($category)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('delete', $category)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($category) {
|
||||
$this->categoryCommandHandler->handleDestroy($category);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('Category successfully deleted'));
|
||||
}
|
||||
|
||||
private function getDataCategory(StoreUpdate $data): array
|
||||
{
|
||||
return [
|
||||
'slug' => $data->getSlug(),
|
||||
'is_public' => $data->isPublic(),
|
||||
'sort' => $data->getSort(),
|
||||
'parent_id' => $data->getParentId(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,239 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Models\User;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Repositories\DocumentationRepository;
|
||||
use App\Repositories\DocumentationVersionRepository;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\Documentation\DocumentationCommandHandler;
|
||||
use App\Services\DocumentationContent\ModelSyncCommand;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class DocumentationService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||
private readonly DocumentationRepository $documentationRepository,
|
||||
private readonly DocumentationCommandHandler $documentationCommandHandler,
|
||||
private readonly ModelSyncCommand $contentSaveCommand,
|
||||
) { }
|
||||
|
||||
public function index(int $projectId, int $versionId, DocumentationBuilderDto $documentationBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('viewAny', Documentation::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$defaultLanguage = $project->languages()->where('is_default', 1)->first();
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||
/** @var ?ProjectLanguage $defaultLanguage */
|
||||
$hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) {
|
||||
$query->where('language_id', $defaultLanguage->id);
|
||||
});
|
||||
}
|
||||
];
|
||||
$with = array_merge($with, $querySettingsDto->getQueryWith());
|
||||
|
||||
$documentations = $this->documentationRepository->getDocumentations(
|
||||
$version->id,
|
||||
$documentationBuilderDto,
|
||||
$with
|
||||
)->pagination(
|
||||
$querySettingsDto->getLimit(),
|
||||
$querySettingsDto->getPage()
|
||||
);
|
||||
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'documentations' => $documentations,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('create', Documentation::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'documentation' => new Documentation(),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $projectId, int $versionId, int $documentationId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||
if (\is_null($documentation)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $documentation)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$withCategories = [];
|
||||
if ($documentation->category_id) {
|
||||
$withCategories[] = $documentation->category_id;
|
||||
}
|
||||
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'documentation' => $documentation,
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage, null, $withCategories),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
if ($user->cannot('create', Documentation::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($this->documentationRepository->isExistsSlug($versionId, $data->getSlug()) !== false) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$documentation = DB::transaction(function () use ($data, $version, $user, $project) {
|
||||
$dataDocumentation = $this->getDataDocumentation($data);
|
||||
$documentation = $this->documentationCommandHandler->handleStore($version, $dataDocumentation);
|
||||
$this->contentSaveCommand->execute($project, $documentation, $data->getContents());
|
||||
|
||||
return $documentation;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($documentation, __('Documentation created successfully'));
|
||||
}
|
||||
|
||||
public function update(int $projectId, int $versionId, int $documentationId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||
if (\is_null($documentation)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $documentation)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($this->documentationRepository->isExistsSlug($versionId, $data->getSlug(), $documentation->id) !== false) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$documentation = DB::transaction(function () use ($data, $documentation, $project) {
|
||||
$dataDocumentation = $this->getDataDocumentation($data);
|
||||
$documentation = $this->documentationCommandHandler->handleUpdate($documentation, $dataDocumentation);
|
||||
$this->contentSaveCommand->execute($project, $documentation, $data->getContents());
|
||||
|
||||
return $documentation;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($documentation, __('Documentation updated successfully'));
|
||||
}
|
||||
|
||||
public function destroy(int $projectId, int $versionId, int $documentationId, User $user): ServiceResultError|ServiceResultSuccess
|
||||
{
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
$project = $version?->project;
|
||||
if (\is_null($version) || $project?->id !== $projectId) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||
if (\is_null($documentation)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('delete', $documentation)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($documentation) {
|
||||
$this->documentationCommandHandler->handleDestroy($documentation);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('Documentation successfully removed'));
|
||||
}
|
||||
|
||||
private function getDataDocumentation(StoreUpdate $data): array
|
||||
{
|
||||
return [
|
||||
'slug' => $data->getSlug(),
|
||||
'is_public' => $data->isPublic(),
|
||||
'sort' => $data->getSort(),
|
||||
'category_id' => $data->getCategoryId(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,222 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\User;
|
||||
use App\Repositories\DocumentationVersionRepository;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\DocumentationVersion\DocumentationVersionCommandHandler;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class DocumentationVersionService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||
private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler,
|
||||
) { }
|
||||
|
||||
public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('viewAny', DocumentationVersion::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$versions = $this->documentationVersionRepository->getVersions(
|
||||
$project->id,
|
||||
$documentationVersionBuilderDto,
|
||||
$querySettingsDto->getQueryWith()
|
||||
)->pagination(
|
||||
$querySettingsDto->getLimit(),
|
||||
$querySettingsDto->getPage()
|
||||
);
|
||||
|
||||
return $this->result([
|
||||
'versions' => $versions,
|
||||
'project' => $project,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
if (\is_null($version)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $version)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('create', DocumentationVersion::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'version' => new DocumentationVersion(),
|
||||
'statuses' => DocumentationVersionStatus::toCollection()->pluck( 'title', 'value')->toArray(),
|
||||
'project' => $project,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
if (\is_null($version)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $version)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'version' => $version,
|
||||
'statuses' => DocumentationVersionStatus::toCollection()->pluck( 'title', 'value')->toArray(),
|
||||
'project' => $project,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(int $projectId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
if ($user->cannot('create', DocumentationVersion::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($this->documentationVersionRepository->isExistsSlug($projectId, $data->getSlug()) !== false) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$version = DB::transaction(function () use ($data, $project, $user) {
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($version, __('Documentation version created successfully'));
|
||||
}
|
||||
|
||||
public function update(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
if (\is_null($version)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('update', $version)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($this->documentationVersionRepository->isExistsSlug($projectId, $data->getSlug(), $version->id) !== false) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$version = DB::transaction(function () use ($data, $version) {
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($version, __('Documentation version updated successfully'));
|
||||
}
|
||||
|
||||
public function destroy(int $projectId, int $versionId, User $user): ServiceResultError|ServiceResultSuccess
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||
if (\is_null($version)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('delete', $version)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($version) {
|
||||
$this->documentationVersionCommandHandler->handleDestroy($version);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('Documentation version successfully removed'));
|
||||
}
|
||||
|
||||
private function getDataVersion(StoreUpdate $data): array
|
||||
{
|
||||
return [
|
||||
'title' => $data->getTitle(),
|
||||
'slug' => $data->getSlug(),
|
||||
'is_public' => $data->isPublic(),
|
||||
'status' => $data->getStatus(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Documentation;
|
||||
|
||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, DocumentationBuilderDto $documentationBuilderDto): Relation | Builder
|
||||
{
|
||||
if ($documentationBuilderDto->isPublic() !== null) {
|
||||
$query->where('is_public', $documentationBuilderDto->isPublic());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Documentation;
|
||||
|
||||
use App\Models\Documentation;
|
||||
use App\Models\DocumentationVersion;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class DocumentationCommandHandler
|
||||
{
|
||||
public function handleStore(DocumentationVersion $version, array $data): Documentation
|
||||
{
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
return $version->documentations()->create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(Documentation $documentation, array $data): Documentation
|
||||
{
|
||||
if (isset($data['slug'])) {
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
}
|
||||
|
||||
$documentation->update($data);
|
||||
$documentation->touch();
|
||||
|
||||
return $documentation;
|
||||
}
|
||||
|
||||
public function handleDestroy(Documentation $documentation): void
|
||||
{
|
||||
$documentation->update([
|
||||
'slug' => $documentation->slug . '#delete:' . $documentation->id,
|
||||
]);
|
||||
$documentation->delete();
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationCategory;
|
||||
|
||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto): Relation | Builder
|
||||
{
|
||||
if ($documentationCategoryBuilderDto->isPublic() !== null) {
|
||||
$query->where('is_public', $documentationCategoryBuilderDto->isPublic());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationCategory;
|
||||
|
||||
use App\Exceptions\Services\DocumentationCategory\ParentException;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationVersion;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class DocumentationCategoryCommandHandler
|
||||
{
|
||||
public function handleStore(DocumentationVersion $version, array $data): DocumentationCategory
|
||||
{
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
return $version->categories()->create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(DocumentationCategory $category, array $data): DocumentationCategory
|
||||
{
|
||||
if (!empty($data['parent_id'])) {
|
||||
$category->parent_id = $data['parent_id'];
|
||||
if ($category->ancestors()->withTrashed()->where('id', $category->id)->exists()) {
|
||||
throw new ParentException('Category ID occurs in the parent chain (category: ' . $category->id . ', parent_id: ' . $category->parent_id . ').');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['slug'])) {
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
}
|
||||
|
||||
$category->update($data);
|
||||
$category->touch();
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
public function handleDestroy(DocumentationCategory $category): void
|
||||
{
|
||||
$category->update([
|
||||
'slug' => $category->slug . '#delete:' . $category->id,
|
||||
]);
|
||||
$category->delete();
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationCategoryContent;
|
||||
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Contents;
|
||||
use App\Exceptions\Services\DocumentationCategoryContent\ContentSaveException;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\Project;
|
||||
|
||||
final readonly class ModelSyncCommand
|
||||
{
|
||||
public function execute(Project $project, DocumentationCategory $category, Contents $contents): void
|
||||
{
|
||||
$languages = $project->languages;
|
||||
$categoryContents = $category->contents;
|
||||
|
||||
$newContents = [];
|
||||
foreach ($contents->getContents() as $content) {
|
||||
/** @var Content $content */
|
||||
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||
if (!$language) {
|
||||
throw new ContentSaveException('Language not found: ' . $content->getLanguageId());
|
||||
}
|
||||
|
||||
$model = $categoryContents->firstWhere('language_id', $language->id);
|
||||
$data = $this->getData($content);
|
||||
if (\is_null($model)) {
|
||||
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||
continue;
|
||||
}
|
||||
$model->update($data);
|
||||
}
|
||||
|
||||
if (!empty($newContents)) {
|
||||
$category->contents()->createMany($newContents);
|
||||
}
|
||||
}
|
||||
|
||||
private function getData(Content $content): array
|
||||
{
|
||||
return [
|
||||
'title' => $content->getTitle(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationContent;
|
||||
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||
use App\Exceptions\Services\DocumentationContent\ContentSaveException;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\Project;
|
||||
|
||||
final readonly class ModelSyncCommand
|
||||
{
|
||||
public function execute(Project $project, Documentation $documentation, Contents $contents): void
|
||||
{
|
||||
$languages = $project->languages;
|
||||
$documentationContents = $documentation->contents;
|
||||
|
||||
$newContents = [];
|
||||
foreach ($contents->getContents() as $content) {
|
||||
/** @var Content $content */
|
||||
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||
if (!$language) {
|
||||
throw new ContentSaveException('Language not found: ' . $content->getLanguageId());
|
||||
}
|
||||
|
||||
$model = $documentationContents->firstWhere('language_id', $language->id);
|
||||
$data = $this->getData($content);
|
||||
if (\is_null($model)) {
|
||||
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||
continue;
|
||||
}
|
||||
$model->update($data);
|
||||
}
|
||||
|
||||
if (!empty($newContents)) {
|
||||
$documentation->contents()->createMany($newContents);
|
||||
}
|
||||
}
|
||||
|
||||
private function getData(Content $content): array
|
||||
{
|
||||
return [
|
||||
'title' => $content->getTitle(),
|
||||
'content' => $content->getContent(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationVersion;
|
||||
|
||||
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, DocumentationVersionBuilderDto $documentationVersionBuilderDto): Relation | Builder
|
||||
{
|
||||
if ($documentationVersionBuilderDto->isPublic() !== null) {
|
||||
$query->where('is_public', $documentationVersionBuilderDto->isPublic());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationVersion;
|
||||
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class DocumentationVersionCommandHandler
|
||||
{
|
||||
public function handleStore(Project $project, array $data): DocumentationVersion
|
||||
{
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
|
||||
return $project->documentationVersions()->create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(DocumentationVersion $version, array $data): DocumentationVersion
|
||||
{
|
||||
if (isset($data['slug'])) {
|
||||
$data['slug'] = Str::lower($data['slug']);
|
||||
}
|
||||
|
||||
$version->update($data);
|
||||
$version->touch();
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
public function handleDestroy(DocumentationVersion $version): void
|
||||
{
|
||||
$version->update([
|
||||
'slug' => $version->slug . '#delete:' . $version->id,
|
||||
]);
|
||||
$version->delete();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user