A documentation section has been added to the site.
This commit is contained in:
@@ -2,14 +2,22 @@
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
use App\Dto\Builder\DocumentationCategory\Category;
|
||||
|
||||
final readonly class Documentation
|
||||
{
|
||||
public function __construct(
|
||||
private ?bool $isPublic = null,
|
||||
private ?bool $isPublic = null,
|
||||
private ?Category $categoryId = null,
|
||||
) { }
|
||||
|
||||
public function isPublic(): ?bool
|
||||
{
|
||||
return $this->isPublic;
|
||||
}
|
||||
|
||||
public function getCategoryId(): ?Category
|
||||
{
|
||||
return $this->categoryId;
|
||||
}
|
||||
}
|
||||
|
@@ -2,14 +2,22 @@
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
use App\Dto\Builder\DocumentationCategory\Category;
|
||||
|
||||
final readonly class DocumentationCategory
|
||||
{
|
||||
public function __construct(
|
||||
private ?bool $isPublic = null,
|
||||
private ?bool $isPublic = null,
|
||||
private ?Category $parentId = null,
|
||||
) { }
|
||||
|
||||
public function isPublic(): ?bool
|
||||
{
|
||||
return $this->isPublic;
|
||||
}
|
||||
|
||||
public function getParentId(): ?Category
|
||||
{
|
||||
return $this->parentId;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder\DocumentationCategory;
|
||||
|
||||
final readonly class Category
|
||||
{
|
||||
public function __construct(
|
||||
private ?int $categoryId,
|
||||
) { }
|
||||
|
||||
public function getCategoryId(): ?int
|
||||
{
|
||||
return $this->categoryId;
|
||||
}
|
||||
|
||||
public function isCategoryNull(): bool
|
||||
{
|
||||
return $this->getCategoryId() === null;
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Dto\Service\Admin\Project\Translation;
|
||||
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Exceptions\Dto\Admin\Project\Transaction\TranslationsException;
|
||||
|
||||
final class Translations
|
||||
@@ -23,7 +24,7 @@ final class Translations
|
||||
|
||||
public static function getTranslationCodes(): array
|
||||
{
|
||||
return [
|
||||
$translations = [
|
||||
'site.Menu',
|
||||
'site.Powered by service',
|
||||
'site.About project',
|
||||
@@ -38,6 +39,17 @@ final class Translations
|
||||
'site.attributes.message',
|
||||
'site.Message sent successfully',
|
||||
'Server Error',
|
||||
'site.Documentation',
|
||||
'site.Documentation not created',
|
||||
'site.Choose version',
|
||||
'site.alert-status-not-supported',
|
||||
'site.alert-status-future',
|
||||
];
|
||||
|
||||
foreach (DocumentationVersionStatus::cases() as $status) {
|
||||
$translations[] = $status->getCodeForTranslation();
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
}
|
||||
|
47
app/application/app/Dto/Service/Site/Documentation.php
Normal file
47
app/application/app/Dto/Service/Site/Documentation.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Site;
|
||||
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Services\WebsiteTranslations;
|
||||
|
||||
final readonly class Documentation
|
||||
{
|
||||
public function __construct(
|
||||
private Project $project,
|
||||
private DocumentationVersion $version,
|
||||
private WebsiteTranslations $websiteTranslations,
|
||||
private ?User $user = null,
|
||||
) { }
|
||||
|
||||
public function getProject(): Project
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
public function getVersion(): DocumentationVersion
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function getWebsiteTranslations(): WebsiteTranslations
|
||||
{
|
||||
return $this->websiteTranslations;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'project' => $this->getProject(),
|
||||
'version' => $this->getVersion(),
|
||||
'websiteTranslations' => $this->getWebsiteTranslations(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ enum CacheTag: string
|
||||
{
|
||||
case Project = 'project';
|
||||
case ProjectTranslation = 'project_translation';
|
||||
case DocumantationVersion = 'documantation_version';
|
||||
|
||||
public function getCache(): TaggedCache
|
||||
{
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
enum DocumentationVersionStatus: int
|
||||
@@ -11,9 +12,18 @@ enum DocumentationVersionStatus: int
|
||||
case CurrentVersion = 100;
|
||||
case FutureVersion = 150;
|
||||
|
||||
public function getTitle(): string
|
||||
public function getTitle(?WebsiteTranslations $websiteTranslations = null): string
|
||||
{
|
||||
return __('version-status.' . $this->name);
|
||||
if (\is_null($websiteTranslations)) {
|
||||
return __($this->getCodeForTranslation());
|
||||
}
|
||||
|
||||
return $websiteTranslations->translate($this->getCodeForTranslation());
|
||||
}
|
||||
|
||||
public function getCodeForTranslation(): string
|
||||
{
|
||||
return 'version-status.' . $this->name;
|
||||
}
|
||||
|
||||
public static function toArray(): array
|
||||
|
@@ -5,15 +5,18 @@ namespace App\Enums\Site;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectLanguage;
|
||||
|
||||
enum ProjectSection
|
||||
enum ProjectSection: string
|
||||
{
|
||||
case Home;
|
||||
case Feedback;
|
||||
case FeedbackSend;
|
||||
case Home = 'home';
|
||||
case Feedback = 'feedback';
|
||||
case FeedbackSend = 'feedback.send';
|
||||
case Documentation = 'documentation';
|
||||
case DocumentationVersion = 'documentation.version';
|
||||
case DocumentationCategory = 'documentation.category';
|
||||
case DocumentationView = 'documentation.view';
|
||||
|
||||
public function url(Project $project, ?ProjectLanguage $language = null): string
|
||||
public function url(Project $project, ?ProjectLanguage $language = null, array $parameters = []): string
|
||||
{
|
||||
$parameters = [];
|
||||
$prefixProject = '';
|
||||
if ($project->http_host === null) {
|
||||
$prefixProject = 'project.';
|
||||
@@ -26,12 +29,7 @@ enum ProjectSection
|
||||
$prefixLanguage = '-language';
|
||||
}
|
||||
|
||||
$route = match ($this) {
|
||||
self::Home => \route($prefixProject . 'home' . $prefixLanguage, $parameters, false),
|
||||
|
||||
self::Feedback => \route($prefixProject . 'feedback' . $prefixLanguage, $parameters, false),
|
||||
self::FeedbackSend => \route($prefixProject . 'feedback.send' . $prefixLanguage, $parameters, false),
|
||||
};
|
||||
$route = \route($prefixProject . $this->value . $prefixLanguage, $parameters, false);
|
||||
|
||||
return $project->http_host . $route;
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Site;
|
||||
|
||||
use App\Dto\Service\Site\Documentation;
|
||||
use App\Services\Site\DocumentationService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class DocumentationController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationService $documentationService,
|
||||
) { }
|
||||
|
||||
public function defaultVersion(Request $request): RedirectResponse | View
|
||||
{
|
||||
$project = $request->get('project');
|
||||
$websiteTranslations = $request->get('websiteTranslations');
|
||||
|
||||
$result = $this->documentationService->defaultVersion($project, $request->user());
|
||||
if ($result->isError()) {
|
||||
if ($result->getCode() === 404) {
|
||||
return view('site.projects.documentation.no-default-version', [
|
||||
'project' => $project,
|
||||
'websiteTranslations' => $websiteTranslations,
|
||||
]);
|
||||
}
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
$url = \App\Enums\Site\ProjectSection::DocumentationVersion->url($project, $websiteTranslations->getLanguage(), ['version' => $result->getVersion()->slug]);
|
||||
/**
|
||||
* 302 redirect because the documentation version can change at any time.
|
||||
*/
|
||||
return redirect($url, 302);
|
||||
}
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$documentation = new Documentation(
|
||||
project: $request->get('project'),
|
||||
version: $request->get('version'),
|
||||
websiteTranslations: $request->get('websiteTranslations'),
|
||||
user: $request->user(),
|
||||
);
|
||||
$result = $this->documentationService->index($documentation);
|
||||
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('site.projects.documentation.index', $result->getData());
|
||||
}
|
||||
|
||||
public function category(string $slug, Request $request): View
|
||||
{
|
||||
$documentation = new Documentation(
|
||||
project: $request->get('project'),
|
||||
version: $request->get('version'),
|
||||
websiteTranslations: $request->get('websiteTranslations'),
|
||||
user: $request->user(),
|
||||
);
|
||||
$result = $this->documentationService->category($slug, $documentation);
|
||||
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
if ($result->isTranslation()) {
|
||||
return $this->viewPageWithoutTranslation($result);
|
||||
}
|
||||
|
||||
return view('site.projects.documentation.category', $result->getData());
|
||||
}
|
||||
|
||||
public function view(string $slug, Request $request): View
|
||||
{
|
||||
$documentation = new Documentation(
|
||||
project: $request->get('project'),
|
||||
version: $request->get('version'),
|
||||
websiteTranslations: $request->get('websiteTranslations'),
|
||||
user: $request->user(),
|
||||
);
|
||||
$result = $this->documentationService->view($slug, $documentation);
|
||||
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
if ($result->isTranslation()) {
|
||||
return $this->viewPageWithoutTranslation($result);
|
||||
}
|
||||
|
||||
return view('site.projects.documentation.view', $result->getData());
|
||||
}
|
||||
}
|
40
app/application/app/Http/Middleware/DocumentationVersion.php
Normal file
40
app/application/app/Http/Middleware/DocumentationVersion.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final readonly class DocumentationVersion
|
||||
{
|
||||
public function handle(Request $request, \Closure $next): Response
|
||||
{
|
||||
$project = $request->get('project');
|
||||
$versionSlug = $request->route()?->parameter('version');
|
||||
if ($versionSlug === null || $project === null) {
|
||||
\abort(Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$seconds = 3600;
|
||||
$version = CacheTag::DocumantationVersion->getCache()->remember(self::class . $project->id . '-' . $versionSlug, $seconds, function () use ($project, $versionSlug) {
|
||||
return $project->documentationVersions()->where('slug', $versionSlug)->first() ?? false;
|
||||
});
|
||||
if ($version === false) {
|
||||
\abort(Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
unset($request->route()->parameters['version']);
|
||||
|
||||
$request->attributes->set('version', $version);
|
||||
|
||||
if (
|
||||
$version->is_public === false
|
||||
&& ( $request->user() === null || $request->user()->cannot('view', $version) )
|
||||
) {
|
||||
\abort(Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Scopes\SortScope;
|
||||
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -9,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[ScopedBy([SortScope::class])]
|
||||
final class Documentation extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -37,6 +38,16 @@ final class DocumentationVersion extends Model
|
||||
'status',
|
||||
];
|
||||
|
||||
/**
|
||||
* The "booted" method of the model.
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::addGlobalScope('status', function (Builder $builder) {
|
||||
$builder->orderBy('status', 'desc');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
|
@@ -59,6 +59,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
Route::pattern('language', '[a-z_]+');
|
||||
Route::pattern('project', '[a-z0-9_-]+');
|
||||
Route::pattern('slug', '[a-z0-9._-]+');
|
||||
Route::pattern('version', '[a-z0-9._-]+');
|
||||
|
||||
$this->configureRateLimiting();
|
||||
Gate::define('AdminPanel', [\App\Policies\AdminPanel::class, 'view']);
|
||||
|
@@ -4,6 +4,7 @@ namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Services\DocumentationCategory\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
@@ -35,9 +36,19 @@ final readonly class DocumentationCategoryRepository
|
||||
return DocumentationCategory::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getCategoryByCode(string $code): ?DocumentationCategory
|
||||
public function getCategoryBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?DocumentationCategory
|
||||
{
|
||||
return DocumentationCategory::query()->where('code', $code)->first();
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($language) {
|
||||
$hasOne->where('language_id', $language->id);
|
||||
}
|
||||
];
|
||||
|
||||
return DocumentationCategory::query()
|
||||
->where('version_id', $versionId)
|
||||
->where('slug', $slug)
|
||||
->with($with)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
||||
|
@@ -5,9 +5,11 @@ namespace App\Repositories;
|
||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use App\Contracts\Search;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Services\Documentation\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class DocumentationRepository
|
||||
@@ -32,9 +34,19 @@ final readonly class DocumentationRepository
|
||||
return Documentation::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getDocumentationByCode(string $code): ?Documentation
|
||||
public function getDocumentationBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?Documentation
|
||||
{
|
||||
return Documentation::query()->where('code', $code)->first();
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($language) {
|
||||
$hasOne->where('language_id', $language->id);
|
||||
}
|
||||
];
|
||||
|
||||
return Documentation::query()
|
||||
->where('version_id', $versionId)
|
||||
->where('slug', $slug)
|
||||
->with($with)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults\Site\DocumentationService;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\ServiceResults\ServiceResult;
|
||||
|
||||
final class DefaultVersion extends ServiceResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationVersion $version,
|
||||
) { }
|
||||
|
||||
public function getVersion(): DocumentationVersion
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ 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\CacheTag;
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\User;
|
||||
@@ -14,6 +15,7 @@ use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\ClearCacheCommandHandler;
|
||||
use App\Services\DocumentationVersion\DocumentationVersionCommandHandler;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -24,6 +26,7 @@ final class DocumentationVersionService extends Service
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||
private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler,
|
||||
private readonly ClearCacheCommandHandler $clearCacheCommandHandler,
|
||||
) { }
|
||||
|
||||
public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
@@ -138,6 +141,7 @@ final class DocumentationVersionService extends Service
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -174,6 +178,7 @@ final class DocumentationVersionService extends Service
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -202,6 +207,7 @@ final class DocumentationVersionService extends Service
|
||||
DB::transaction(function () use ($version) {
|
||||
$this->documentationVersionCommandHandler->handleDestroy($version);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
|
@@ -10,10 +10,12 @@ 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;
|
||||
return $query
|
||||
->when($documentationBuilderDto->isPublic(), function (Builder $query) use ($documentationBuilderDto) {
|
||||
$query->where('is_public', $documentationBuilderDto->isPublic());
|
||||
})
|
||||
->when($documentationBuilderDto->getCategoryId(), function (Builder $query) use ($documentationBuilderDto) {
|
||||
$query->where('category_id', $documentationBuilderDto->getCategoryId()->getCategoryId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -10,10 +10,12 @@ 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;
|
||||
return $query
|
||||
->when($documentationCategoryBuilderDto->isPublic(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||
$query->where('is_public', $documentationCategoryBuilderDto->isPublic());
|
||||
})
|
||||
->when($documentationCategoryBuilderDto->getParentId(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||
$query->where('parent_id', $documentationCategoryBuilderDto->getParentId()->getCategoryId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
151
app/application/app/Services/Site/DocumentationService.php
Normal file
151
app/application/app/Services/Site/DocumentationService.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Site;
|
||||
|
||||
use App\Dto\Service\Site\Documentation;
|
||||
use App\Enums\CacheTag;
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\Documentation as ModelDocumentation;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Repositories\DocumentationRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\Site\DocumentationService\DefaultVersion;
|
||||
use app\ServiceResults\Site\PagePossibleWithoutTranslation;
|
||||
use App\Services\Service;
|
||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class DocumentationService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||
private readonly DocumentationRepository $documentationRepository
|
||||
) { }
|
||||
|
||||
public function defaultVersion(Project $project, ?User $user): ServiceResultError | DefaultVersion
|
||||
{
|
||||
$seconds = 3600;
|
||||
$isPublic = null;
|
||||
if ($user?->cannot('viewAny', DocumentationVersion::class)) {
|
||||
$isPublic = 1;
|
||||
}
|
||||
$version = CacheTag::DocumantationVersion->getCache()
|
||||
->remember(self::class . $project->id . '_' . $isPublic ?? 0, $seconds, function () use ($project, $isPublic) {
|
||||
$versions = $project->documentationVersions()
|
||||
->when($isPublic, function (Builder $query) {
|
||||
$query->where('is_public', 1);
|
||||
})
|
||||
->limit(10)
|
||||
->get();
|
||||
return $versions->firstWhere('status', DocumentationVersionStatus::CurrentVersion)
|
||||
??
|
||||
$versions->first() ?? false;
|
||||
});
|
||||
|
||||
if ($version === false) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
return new DefaultVersion($version);
|
||||
}
|
||||
|
||||
public function index(Documentation $documentation): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
return $this->result(array_merge($documentation->toArray(), [
|
||||
'categories' => $this->getCategories($documentation, null),
|
||||
'documentations' => $this->getDocumentations($documentation, null),
|
||||
]));
|
||||
}
|
||||
|
||||
public function category(string $slug, Documentation $documentation): ServiceResultError | PagePossibleWithoutTranslation
|
||||
{
|
||||
$category = $this->documentationCategoryRepository->getCategoryBySlugWithContent($slug, $documentation->getVersion()->id, $documentation->getWebsiteTranslations()->getLanguage());
|
||||
if (!$category) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
if (
|
||||
$category->is_public === false &&
|
||||
($documentation->getUser() === null || $documentation->getUser()->cannot('view', $category))
|
||||
) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$data = array_merge($documentation->toArray(), [
|
||||
'category' => $category,
|
||||
'categories' => $this->getCategories($documentation, $category->id),
|
||||
'documentations' => $this->getDocumentations($documentation, $category->id),
|
||||
]);
|
||||
return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($category->content?->title));
|
||||
}
|
||||
|
||||
public function view(string $slug, Documentation $documentation): ServiceResultError | PagePossibleWithoutTranslation
|
||||
{
|
||||
$document = $this->documentationRepository->getDocumentationBySlugWithContent($slug, $documentation->getVersion()->id, $documentation->getWebsiteTranslations()->getLanguage());
|
||||
if (!$document) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
if (
|
||||
$document->is_public === false &&
|
||||
($documentation->getUser() === null || $documentation->getUser()->cannot('view', $document))
|
||||
) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$data = array_merge($documentation->toArray(), [
|
||||
'documentation' => $document,
|
||||
]);
|
||||
return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($document->content?->title));
|
||||
}
|
||||
|
||||
private function getCategories(Documentation $documentation, ?int $parentId): Collection
|
||||
{
|
||||
$isPublic = null;
|
||||
if ($documentation->getUser() === null || $documentation->getUser()->cannot('viewAny', DocumentationCategory::class)) {
|
||||
$isPublic = true;
|
||||
}
|
||||
$builderDto = new DocumentationCategoryBuilderDto(
|
||||
isPublic: $isPublic,
|
||||
parentId: new DocumentationCategoryBuilderDto\Category($parentId),
|
||||
);
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($documentation) {
|
||||
$hasOne->where('language_id', $documentation->getWebsiteTranslations()->getLanguage()->id);
|
||||
}
|
||||
];
|
||||
return $this->documentationCategoryRepository->getCategories(
|
||||
$documentation->getVersion()->id,
|
||||
$builderDto,
|
||||
$with
|
||||
)->all();
|
||||
}
|
||||
|
||||
private function getDocumentations(Documentation $documentation, ?int $categoryId): Collection
|
||||
{
|
||||
$isPublic = null;
|
||||
if ($documentation->getUser() === null || $documentation->getUser()->cannot('viewAny', ModelDocumentation::class)) {
|
||||
$isPublic = true;
|
||||
}
|
||||
$builderDto = new DocumentationBuilderDto(
|
||||
isPublic: $isPublic,
|
||||
categoryId: new DocumentationCategoryBuilderDto\Category($categoryId),
|
||||
);
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($documentation) {
|
||||
$hasOne->where('language_id', $documentation->getWebsiteTranslations()->getLanguage()->id);
|
||||
}
|
||||
];
|
||||
return $this->documentationRepository->getDocumentations(
|
||||
$documentation->getVersion()->id,
|
||||
$builderDto,
|
||||
$with
|
||||
)->all();
|
||||
}
|
||||
}
|
48
app/application/app/View/Components/Site/ChooseVersion.php
Normal file
48
app/application/app/View/Components/Site/ChooseVersion.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class ChooseVersion extends Component
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationVersion $version,
|
||||
private readonly WebsiteTranslations $websiteTranslations,
|
||||
private readonly Project $project,
|
||||
private readonly ?User $user,
|
||||
) { }
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$isPublic = null;
|
||||
if (\is_null($this->user) || $this->user->cannot('viewAny', DocumentationVersion::class)) {
|
||||
$isPublic = 1;
|
||||
}
|
||||
|
||||
$seconds = 3600 * 12;
|
||||
$versions = CacheTag::DocumantationVersion->getCache()
|
||||
->remember(self::class . $this->project->id . '-' . $isPublic ?? 0, $seconds, function () use ($isPublic) {
|
||||
return $this->project->documentationVersions()
|
||||
->when($isPublic, function (Builder $query) {
|
||||
$query->where('is_public', 1);
|
||||
})
|
||||
->get();
|
||||
});
|
||||
|
||||
return view('components.site.choose-version', [
|
||||
'websiteTranslations' => $this->websiteTranslations,
|
||||
'versions' => $versions,
|
||||
'version' => $this->version,
|
||||
'project' => $this->project,
|
||||
]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site;
|
||||
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class DocumentationVersion extends Component
|
||||
{
|
||||
public function __construct(
|
||||
private readonly \App\Models\DocumentationVersion $version,
|
||||
private readonly WebsiteTranslations $websiteTranslations,
|
||||
) { }
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.site.documentation-version', [
|
||||
'websiteTranslations' => $this->websiteTranslations,
|
||||
'version' => $this->version,
|
||||
]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user