Added the ability to add project information.

This commit is contained in:
2024-04-14 15:27:51 +05:00
parent b7d0a2453e
commit 2769585de0
35 changed files with 862 additions and 14 deletions

View File

@@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace App\Dto\Service\Admin\Project\About;
use App\Dto\Service\Dto;
final readonly class StoreUpdate extends Dto
{
public function __construct(
private string $title,
private string $description,
) { }
public function getTitle(): string
{
return $this->title;
}
public function getDescription(): string
{
return $this->description;
}
}

View File

@@ -8,6 +8,7 @@ enum Permission: string
case Role = 'role';
case User = 'user';
case Project = 'project';
case ProjectContent = 'project-content';
public function getPermissions(): array
{
@@ -15,6 +16,11 @@ enum Permission: string
self::AdminPanel => [
'view' => __('permissions.Administrative panel allowed'),
],
self::ProjectContent => [
'view' => __('permissions.Allowed to watch'),
'create' => __('permissions.Allowed to create'),
'update' => __('permissions.Allowed to edit'),
],
default => $this->getBasePermissions()
};

View File

@@ -0,0 +1,54 @@
<?php declare(strict_types=1);
namespace App\Http\Controllers\Admin\Projects;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests\Admin\Projects\About\StoreUpdateRequest;
use App\Services\Admin\Project\AboutService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
final class AboutController extends Controller
{
public function __construct(
private readonly AboutService $aboutService,
) { }
public function languages(int $project, Request $request): View
{
$user = $request->user();
$result = $this->aboutService->languages($project, $user);
if ($result->isError()) {
$this->errors($result);
}
return view('admin/projects/about/languages', $result->getData());
}
public function edit(int $project, int $language, Request $request): View
{
$user = $request->user();
$result = $this->aboutService->edit($project, $language, $user);
if ($result->isError()) {
$this->errors($result);
}
return view('admin/projects/about/edit', $result->getData());
}
public function update(int $project, int $language, StoreUpdateRequest $request): RedirectResponse
{
$data = $request->getDto();
$user = $request->user();
$result = $this->aboutService->storeOrUpdate($project, $language, $data, $user);
if ($result->isError()) {
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
}
return redirect()->route('admin.projects.about.edit', [
'project' => $project,
'language' => $language,
])->withSuccess($result->getMessage());
}
}

View File

@@ -34,6 +34,17 @@ final class ProjectsController extends Controller
return view('admin/projects/index', $result->getData());
}
public function show(int $id, Request $request): View
{
$user = $request->user();
$result = $this->projectService->show($id, $user);
if ($result->isError()) {
$this->errors($result);
}
return view('admin/projects/show', $result->getData());
}
public function create(Request $request): View
{
$user = $request->user();

View File

@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace App\Http\Requests\Admin\Projects\About;
use App\Contracts\FormRequestDto;
use App\Dto\Service\Admin\Project\About\StoreUpdate;
use Illuminate\Foundation\Http\FormRequest;
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255',],
'description' => ['nullable', 'string',],
];
}
public function getDto(): StoreUpdate
{
return new StoreUpdate(
title: $this->input('title'),
description: $this->input('description'),
);
}
}

View File

@@ -47,4 +47,9 @@ final class Project extends Model implements StorageContract
{
return $this->hasMany(ProjectLanguage::class);
}
public function contents(): HasMany
{
return $this->hasMany(ProjectContent::class);
}
}

View File

@@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
final class ProjectContent extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'project_content';
protected $fillable = [
'title',
'description',
];
}

View File

@@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace App\Policies;
use App\Models\ProjectContent;
use App\Models\User;
final readonly class ProjectContentPolicy extends Policy
{
public function viewAny(User $user): bool
{
return $user->hasPermission('project-content.view');
}
public function view(User $user, ProjectContent $projectContent): bool
{
return $user->hasPermission('project-content.view');
}
public function create(User $user): bool
{
return $user->hasPermission('project-content.create');
}
public function update(User $user, ProjectContent $projectContent): bool
{
return $user->hasPermission('project-content.update');
}
}

View File

@@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace App\Repositories;
use App\Models\ProjectContent;
final class ProjectContentRepository
{
public function getContentByLanguageId(int $projectId, int $languageId): ?ProjectContent
{
return ProjectContent::query()
->where(['project_id' => $projectId, 'language_id' => $languageId])
->first();
}
}

View File

@@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace App\Repositories;
use App\Models\ProjectLanguage;
final class ProjectLanguageRepository
{
public function isExistsLanguageById(int $projectId, int $languageId): bool
{
return ProjectLanguage::query()
->where(['id' => $languageId, 'project_id' => $projectId])
->exists();
}
}

View File

@@ -0,0 +1,132 @@
<?php declare(strict_types=1);
namespace App\Services\Admin\Project;
use App\Contracts\ServiceResultError;
use App\Dto\Service\Admin\Project\About\StoreUpdate;
use App\Models\ProjectContent;
use App\Models\User;
use App\Repositories\ProjectContentRepository;
use App\Repositories\ProjectLanguageRepository;
use App\Repositories\ProjectRepository;
use App\ServiceResults\ServiceResultArray;
use App\ServiceResults\StoreUpdateResult;
use App\Services\ProjectContent\ProjectContentCommandHandler;
use App\Services\Service;
use Illuminate\Support\Facades\DB;
final class AboutService extends Service
{
public function __construct(
private readonly ProjectRepository $projectRepository,
private readonly ProjectLanguageRepository $projectLanguageRepository,
private readonly ProjectContentRepository $projectContentRepository,
private readonly ProjectContentCommandHandler $projectContentCommandHandler,
) { }
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
{
$project = $this->projectRepository->getProjectById($projectId);
if (is_null($project)) {
return $this->errNotFound(__('Not Found'));
}
if ($user->cannot('viewAny', ProjectContent::class)) {
return $this->errFobidden(__('Access is denied'));
}
return $this->result([
'project' => $project,
]);
}
public function edit(int $projectId, int $languageId, User $user): ServiceResultError | ServiceResultArray
{
$project = $this->projectRepository->getProjectById($projectId);
$language = $project?->languages()->firstWhere('id', $languageId);
if (\is_null($project) || \is_null($language)) {
return $this->errNotFound(__('Not Found'));
}
$content = $project->contents()->firstWhere('language_id', $languageId);
if (\is_null($content)) {
if ($user->cannot('create', ProjectContent::class)) {
return $this->errFobidden(__('Access is denied'));
}
$content = new ProjectContent();
$content->project_id = $project->id;
$content->language_id = $language->id;
} else if($user->cannot('update', $content)) {
return $this->errFobidden(__('Access is denied'));
}
return $this->result([
'project' => $project,
'language' => $language,
'content' => $content,
]);
}
public function storeOrUpdate(int $projectId, int $languageId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
{
if (! $this->projectLanguageRepository->isExistsLanguageById($projectId, $languageId)) {
return $this->errNotFound(__('Not Found'));
}
$content = $this->projectContentRepository->getContentByLanguageId($projectId, $languageId);
if (is_null($content)) {
return $this->store($projectId, $languageId, $data, $user);
}
return $this->update($content, $data, $user);
}
private function store(int $projectId, int $languageId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
{
if ($user->cannot('create', ProjectContent::class)) {
return $this->errFobidden(__('Access is denied'));
}
try {
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId) {
$dataAboutProject = $this->getDataAboutProject($data);
return $this->projectContentCommandHandler->handleStore($projectId, $languageId, $dataAboutProject);
});
} catch (\Throwable $e) {
report($e);
return $this->errService(__('Server Error'));
}
return $this->resultStoreUpdateModel($aboutProject, __('Project information has been successfully updated'));
}
private function update(ProjectContent $content, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
{
if ($user->cannot('update', $content)) {
return $this->errFobidden(__('Access is denied'));
}
try {
$aboutProject = DB::transaction(function () use ($data, $content) {
$dataAboutProject = $this->getDataAboutProject($data);
return $this->projectContentCommandHandler->handleUpdate($content, $dataAboutProject);
});
} catch (\Throwable $e) {
report($e);
return $this->errService(__('Server Error'));
}
return $this->resultStoreUpdateModel($aboutProject, __('Project information has been successfully updated'));
}
private function getDataAboutProject(StoreUpdate $data): array
{
return [
'title' => $data->getTitle(),
'description' => $data->getDescription(),
];
}
}

View File

@@ -50,6 +50,23 @@ final class ProjectService extends Service
]);
}
public function show(int $id, User $user): ServiceResultError | ServiceResultArray
{
$project = $this->projectRepository->getProjectById($id);
if (is_null($project)) {
return $this->errNotFound(__('Not Found'));
}
if ($user->cannot('view', $project)) {
return $this->errFobidden(__('Access is denied'));
}
return $this->result([
'project' => $project,
]);
}
public function create(User $user): ServiceResultError | ServiceResultArray
{
if ($user->cannot('create', Project::class)) {

View File

@@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace App\Services\ProjectContent;
use App\Models\ProjectContent;
final readonly class ProjectContentCommandHandler
{
public function handleStore(int $projectId, int $languageId, array $data): ProjectContent
{
$content = new ProjectContent();
$content->project_id = $projectId;
$content->language_id = $languageId;
$content->fill($data);
$content->save();
return $content;
}
public function handleUpdate(ProjectContent $content, array $data): ProjectContent
{
$content->update($data);
return $content;
}
}

View File

@@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace App\View\Components\Volt\Forms;
use Illuminate\Support\Str;
use Illuminate\View\View;
final class Textarea extends Form
{
public function __construct(
private readonly string $title,
private readonly string $name,
private readonly ?string $value = '',
) { }
protected function getName(): string
{
return $this->name;
}
private function getTitle(): string
{
return Str::ucfirst($this->title);
}
private function getValue(): string
{
return (string) old($this->getRequestName(), $this->value);
}
/**
* @inheritDoc
*/
public function render(): View
{
return view('components.volt.forms.textarea', [
'title' => $this->getTitle(),
'name' => $this->getName(),
'requestName' => $this->getRequestName(),
'value' => $this->getValue(),
]);
}
}

View File

@@ -0,0 +1,46 @@
<?php declare(strict_types=1);
namespace App\View\Components\Volt\Forms;
use Illuminate\Support\Str;
use Illuminate\View\View;
final class TextareaWysiwyg extends Form
{
public function __construct(
private readonly string $title,
private readonly string $name,
private readonly ?string $value = '',
) { }
protected function getName(): string
{
return $this->name;
}
private function getTitle(): string
{
return Str::ucfirst($this->title);
}
private function getValue(): string
{
return (string) old($this->getRequestName(), $this->value);
}
/**
* @inheritDoc
*/
public function render(): View
{
$tinymceLicenseKey = config('tinymce.license_key');
return view('components.volt.forms.textarea-wysiwyg', [
'tinymceLicenseKey' => $tinymceLicenseKey,
'title' => $this->getTitle(),
'name' => $this->getName(),
'requestName' => $this->getRequestName(),
'value' => $this->getValue(),
]);
}
}