Init git.
This commit is contained in:
95
app/application/app/Console/Commands/CreateUserAdmin.php
Normal file
95
app/application/app/Console/Commands/CreateUserAdmin.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Dto\Service\User\ManyRoleDto;
|
||||
use App\Enums\SystemRole;
|
||||
use App\Repositories\RoleRepository;
|
||||
use App\Rules\Username;
|
||||
use App\Services\User\UserCommandHandler;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
final class CreateUserAdmin extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:create-user-admin {email} {username} {password}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create admin user.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(UserCommandHandler $userCommandHandler, RoleRepository $roleRepository): void
|
||||
{
|
||||
$validator = $this->getData();
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->errorMessageAndStop($validator->errors()->all());
|
||||
}
|
||||
$data = $validator->valid();
|
||||
|
||||
try {
|
||||
$role = $roleRepository->getRoleByCode(SystemRole::Admin->value);
|
||||
if (is_null($role)) {
|
||||
$this->errorMessageAndStop('Administrator role not found.');
|
||||
}
|
||||
DB::transaction(function () use($data, $userCommandHandler, $role) {
|
||||
$data['name'] = 'Administrator';
|
||||
$user = $userCommandHandler->handleStore($data, $data['password']);
|
||||
$userCommandHandler->handleConfirmationByEmail($user);
|
||||
$roles = new ManyRoleDto([$role->id]);
|
||||
$userCommandHandler->handleSyncRoles($user, $roles);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->errorMessageAndStop($e->getMessage());
|
||||
}
|
||||
|
||||
$this->info('The command was successful!');
|
||||
}
|
||||
|
||||
private function getData(): Validator
|
||||
{
|
||||
return ValidatorFacade::make([
|
||||
'email' => $this->argument('email'),
|
||||
'username' => $this->argument('username'),
|
||||
'password' => $this->argument('password'),
|
||||
], [
|
||||
'email' => ['required', 'email', 'max:255', 'unique:users,email'],
|
||||
'username' => ['required', 'string', new Username(), 'unique:users,username'],
|
||||
'password' => ['required', Password::default()],
|
||||
]);
|
||||
}
|
||||
|
||||
private function stop(): never
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
private function errorMessageAndStop(string | array $error): never
|
||||
{
|
||||
$this->info('User not created. See error messages below:');
|
||||
|
||||
if (is_array($error)) {
|
||||
foreach ($error as $err) {
|
||||
$this->error($err);
|
||||
}
|
||||
} else {
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
$this->stop();
|
||||
}
|
||||
}
|
10
app/application/app/Contracts/FormRequestDto.php
Normal file
10
app/application/app/Contracts/FormRequestDto.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
interface FormRequestDto
|
||||
{
|
||||
public function getDto(): Dto;
|
||||
}
|
18
app/application/app/Contracts/Search.php
Normal file
18
app/application/app/Contracts/Search.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Pagination\CursorPaginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface Search
|
||||
{
|
||||
public function __construct(Relation | Builder $query);
|
||||
public function all(): Collection;
|
||||
public function get(int $limit): Collection;
|
||||
public function pagination(int $limit, int $page = 1): LengthAwarePaginator;
|
||||
public function cursorPaginate(int $limit): CursorPaginator;
|
||||
}
|
9
app/application/app/Contracts/ServiceResult.php
Normal file
9
app/application/app/Contracts/ServiceResult.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
interface ServiceResult
|
||||
{
|
||||
public function isSuccess(): bool;
|
||||
public function isError(): bool;
|
||||
}
|
12
app/application/app/Contracts/ServiceResultError.php
Normal file
12
app/application/app/Contracts/ServiceResultError.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
interface ServiceResultError
|
||||
{
|
||||
public function getCode(): ?int;
|
||||
public function getMessage(): string;
|
||||
public function getErrors(): array;
|
||||
public function getErrorsOrMessage(): array|string;
|
||||
public function getData(): array;
|
||||
}
|
10
app/application/app/Dto/Builder/Role.php
Normal file
10
app/application/app/Dto/Builder/Role.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
final readonly class Role
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
) { }
|
||||
}
|
10
app/application/app/Dto/Builder/User.php
Normal file
10
app/application/app/Dto/Builder/User.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Builder;
|
||||
|
||||
final readonly class User
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
) { }
|
||||
}
|
27
app/application/app/Dto/QuerySettingsDto.php
Normal file
27
app/application/app/Dto/QuerySettingsDto.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto;
|
||||
|
||||
final readonly class QuerySettingsDto
|
||||
{
|
||||
public function __construct(
|
||||
private int $limit,
|
||||
private int $page = 1,
|
||||
private array $queryWith = []
|
||||
) { }
|
||||
|
||||
public function getLimit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
public function getQueryWith(): array
|
||||
{
|
||||
return $this->queryWith;
|
||||
}
|
||||
}
|
21
app/application/app/Dto/Service/Admin/Role/Index.php
Normal file
21
app/application/app/Dto/Service/Admin/Role/Index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Role;
|
||||
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Service\Pages;
|
||||
|
||||
final readonly class Index extends Pages
|
||||
{
|
||||
public function __construct(
|
||||
private Role $roleBuilderDto,
|
||||
int $page
|
||||
) {
|
||||
parent::__construct($page);
|
||||
}
|
||||
|
||||
public function getRoleBuilderDto(): Role
|
||||
{
|
||||
return $this->roleBuilderDto;
|
||||
}
|
||||
}
|
29
app/application/app/Dto/Service/Admin/Role/StoreUpdate.php
Normal file
29
app/application/app/Dto/Service/Admin/Role/StoreUpdate.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Role;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private ?string $code,
|
||||
private array $permissions,
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return $this->permissions;
|
||||
}
|
||||
}
|
21
app/application/app/Dto/Service/Admin/User/Index.php
Normal file
21
app/application/app/Dto/Service/Admin/User/Index.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\User;
|
||||
|
||||
use App\Dto\Builder\User;
|
||||
use App\Dto\Service\Pages;
|
||||
|
||||
final readonly class Index extends Pages
|
||||
{
|
||||
public function __construct(
|
||||
private User $userBuilderDto,
|
||||
int $page
|
||||
) {
|
||||
parent::__construct($page);
|
||||
}
|
||||
|
||||
public function getUserBuilderDto(): User
|
||||
{
|
||||
return $this->userBuilderDto;
|
||||
}
|
||||
}
|
48
app/application/app/Dto/Service/Admin/User/StoreUpdate.php
Normal file
48
app/application/app/Dto/Service/Admin/User/StoreUpdate.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\User;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Dto\Service\User\ManyRoleDto;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $email,
|
||||
private string $username,
|
||||
private bool $isActive,
|
||||
private ManyRoleDto $roles,
|
||||
private ?string $password = null
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getRoles(): ManyRoleDto
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->isActive;
|
||||
}
|
||||
}
|
27
app/application/app/Dto/Service/Authorization.php
Normal file
27
app/application/app/Dto/Service/Authorization.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
final readonly class Authorization extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $email,
|
||||
private string $password,
|
||||
private bool $remember = false
|
||||
) { }
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getRemember(): bool
|
||||
{
|
||||
return $this->remember;
|
||||
}
|
||||
}
|
8
app/application/app/Dto/Service/Dto.php
Normal file
8
app/application/app/Dto/Service/Dto.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
abstract readonly class Dto
|
||||
{
|
||||
|
||||
}
|
15
app/application/app/Dto/Service/Pages.php
Normal file
15
app/application/app/Dto/Service/Pages.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service;
|
||||
|
||||
abstract readonly class Pages extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private int $page
|
||||
) { }
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
}
|
17
app/application/app/Dto/Service/Site/Profile/Update.php
Normal file
17
app/application/app/Dto/Service/Site/Profile/Update.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Site\Profile;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class Update extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $name
|
||||
) { }
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Site\Profile;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Enums\Lang;
|
||||
|
||||
final readonly class UpdateSettings extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private ?Lang $lang,
|
||||
private ?string $timezone
|
||||
) { }
|
||||
|
||||
public function getLang(): ?Lang
|
||||
{
|
||||
return $this->lang;
|
||||
}
|
||||
|
||||
public function getTimezone(): ?string
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
}
|
32
app/application/app/Dto/Service/User/ManyRoleDto.php
Normal file
32
app/application/app/Dto/Service/User/ManyRoleDto.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\User;
|
||||
|
||||
use App\Exceptions\Dto\Service\User\ManyRoleDtoException;
|
||||
|
||||
final class ManyRoleDto
|
||||
{
|
||||
private array $roles = [];
|
||||
|
||||
public function __construct(array $roles = []) {
|
||||
foreach ($roles as $role) {
|
||||
if (!is_numeric($role) || is_float($role)) {
|
||||
throw new ManyRoleDtoException('Not an integer: ' . $role . '.');
|
||||
}
|
||||
$this->add((int) $role);
|
||||
}
|
||||
}
|
||||
|
||||
public function add(int $id): void
|
||||
{
|
||||
if ($id < 1) {
|
||||
throw new ManyRoleDtoException('Only Integer > 0.');
|
||||
}
|
||||
$this->roles[] = $id;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
}
|
17
app/application/app/Dto/Service/User/UpdatePassword.php
Normal file
17
app/application/app/Dto/Service/User/UpdatePassword.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\User;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class UpdatePassword extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $password
|
||||
) { }
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
46
app/application/app/Enums/Lang.php
Normal file
46
app/application/app/Enums/Lang.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
enum Lang: int
|
||||
{
|
||||
case Ru = 1;
|
||||
case En = 2;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Ru => 'Русский',
|
||||
self::En => 'English'
|
||||
};
|
||||
}
|
||||
|
||||
public function getLocale(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Ru => 'ru',
|
||||
self::En => 'en'
|
||||
};
|
||||
}
|
||||
|
||||
public static function toArray(): array
|
||||
{
|
||||
$choices = [];
|
||||
foreach (self::cases() as $lang) {
|
||||
$choices[] = [
|
||||
'name' => $lang->name,
|
||||
'value' => $lang->value,
|
||||
'title' => $lang->getTitle(),
|
||||
'locale' => $lang->getLocale()
|
||||
];
|
||||
}
|
||||
return $choices;
|
||||
}
|
||||
|
||||
public static function toCollection(): Collection
|
||||
{
|
||||
return collect(self::toArray());
|
||||
}
|
||||
}
|
61
app/application/app/Enums/Permission.php
Normal file
61
app/application/app/Enums/Permission.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum Permission: string
|
||||
{
|
||||
case AdminPanel = 'allow-admin-panel';
|
||||
case Role = 'role';
|
||||
case User = 'user';
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
$permissions = match ($this) {
|
||||
self::AdminPanel => [
|
||||
'view' => __('permissions.Administrative panel allowed'),
|
||||
],
|
||||
default => $this->getBasePermissions()
|
||||
};
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return __('permissions.' . $this->name);
|
||||
}
|
||||
|
||||
public function formatValue(string $permission): string
|
||||
{
|
||||
return $this->value . '.' . $permission;
|
||||
}
|
||||
|
||||
public static function toArrayList(): array
|
||||
{
|
||||
$permissions = [];
|
||||
foreach (self::cases() as $permissionEnum) {
|
||||
foreach ($permissionEnum->getPermissions() as $permissionName => $permissionTitle) {
|
||||
$name = $permissionEnum->formatValue($permissionName);
|
||||
$title = $permissionEnum->getTitle() . ' - ' . $permissionTitle;
|
||||
$permissions[$name] = $title;
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
public static function toArrayListCodes(): array
|
||||
{
|
||||
return \array_keys(self::toArrayList());
|
||||
}
|
||||
|
||||
private function getBasePermissions(): array
|
||||
{
|
||||
return [
|
||||
'view' => __('permissions.Allowed to watch'),
|
||||
'create' => __('permissions.Allowed to create'),
|
||||
'update' => __('permissions.Allowed to edit'),
|
||||
'delete' => __('permissions.Allowed to delete'),
|
||||
];
|
||||
}
|
||||
}
|
8
app/application/app/Enums/SystemRole.php
Normal file
8
app/application/app/Enums/SystemRole.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SystemRole: string
|
||||
{
|
||||
case Admin = 'admin';
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Dto\Service\User;
|
||||
|
||||
final class ManyRoleDtoException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Services\Rule;
|
||||
|
||||
final class RoleSyncPermissionsCommandHandlerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
35
app/application/app/Helpers/Helpers.php
Normal file
35
app/application/app/Helpers/Helpers.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class Helpers
|
||||
{
|
||||
public static function getTimeZoneList(): Collection
|
||||
{
|
||||
return Cache::rememberForever('timezones_list_collection', function () {
|
||||
$timezone = [];
|
||||
foreach (timezone_identifiers_list(\DateTimeZone::ALL) as $key => $value) {
|
||||
$timezone[$value] = $value . ' (UTC ' . now($value)->format('P') . ')';
|
||||
}
|
||||
return collect($timezone)->sortKeys();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* $name = 'field[key]' return 'field.key'
|
||||
*/
|
||||
public static function formatAttributeNameToRequestName(string $name): string
|
||||
{
|
||||
return Str::of($name)
|
||||
->replace(
|
||||
['.', '[', ']'],
|
||||
['_', '.', ''],
|
||||
)
|
||||
->rtrim('.')
|
||||
->value();
|
||||
}
|
||||
}
|
10
app/application/app/Http/Controllers/Admin/Controller.php
Normal file
10
app/application/app/Http/Controllers/Admin/Controller.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class DashboardController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return view('admin/dashboard/index');
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Admin\Roles\IndexRequest;
|
||||
use App\Http\Requests\Admin\Roles\StoreUpdateRequest;
|
||||
use App\Services\Admin\RoleService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class RolesController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RoleService $roleService
|
||||
) { }
|
||||
public function index(IndexRequest $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$data = $request->getDto();
|
||||
$querySettingsDto = new QuerySettingsDto(
|
||||
limit: 20,
|
||||
page: $data->getPage(),
|
||||
queryWith: []
|
||||
);
|
||||
|
||||
$result = $this->roleService->index($data->getRoleBuilderDto(), $querySettingsDto, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/index', $result->getData());
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->create($user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/create', $result->getData());
|
||||
}
|
||||
|
||||
public function edit(int $id, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->edit($id, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/roles/edit', $result->getData());
|
||||
}
|
||||
|
||||
public function store(StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->store($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->update($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function destroy(int $id, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->roleService->destroy($id, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
106
app/application/app/Http/Controllers/Admin/UsersController.php
Normal file
106
app/application/app/Http/Controllers/Admin/UsersController.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Admin\Users\IndexRequest;
|
||||
use App\Http\Requests\Admin\Users\StoreUpdateRequest;
|
||||
use App\Http\Requests\Admin\Users\UpdatePasswordRequest;
|
||||
use App\Services\Admin\UserService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class UsersController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserService $userService
|
||||
) { }
|
||||
|
||||
public function index(IndexRequest $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$data = $request->getDto();
|
||||
$querySettingsDto = new QuerySettingsDto(
|
||||
limit: 20,
|
||||
page: $data->getPage(),
|
||||
queryWith: []
|
||||
);
|
||||
|
||||
$result = $this->userService->index($data->getUserBuilderDto(), $querySettingsDto, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/index', $result->getData());
|
||||
}
|
||||
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->create($user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/create', $result->getData());
|
||||
}
|
||||
|
||||
public function edit(int $id, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->edit($id, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin/users/edit', $result->getData());
|
||||
}
|
||||
|
||||
public function store(StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->store($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->update($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updatePassword(int $id, UpdatePasswordRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->userService->updatePassword($id, $data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.edit', $result->getModel())->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function destroy(int $id, Request $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->userService->destroy($id, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.users.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
49
app/application/app/Http/Controllers/AuthController.php
Normal file
49
app/application/app/Http/Controllers/AuthController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\AuthorizationRequest;
|
||||
use App\Services\AuthService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
final class AuthController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AuthService $authService
|
||||
) { }
|
||||
|
||||
public function login(): View
|
||||
{
|
||||
return view('login', [
|
||||
'captcha' => config('app.captcha', false),
|
||||
]);
|
||||
}
|
||||
|
||||
public function authorization(AuthorizationRequest $request): RedirectResponse
|
||||
{
|
||||
$authorization = $request->getDto();
|
||||
$result = $this->authService->authorization($authorization);
|
||||
if ($result->isError()) {
|
||||
if ($result->getCode() === Response::HTTP_UNAUTHORIZED) {
|
||||
Log::warning('Unauthorized ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||
}
|
||||
return redirect()->route('login')->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
$request->session()->regenerate();
|
||||
Log::notice('Logged in ' . $authorization->getEmail() . ' [' . $request->getClientIp() . ']');
|
||||
return redirect()->route('home');
|
||||
}
|
||||
|
||||
public function logout(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
return redirect(route('login'));
|
||||
}
|
||||
}
|
8
app/application/app/Http/Controllers/Controller.php
Normal file
8
app/application/app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
8
app/application/app/Http/Controllers/Site/Controller.php
Normal file
8
app/application/app/Http/Controllers/Site/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Site;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
13
app/application/app/Http/Controllers/Site/HomeController.php
Normal file
13
app/application/app/Http/Controllers/Site/HomeController.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Site;
|
||||
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class HomeController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return \view('site.home.index');
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Site;
|
||||
|
||||
use App\Enums\Lang;
|
||||
use App\Helpers\Helpers;
|
||||
use App\Http\Requests\Site\Profile\UpdatePasswordRequest;
|
||||
use App\Http\Requests\Site\Profile\UpdateRequest;
|
||||
use App\Http\Requests\Site\Profile\UpdateSettingsRequest;
|
||||
use App\Services\Site\ProfileService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class ProfileController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProfileService $profileService
|
||||
) { }
|
||||
|
||||
public function profile(Request $request): View
|
||||
{
|
||||
return view('site.profile.profile', [
|
||||
'user' => $request->user()
|
||||
]);
|
||||
}
|
||||
|
||||
public function settings(Request $request): View
|
||||
{
|
||||
return view('site.profile.settings', [
|
||||
'user' => $request->user(),
|
||||
'languages' => Lang::toCollection()->pluck(value: 'title', key: 'value')->toArray(),
|
||||
'timezone' => Helpers::getTimeZoneList()->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->update($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.edit')->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updatePassword(UpdatePasswordRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->updatePassword($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.edit')->withSuccess($result->getMessage());
|
||||
}
|
||||
|
||||
public function updateSettings(UpdateSettingsRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
|
||||
$result = $this->profileService->updateSettings($data, $user);
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getMessage());
|
||||
}
|
||||
return redirect()->route('profile.settings')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
26
app/application/app/Http/Middleware/AdminPanel.php
Normal file
26
app/application/app/Http/Middleware/AdminPanel.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class AdminPanel
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (
|
||||
$request->user() === null || $request->user()->cannot('AdminPanel')
|
||||
) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
18
app/application/app/Http/Middleware/UserIsActive.php
Normal file
18
app/application/app/Http/Middleware/UserIsActive.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class UserIsActive
|
||||
{
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
if ($request->user()->is_active === false) {
|
||||
\abort(Response::HTTP_FORBIDDEN, 'User disabled');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
19
app/application/app/Http/Middleware/UserLocale.php
Normal file
19
app/application/app/Http/Middleware/UserLocale.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
final class UserLocale
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($request->user() && $request->user()->lang) {
|
||||
App::setLocale($request->user()->lang->getLocale());
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Service\Admin\Role\Index;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$this->redirect = route('admin.roles.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
roleBuilderDto: new Role(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Role\StoreUpdate;
|
||||
use App\Rules\Permission;
|
||||
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
|
||||
{
|
||||
$rules = [
|
||||
'name' => ['required', 'max:255'],
|
||||
'permissions' => ['array', new Permission()]
|
||||
];
|
||||
if ($this->getMethod() === 'POST') {
|
||||
$rules['code'] = ['required', 'min:3', 'max:255', 'regex:/^[a-z0-9_-]+$/i'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): StoreUpdate
|
||||
{
|
||||
return new StoreUpdate(
|
||||
name: $this->input('name'),
|
||||
code: $this->input('code', null),
|
||||
permissions: $this->input('permissions', [])
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\User;
|
||||
use App\Dto\Service\Admin\User\Index;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$this->redirect = route('admin.users.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
userBuilderDto: new User(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\User\StoreUpdate;
|
||||
use App\Dto\Service\User\ManyRoleDto;
|
||||
use App\Rules\Username;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => ['required', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'username' => ['required', 'string', new Username()],
|
||||
'is_active' => ['required', 'boolean'],
|
||||
'roles' => ['array', Rule::exists('roles', 'id')],
|
||||
];
|
||||
|
||||
if ($this->getMethod() === 'POST') {
|
||||
$rules['password'] = ['required', Password::default()];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): StoreUpdate
|
||||
{
|
||||
return new StoreUpdate(
|
||||
name: $this->input('name'),
|
||||
email: $this->input('email'),
|
||||
username: $this->input('username'),
|
||||
isActive: (bool) $this->input('is_active', false),
|
||||
roles: new ManyRoleDto($this->input('roles', [])),
|
||||
password: $this->input('password', null),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Users;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class UpdatePasswordRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'password' => ['required', Password::default()],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdatePassword
|
||||
{
|
||||
return new UpdatePassword(password: $this->input('password'));
|
||||
}
|
||||
}
|
37
app/application/app/Http/Requests/AuthorizationRequest.php
Normal file
37
app/application/app/Http/Requests/AuthorizationRequest.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Authorization;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class AuthorizationRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'password' => ['required', 'min:3'],
|
||||
'remember' => ['nullable', 'boolean'],
|
||||
];
|
||||
|
||||
if (config('app.captcha', false)) {
|
||||
$rules['captcha-verified'] = ['captcha'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getDto(): Authorization
|
||||
{
|
||||
return new Authorization(
|
||||
email: $this->input('email'),
|
||||
password: $this->input('password'),
|
||||
remember: (bool) $this->input('remember', false)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Site\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
final class UpdatePasswordRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'password' => ['required', 'confirmed', Password::default()],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdatePassword
|
||||
{
|
||||
return new UpdatePassword(password: $this->input('password'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Site\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Site\Profile\Update;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class UpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Update
|
||||
{
|
||||
return new Update(name: $this->input('name'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Site\Profile;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Site\Profile\UpdateSettings;
|
||||
use App\Enums\Lang;
|
||||
use App\Helpers\Helpers;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
final class UpdateSettingsRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'lang' => ['nullable', new Enum(Lang::class)],
|
||||
'timezone' => ['nullable', Rule::in(Helpers::getTimeZoneList()->keys()->toArray())]
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): UpdateSettings
|
||||
{
|
||||
$lang = $this->input('lang', null);
|
||||
if (!is_null($lang)) {
|
||||
$lang = Lang::from((int) $lang);
|
||||
}
|
||||
|
||||
return new UpdateSettings(
|
||||
lang: $lang,
|
||||
timezone: $this->input('timezone', null),
|
||||
);
|
||||
}
|
||||
}
|
55
app/application/app/Models/Role.php
Normal file
55
app/application/app/Models/Role.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SystemRole as SystemRoleEnum;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\hasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
final class Role extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'code'
|
||||
];
|
||||
|
||||
public function scopeLatest(Builder $query): Builder
|
||||
{
|
||||
return $query->orderBy('id', 'desc');
|
||||
}
|
||||
|
||||
public function scopeAlphavit(Builder $query): Builder
|
||||
{
|
||||
return $query->orderBy('name', 'asc');
|
||||
}
|
||||
|
||||
public function permissions(): hasMany
|
||||
{
|
||||
return $this->hasMany(RolePermission::class, 'role_id', 'id');
|
||||
}
|
||||
|
||||
protected function isRemove(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($dontRemove) => ( SystemRoleEnum::tryFrom($this->code) === null ),
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
protected function isAdmin(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => ( $this->code === SystemRoleEnum::Admin->value ),
|
||||
)->shouldCache();
|
||||
}
|
||||
}
|
21
app/application/app/Models/RolePermission.php
Normal file
21
app/application/app/Models/RolePermission.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class RolePermission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'role_permission';
|
||||
|
||||
public function getPermissionTitleAttribute(): string
|
||||
{
|
||||
$pemissions = Permission::toArrayList();
|
||||
|
||||
return $pemissions[$this->permission] ?? '';
|
||||
}
|
||||
}
|
102
app/application/app/Models/User.php
Normal file
102
app/application/app/Models/User.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Enums\Lang;
|
||||
use App\Enums\SystemRole;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
final class User extends Authenticatable
|
||||
{
|
||||
use HasFactory, Notifiable, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The model's default values for attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'is_active' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'username',
|
||||
'email',
|
||||
'password',
|
||||
'timezone',
|
||||
'lang',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'lang' => Lang::class,
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user's roles
|
||||
*/
|
||||
public function roles(): belongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class);
|
||||
}
|
||||
|
||||
public function hasRole(string $role): bool
|
||||
{
|
||||
return $this->roles->where('code', $role)->isNotEmpty();
|
||||
}
|
||||
|
||||
public function hasPermission(string $permission): bool
|
||||
{
|
||||
return $this->permissions->search($permission) !== false;
|
||||
}
|
||||
|
||||
protected function isAdmin(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->hasRole(SystemRole::Admin->value),
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
protected function permissions(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
$roles = $this->roles->modelKeys();
|
||||
return RolePermission::whereIn('role_id', $roles)->select('permission')->pluck('permission');
|
||||
},
|
||||
)->shouldCache();
|
||||
}
|
||||
}
|
14
app/application/app/Policies/AdminPanel.php
Normal file
14
app/application/app/Policies/AdminPanel.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\SystemRole;
|
||||
use App\Models\User;
|
||||
|
||||
readonly class AdminPanel extends Policy
|
||||
{
|
||||
public function view(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('allow-admin-panel.view');
|
||||
}
|
||||
}
|
24
app/application/app/Policies/Policy.php
Normal file
24
app/application/app/Policies/Policy.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
abstract readonly class Policy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
final public function before(User $user): ?bool
|
||||
{
|
||||
if ($user->is_active !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user->is_admin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
34
app/application/app/Policies/RolePolicy.php
Normal file
34
app/application/app/Policies/RolePolicy.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class RolePolicy extends Policy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('role.view');
|
||||
}
|
||||
|
||||
public function view(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.view');
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('role.create');
|
||||
}
|
||||
|
||||
public function update(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.update');
|
||||
}
|
||||
|
||||
public function delete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->hasPermission('role.delete');
|
||||
}
|
||||
}
|
33
app/application/app/Policies/UserPolicy.php
Normal file
33
app/application/app/Policies/UserPolicy.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class UserPolicy extends Policy
|
||||
{
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('user.view');
|
||||
}
|
||||
|
||||
public function view(User $user, User $userView): bool
|
||||
{
|
||||
return $user->hasPermission('user.view');
|
||||
}
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->hasPermission('user.create');
|
||||
}
|
||||
|
||||
public function update(User $user, User $userUpdate): bool
|
||||
{
|
||||
return $user->hasPermission('user.update');
|
||||
}
|
||||
|
||||
public function delete(User $user, User $userDelete): bool
|
||||
{
|
||||
return $user->hasPermission('user.delete');
|
||||
}
|
||||
}
|
71
app/application/app/Providers/AppServiceProvider.php
Normal file
71
app/application/app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Services\Search\Search;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(CreateSearchInstanceCommand::class, function () {
|
||||
return new CreateSearchInstanceCommand(Search::class);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
if (config('app.force_https') === true) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
$this->passwordDefaults();
|
||||
|
||||
$this->configureRateLimiting();
|
||||
Gate::define('AdminPanel', [\App\Policies\AdminPanel::class, 'view']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*/
|
||||
private function configureRateLimiting(): void
|
||||
{
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
return [
|
||||
Limit::perHour(config('rate_limiting.login_max_request', 50))->by($request->getClientIp()),
|
||||
Limit::perHour(config('rate_limiting.login_max_email_request', 10))->by($request->getClientIp() . '-' . $request->input('email')),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
private function passwordDefaults(): void
|
||||
{
|
||||
Password::defaults(function () {
|
||||
$rule = Password::min(8);
|
||||
|
||||
if ($this->app->isProduction()) {
|
||||
$rule->letters()
|
||||
->mixedCase()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised();
|
||||
}
|
||||
|
||||
return $rule;
|
||||
});
|
||||
}
|
||||
}
|
59
app/application/app/Repositories/RoleRepository.php
Normal file
59
app/application/app/Repositories/RoleRepository.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\Role;
|
||||
use App\Dto\Builder\Role as RoleBuilderDto;
|
||||
use App\Services\Role\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class RoleRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getRoleById(int $id): ?Role
|
||||
{
|
||||
return Role::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getRoleByCode(string $code): ?Role
|
||||
{
|
||||
return Role::query()->where('code', $code)->first();
|
||||
}
|
||||
|
||||
public function getRoles(RoleBuilderDto $roleBuilderDto, array $with = []): Search
|
||||
{
|
||||
$query = $this->builderCommand->execute(
|
||||
query: Role::query()->with($with),
|
||||
roleBuilderDto: $roleBuilderDto
|
||||
);
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getRolesForSelect(array $withExcepts = []): array
|
||||
{
|
||||
return Role::query()
|
||||
->when($withExcepts, function (Builder $query, array $withExcepts) {
|
||||
$query->withTrashed()->whereNull('deleted_at')->orWhereIn('id', $withExcepts);
|
||||
})
|
||||
->pluck('name', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function isExistsCode(string $code, ?int $exceptId = null): bool
|
||||
{
|
||||
return Role::query()
|
||||
->where('code', $code)
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
}
|
61
app/application/app/Repositories/UserRepository.php
Normal file
61
app/application/app/Repositories/UserRepository.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\User;
|
||||
use App\Dto\Builder\User as UserBuilderDto;
|
||||
use App\Services\User\BuilderCommand;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class UserRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getUserById(int $id): ?User
|
||||
{
|
||||
return User::query()->where('id', $id)->first();
|
||||
}
|
||||
|
||||
public function getUserByEmail(string $email): ?User
|
||||
{
|
||||
return User::query()->where('email', Str::lower($email))->first();
|
||||
}
|
||||
|
||||
public function getUsers(UserBuilderDto $userBuilderDto, array $with = []): Search
|
||||
{
|
||||
$query = $this->builderCommand->execute(
|
||||
query: User::query()->with($with),
|
||||
userBuilderDto: $userBuilderDto
|
||||
);
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function isExistsEmail(string $email, ?int $exceptId = null): bool
|
||||
{
|
||||
return User::query()
|
||||
->where('email', Str::lower($email))
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function isExistsUsername(string $username, ?int $exceptId = null): bool
|
||||
{
|
||||
return User::query()
|
||||
->where('username', Str::lower($username))
|
||||
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||
$query->where('id', '!=', $exceptId);
|
||||
})
|
||||
->withTrashed()
|
||||
->exists();
|
||||
}
|
||||
}
|
55
app/application/app/Rules/Permission.php
Normal file
55
app/application/app/Rules/Permission.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
final readonly class Permission implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!is_string($value) && !is_array($value)) {
|
||||
$fail('validation.no_type')->translate(['type' => 'string, array']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$this->validatePermission($value, $fail);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$this->validatePermission($item, $fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function validatePermission(string $value, Closure $fail): void
|
||||
{
|
||||
$value = explode('.', $value, 2);
|
||||
|
||||
if (count($value) !== 2) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
list($permissionEnum, $permission) = $value;
|
||||
$permissionEnum = \App\Enums\Permission::tryFrom($permissionEnum);
|
||||
if (is_null($permissionEnum)) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
|
||||
$permissions = $permissionEnum->getPermissions();
|
||||
if (!isset($permissions[$permission])) {
|
||||
$fail('validation.enum')->translate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
34
app/application/app/Rules/Repository.php
Normal file
34
app/application/app/Rules/Repository.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Closure;
|
||||
|
||||
final readonly class Repository implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!is_string($value) && !is_array($value)) {
|
||||
$fail('validation.no_type')->translate(['type' => 'string, array']);
|
||||
return;
|
||||
}
|
||||
|
||||
$validator = Validator::make([
|
||||
'slug' => $value,
|
||||
], [
|
||||
'slug' => 'min:1|max:150|regex:/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
$fail($error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
app/application/app/Rules/Username.php
Normal file
34
app/application/app/Rules/Username.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
final readonly class Username implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (!is_string($value) && !is_array($value)) {
|
||||
$fail('validation.no_type')->translate(['type' => 'string, array']);
|
||||
return;
|
||||
}
|
||||
|
||||
$validator = Validator::make([
|
||||
'username' => $value,
|
||||
], [
|
||||
'username' => 'min:1|max:70|regex:/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
$fail($error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
app/application/app/ServiceResults/ServiceResult.php
Normal file
20
app/application/app/ServiceResults/ServiceResult.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults;
|
||||
|
||||
use App\Contracts\ServiceResult as ServiceResultContract;
|
||||
use App\Contracts\ServiceResultError as ServiceResultErrorContract;
|
||||
|
||||
abstract class ServiceResult implements ServiceResultContract
|
||||
{
|
||||
public function isSuccess(): bool
|
||||
{
|
||||
return $this->isError() === false;
|
||||
}
|
||||
|
||||
public function isError(): bool
|
||||
{
|
||||
return $this instanceof ServiceResultErrorContract === true;
|
||||
}
|
||||
}
|
16
app/application/app/ServiceResults/ServiceResultArray.php
Normal file
16
app/application/app/ServiceResults/ServiceResultArray.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults;
|
||||
|
||||
final class ServiceResultArray extends ServiceResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly array $data,
|
||||
) { }
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
49
app/application/app/ServiceResults/ServiceResultError.php
Normal file
49
app/application/app/ServiceResults/ServiceResultError.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults;
|
||||
|
||||
use App\Contracts\ServiceResultError as ServiceResultErrorContract;
|
||||
|
||||
final class ServiceResultError extends ServiceResult implements ServiceResultErrorContract
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $message,
|
||||
private readonly array $errors = [],
|
||||
private readonly ?int $code = null,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getCode(): ?int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function getErrorsOrMessage(): array|string
|
||||
{
|
||||
if (!empty($this->getErrors())) {
|
||||
return $this->getErrors();
|
||||
}
|
||||
|
||||
return $this->getMessage();
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return [
|
||||
'message' => $this->getMessage(),
|
||||
'errors' => $this->errors
|
||||
];
|
||||
}
|
||||
}
|
15
app/application/app/ServiceResults/ServiceResultSuccess.php
Normal file
15
app/application/app/ServiceResults/ServiceResultSuccess.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults;
|
||||
|
||||
final class ServiceResultSuccess extends ServiceResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $message
|
||||
) { }
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
23
app/application/app/ServiceResults/StoreUpdateResult.php
Normal file
23
app/application/app/ServiceResults/StoreUpdateResult.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\ServiceResults;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class StoreUpdateResult extends ServiceResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Model $model,
|
||||
private readonly string $message
|
||||
) { }
|
||||
|
||||
public function getModel(): Model
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
165
app/application/app/Services/Admin/RoleService.php
Normal file
165
app/application/app/Services/Admin/RoleService.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin;
|
||||
|
||||
use App\Dto\Builder\Role as RoleBuilderDto;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Service\Admin\Role\StoreUpdate;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Repositories\RoleRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\Role\RoleCommandHandler;
|
||||
use App\Services\Role\RoleSyncPermissionsCommandHandler;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class RoleService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly RoleRepository $roleRepository,
|
||||
private readonly RoleCommandHandler $roleCommandHandler,
|
||||
private readonly RoleSyncPermissionsCommandHandler $roleSyncPermissionsCommandHandler
|
||||
) { }
|
||||
|
||||
public function index(RoleBuilderDto $roleBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
if ($user->cannot('viewAny', Role::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$roles = $this->roleRepository->getRoles(
|
||||
$roleBuilderDto,
|
||||
$querySettingsDto->getQueryWith()
|
||||
)->pagination(
|
||||
$querySettingsDto->getLimit(),
|
||||
$querySettingsDto->getPage()
|
||||
);
|
||||
|
||||
return $this->result([
|
||||
'roles' => $roles
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
if ($user->cannot('create', Role::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'role' => new Role(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $id, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$role = $this->roleRepository->getRoleById($id);
|
||||
|
||||
if (is_null($role)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $role)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'role' => $role,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
if ($user->cannot('create', Role::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($this->roleRepository->isExistsCode($data->getCode())) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.code')]),
|
||||
['code' => __('validation.unique', ['attribute' => __('validation.attributes.code')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$role = DB::transaction(function () use ($data) {
|
||||
$dataRole = $this->getDataRole($data);
|
||||
$dataRole['code'] = $data->getCode();
|
||||
|
||||
$role = $this->roleCommandHandler->handleStore($dataRole);
|
||||
$role = $this->roleSyncPermissionsCommandHandler->handle($role, $data->getPermissions());
|
||||
|
||||
return $role;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($role, __('The group was successfully created'));
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$role = $this->roleRepository->getRoleById($id);
|
||||
|
||||
if (is_null($role)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('update', $role)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
$role = DB::transaction(function () use ($data, $role) {
|
||||
$dataRole = $this->getDataRole($data);
|
||||
|
||||
$role = $this->roleCommandHandler->handleUpdate($role, $dataRole);
|
||||
$role = $this->roleSyncPermissionsCommandHandler->handle($role, $data->getPermissions());
|
||||
|
||||
return $role;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($role, __('The group was successfully updated'));
|
||||
}
|
||||
|
||||
public function destroy(int $id, User $user): ServiceResultError|ServiceResultSuccess
|
||||
{
|
||||
$role = $this->roleRepository->getRoleById($id);
|
||||
|
||||
if (is_null($role)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('delete', $role)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($role) {
|
||||
$this->roleCommandHandler->handleDestroy($role);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('The group has been deleted'));
|
||||
}
|
||||
|
||||
private function getDataRole(StoreUpdate $data): array
|
||||
{
|
||||
return [
|
||||
'name' => $data->getName(),
|
||||
];
|
||||
}
|
||||
}
|
216
app/application/app/Services/Admin/UserService.php
Normal file
216
app/application/app/Services/Admin/UserService.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin;
|
||||
|
||||
use App\Dto\Builder\User as UserBuilderDto;
|
||||
use App\Dto\Service\Admin\User\StoreUpdate;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use App\Helpers\Helpers;
|
||||
use App\Models\User;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Repositories\RoleRepository;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\Service;
|
||||
use App\Services\User\UserCommandHandler;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class UserService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly UserCommandHandler $userCommandHandler,
|
||||
private readonly RoleRepository $roleRepository
|
||||
) { }
|
||||
|
||||
public function index(UserBuilderDto $userBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
if ($user->cannot('viewAny', User::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$users = $this->userRepository->getUsers(
|
||||
$userBuilderDto,
|
||||
$querySettingsDto->getQueryWith()
|
||||
)->pagination(
|
||||
$querySettingsDto->getLimit(),
|
||||
$querySettingsDto->getPage()
|
||||
);
|
||||
|
||||
return $this->result([
|
||||
'users' => $users,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
if ($user->cannot('create', User::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'user' => new User(),
|
||||
'roles' => $this->roleRepository->getRolesForSelect(),
|
||||
'userRoles' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(int $id, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$modelUser = $this->userRepository->getUserById($id);
|
||||
|
||||
if (is_null($modelUser)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('view', $modelUser)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$userRoles = $modelUser->roles()->withTrashed()->pluck('id')->toArray();
|
||||
return $this->result([
|
||||
'user' => $modelUser,
|
||||
'roles' => $this->roleRepository->getRolesForSelect($userRoles),
|
||||
'userRoles' => $userRoles,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
if ($user->cannot('create', User::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($this->userRepository->isExistsEmail($data->getEmail())) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.email')]),
|
||||
['code' => __('validation.unique', ['attribute' => __('validation.attributes.email')])]
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->userRepository->isExistsUsername($data->getUsername())) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.username')]),
|
||||
['code' => __('validation.unique', ['attribute' => __('validation.attributes.username')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$modelUser = DB::transaction(function () use ($data) {
|
||||
$dataUser = $this->getDataUser($data);
|
||||
|
||||
$modelUser = $this->userCommandHandler->handleStore($dataUser, $data->getPassword());
|
||||
$this->userCommandHandler->handleSyncRoles($modelUser, $data->getRoles());
|
||||
|
||||
return $modelUser;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($modelUser, __('The user was successfully created'));
|
||||
}
|
||||
|
||||
public function update(int $id, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$modelUser = $this->userRepository->getUserById($id);
|
||||
|
||||
if (is_null($modelUser)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('update', $modelUser)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
if ($this->userRepository->isExistsEmail($data->getEmail(), $modelUser->id)) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.email')]),
|
||||
['code' => __('validation.unique', ['attribute' => __('validation.attributes.email')])]
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->userRepository->isExistsUsername($data->getUsername(), $modelUser->id)) {
|
||||
return $this->errValidate(
|
||||
__('validation.unique', ['attribute' => __('validation.attributes.username')]),
|
||||
['code' => __('validation.unique', ['attribute' => __('validation.attributes.username')])]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$modelUser = DB::transaction(function () use ($data, $modelUser) {
|
||||
$dataUser = $this->getDataUser($data);
|
||||
|
||||
$modelUser = $this->userCommandHandler->handleUpdate($modelUser, $dataUser);
|
||||
$this->userCommandHandler->handleSyncRoles($modelUser, $data->getRoles());
|
||||
|
||||
return $modelUser;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($modelUser, __('The user was successfully updated'));
|
||||
}
|
||||
|
||||
public function updatePassword(int $id, UpdatePassword $data, User $user): ServiceResultError | StoreUpdateResult
|
||||
{
|
||||
$modelUser = $this->userRepository->getUserById($id);
|
||||
|
||||
if (is_null($modelUser)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('update', $modelUser)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->userCommandHandler->handleUpdatePassword($modelUser, $data->getPassword());
|
||||
} catch (\Throwable $e) {
|
||||
report($e->getMessage());
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->resultStoreUpdateModel($modelUser, __('The password has been changed'));
|
||||
}
|
||||
|
||||
public function destroy(int $id, User $user): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
$modelUser = $this->userRepository->getUserById($id);
|
||||
|
||||
if (is_null($modelUser)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if ($user->cannot('delete', $modelUser)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($modelUser) {
|
||||
$this->userCommandHandler->handleDestroy($modelUser);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('The user has been deleted'));
|
||||
}
|
||||
|
||||
private function getDataUser(StoreUpdate $data): array
|
||||
{
|
||||
return [
|
||||
'name' => $data->getName(),
|
||||
'email' => $data->getEmail(),
|
||||
'username' => $data->getUsername(),
|
||||
'is_active' => $data->isActive(),
|
||||
];
|
||||
}
|
||||
}
|
37
app/application/app/Services/AuthService.php
Normal file
37
app/application/app/Services/AuthService.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Dto\Service\Authorization;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
final class AuthService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserRepository $userRepository
|
||||
) { }
|
||||
|
||||
public function authorization(Authorization $authorization): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
$user = $this->userRepository->getUserByEmail($authorization->getEmail());
|
||||
if (is_null($user)) {
|
||||
return $this->errUnauthorized(__('auth.failed'));
|
||||
}
|
||||
if (Hash::check($authorization->getPassword(), $user->password) !== true) {
|
||||
return $this->errUnauthorized(__('auth.password'));
|
||||
}
|
||||
|
||||
try {
|
||||
Auth::login($user, $authorization->getRemember());
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('auth.success'));
|
||||
}
|
||||
}
|
15
app/application/app/Services/Role/BuilderCommand.php
Normal file
15
app/application/app/Services/Role/BuilderCommand.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Role;
|
||||
|
||||
use App\Dto\Builder\Role as RoleBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, RoleBuilderDto $roleBuilderDto): Relation | Builder
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
}
|
26
app/application/app/Services/Role/RoleCommandHandler.php
Normal file
26
app/application/app/Services/Role/RoleCommandHandler.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Role;
|
||||
|
||||
use App\Models\Role;
|
||||
|
||||
final readonly class RoleCommandHandler
|
||||
{
|
||||
public function handleStore(array $data): Role
|
||||
{
|
||||
return Role::create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(Role $role, array $data): Role
|
||||
{
|
||||
$role->update($data);
|
||||
$role->touch();
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
public function handleDestroy(Role $role): void
|
||||
{
|
||||
$role->delete();
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Role;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use App\Exceptions\Services\Rule\RoleSyncPermissionsCommandHandlerException;
|
||||
use App\Models\Role;
|
||||
use App\Models\RolePermission;
|
||||
|
||||
final readonly class RoleSyncPermissionsCommandHandler
|
||||
{
|
||||
public function handle(Role $role, array $dataPermissions): Role
|
||||
{
|
||||
$rolePermissions = $role->permissions->pluck('id', 'permission')->toArray();
|
||||
|
||||
$data = $this->getInsertDeleteData($role->id, $rolePermissions, $dataPermissions);
|
||||
|
||||
if (!empty($data['insert'])) {
|
||||
RolePermission::query()->insert($data['insert']);
|
||||
}
|
||||
|
||||
if (!empty($data['delete'])) {
|
||||
RolePermission::query()->whereIn('id', $data['delete'])->delete();
|
||||
}
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
private function getInsertDeleteData(int $roleId, array $rolePermissions, array $permissionsData): array
|
||||
{
|
||||
$data = [
|
||||
'insert' => [],
|
||||
'delete' => []
|
||||
];
|
||||
|
||||
$permissions = Permission::toArrayListCodes();
|
||||
foreach ($permissionsData as $permission) {
|
||||
if (array_search($permission, $permissions) === false) {
|
||||
throw new RoleSyncPermissionsCommandHandlerException('Таких разрешений в системе нет: ' . $permission);
|
||||
}
|
||||
|
||||
if (isset($rolePermissions[$permission])) {
|
||||
unset($rolePermissions[$permission]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['insert'][] = [
|
||||
'role_id' => $roleId,
|
||||
'permission' => $permission,
|
||||
];
|
||||
}
|
||||
|
||||
$data['delete'] = array_values($rolePermissions);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Search;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class CreateSearchInstanceCommand
|
||||
{
|
||||
public function __construct(
|
||||
private string $abstract
|
||||
) { }
|
||||
|
||||
public function execute(Relation | Builder $query): Search
|
||||
{
|
||||
return new $this->abstract($query);
|
||||
}
|
||||
}
|
79
app/application/app/Services/Search/Search.php
Normal file
79
app/application/app/Services/Search/Search.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Search;
|
||||
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Pagination\CursorPaginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use App\Contracts\Search as SearchContract;
|
||||
|
||||
final readonly class Search implements SearchContract
|
||||
{
|
||||
public function __construct(
|
||||
private Relation|Builder $query
|
||||
) { }
|
||||
|
||||
public function all(): Collection
|
||||
{
|
||||
return $this->query->get();
|
||||
}
|
||||
|
||||
public function get(int $limit): Collection
|
||||
{
|
||||
return $this->query->limit($limit)->get();
|
||||
}
|
||||
|
||||
public function pagination(int $limit, int $page = 1): LengthAwarePaginator
|
||||
{
|
||||
if ($page > 100) {
|
||||
return $this->paginationPerfomance($limit, $page);
|
||||
}
|
||||
return $this->query->paginate($limit, page: $page)->withQueryString();
|
||||
}
|
||||
|
||||
public function cursorPaginate(int $limit): CursorPaginator
|
||||
{
|
||||
return $this->query->cursorPaginate($limit);
|
||||
}
|
||||
|
||||
private function paginationPerfomance(int $limit, int $page = 1): LengthAwarePaginator
|
||||
{
|
||||
$total = $this->query->clone()->count();
|
||||
$options = [
|
||||
'path' => Paginator::resolveCurrentPath(),
|
||||
'pageName' => 'page',
|
||||
];
|
||||
|
||||
$result = collect();
|
||||
if ($total > 0) {
|
||||
$result = $this->subQuery($limit, $page);
|
||||
}
|
||||
|
||||
$pagination = Container::getInstance()->makeWith(LengthAwarePaginator::class, [
|
||||
'items' => $result,
|
||||
'total' => $total,
|
||||
'perPage' => $limit,
|
||||
'currentPage' => $page,
|
||||
'options' => $options
|
||||
]);
|
||||
|
||||
return $pagination->withQueryString();
|
||||
}
|
||||
|
||||
private function subQuery(int $limit, int $page): Collection
|
||||
{
|
||||
$table = $this->query->getModel()->getTable();
|
||||
return $this->query->getModel()::query()
|
||||
->select($table.'.*')
|
||||
->with($this->query->getEagerLoads())
|
||||
->from(
|
||||
clone $this->query->select('id')->forPage($page, $limit),
|
||||
'q'
|
||||
)->join($table.' as '.$table, $table.'.id', '=', 'q.id')
|
||||
->get();
|
||||
}
|
||||
}
|
74
app/application/app/Services/Service.php
Normal file
74
app/application/app/Services/Service.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\Site\PagePossibleWithoutTranslation;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
abstract class Service
|
||||
{
|
||||
final protected function errValidate(string $message, array $errors = []): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_UNPROCESSABLE_ENTITY, $message, $errors);
|
||||
}
|
||||
|
||||
final protected function errFobidden(string $message): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_FORBIDDEN, $message);
|
||||
}
|
||||
|
||||
final protected function errNotFound(string $message): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_NOT_FOUND, $message);
|
||||
}
|
||||
|
||||
final protected function errService(string $message): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_INTERNAL_SERVER_ERROR, $message);
|
||||
}
|
||||
|
||||
final protected function notAcceptable(string $message): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_NOT_ACCEPTABLE, $message);
|
||||
}
|
||||
|
||||
final protected function errUnauthorized(string $message): ServiceResultError
|
||||
{
|
||||
return $this->error(Response::HTTP_UNAUTHORIZED, $message);
|
||||
}
|
||||
|
||||
final protected function ok(string $message = 'OK'): ServiceResultSuccess
|
||||
{
|
||||
return new ServiceResultSuccess($message);
|
||||
}
|
||||
|
||||
final protected function resultStoreUpdateModel(Model $model, string $message = 'OK'): StoreUpdateResult
|
||||
{
|
||||
return new StoreUpdateResult($model, $message);
|
||||
}
|
||||
|
||||
final protected function result(array $data = []): ServiceResultArray
|
||||
{
|
||||
return new ServiceResultArray(data: $data);
|
||||
}
|
||||
|
||||
final protected function resultSitePage(Project $project, WebsiteTranslations $websiteTranslations, array $data, bool $isTranslation): PagePossibleWithoutTranslation
|
||||
{
|
||||
return new PagePossibleWithoutTranslation($project, $websiteTranslations, $data, $isTranslation);
|
||||
}
|
||||
|
||||
final protected function error(int $code, string $message, array $errors = []): ServiceResultError
|
||||
{
|
||||
return new ServiceResultError(
|
||||
message: $message,
|
||||
errors: $errors,
|
||||
code: $code
|
||||
);
|
||||
}
|
||||
}
|
59
app/application/app/Services/Site/ProfileService.php
Normal file
59
app/application/app/Services/Site/ProfileService.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Site;
|
||||
|
||||
use App\Dto\Service\Site\Profile\Update;
|
||||
use App\Dto\Service\Site\Profile\UpdateSettings;
|
||||
use App\Dto\Service\User\UpdatePassword;
|
||||
use App\Models\User;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\Service;
|
||||
use App\Services\User\UserCommandHandler;
|
||||
|
||||
final class ProfileService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserCommandHandler $userCommandHandler
|
||||
) { }
|
||||
|
||||
public function update(Update $update, User $user): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'name' => $update->getName()
|
||||
];
|
||||
$this->userCommandHandler->handleUpdate($user, $data);
|
||||
} catch (\Throwable $e) {
|
||||
report($e->getMessage());
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
return $this->ok(__('Profile saved successfully'));
|
||||
}
|
||||
|
||||
public function updatePassword(UpdatePassword $update, User $user): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
try {
|
||||
$this->userCommandHandler->handleUpdatePassword($user, $update->getPassword());
|
||||
} catch (\Throwable $e) {
|
||||
report($e->getMessage());
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
return $this->ok(__('The password has been changed'));
|
||||
}
|
||||
|
||||
public function updateSettings(UpdateSettings $update, User $user): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
try {
|
||||
$data = [
|
||||
'lang' => $update->getLang(),
|
||||
'timezone' => $update->getTimezone(),
|
||||
];
|
||||
$this->userCommandHandler->handleUpdate($user, $data);
|
||||
} catch (\Throwable $e) {
|
||||
report($e->getMessage());
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
return $this->ok(__('The settings have been saved'));
|
||||
}
|
||||
}
|
15
app/application/app/Services/User/BuilderCommand.php
Normal file
15
app/application/app/Services/User/BuilderCommand.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\User;
|
||||
|
||||
use App\Dto\Builder\User as UserBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
|
||||
final readonly class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, UserBuilderDto $userBuilderDto): Relation | Builder
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
}
|
78
app/application/app/Services/User/UserCommandHandler.php
Normal file
78
app/application/app/Services/User/UserCommandHandler.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\User;
|
||||
|
||||
use App\Dto\Service\User\ManyRoleDto;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class UserCommandHandler
|
||||
{
|
||||
public function handleStore(array $data, int|string $password): User
|
||||
{
|
||||
$data['email'] = Str::lower($data['email']);
|
||||
$data['username'] = Str::lower($data['username']);
|
||||
$data['password'] = $this->hashPassword($password);
|
||||
$user = User::create($data);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function handleUpdate(User $user, array $data): User
|
||||
{
|
||||
if (isset($data['email'])) {
|
||||
$data['email'] = Str::lower($data['email']);
|
||||
}
|
||||
|
||||
if (isset($data['username'])) {
|
||||
$data['username'] = Str::lower($data['username']);
|
||||
}
|
||||
|
||||
if (isset($data['password'])) {
|
||||
unset($data['password']);
|
||||
}
|
||||
|
||||
$user->update($data);
|
||||
$user->touch();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function handleConfirmationByEmail(User $user): void
|
||||
{
|
||||
$user->update(['email_verified_at' => new Carbon('NOW')]);
|
||||
}
|
||||
|
||||
public function handleUpdatePassword(User $user, int|string $password): void
|
||||
{
|
||||
$user->update(['password' => $this->hashPassword($password)]);
|
||||
}
|
||||
|
||||
public function handleSyncRoles(User $user, ManyRoleDto $roles): void
|
||||
{
|
||||
$user->roles()->sync($roles->toArray());
|
||||
}
|
||||
|
||||
public function attachRole(User $user, Role $role): void
|
||||
{
|
||||
$user->roles()->attach($role);
|
||||
}
|
||||
|
||||
public function detachRole(User $user, Role $role): void
|
||||
{
|
||||
$user->roles()->detach($user);
|
||||
}
|
||||
|
||||
private function hashPassword(int|string $password): string
|
||||
{
|
||||
return Hash::make($password);
|
||||
}
|
||||
|
||||
public function handleDestroy(User $user): void
|
||||
{
|
||||
$user->delete();
|
||||
}
|
||||
}
|
17
app/application/app/View/Components/Layout/Admin.php
Normal file
17
app/application/app/View/Components/Layout/Admin.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Layout;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Admin extends Component
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layout.admin');
|
||||
}
|
||||
}
|
18
app/application/app/View/Components/Layout/Site.php
Normal file
18
app/application/app/View/Components/Layout/Site.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Layout;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Site extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layout.site');
|
||||
}
|
||||
}
|
57
app/application/app/View/Components/Site/Forms/Checkbox.php
Normal file
57
app/application/app/View/Components/Site/Forms/Checkbox.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Checkbox extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly string $checkboxValue,
|
||||
private readonly ?string $userValue = '',
|
||||
private readonly ?string $notCheckedValue = null
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getCheckboxValue(): string
|
||||
{
|
||||
return (string) $this->checkboxValue;
|
||||
}
|
||||
|
||||
private function getUserValue(): string
|
||||
{
|
||||
return (string) old($this->getRequestName(), $this->userValue);
|
||||
}
|
||||
|
||||
private function getNotCheckedValue(): ?string
|
||||
{
|
||||
return $this->notCheckedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.site.forms.checkbox', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'checkboxValue' => $this->getCheckboxValue(),
|
||||
'userValue' => $this->getUserValue(),
|
||||
'notCheckedValue' => $this->getNotCheckedValue(),
|
||||
]);
|
||||
}
|
||||
}
|
26
app/application/app/View/Components/Site/Forms/Form.php
Normal file
26
app/application/app/View/Components/Site/Forms/Form.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site\Forms;
|
||||
|
||||
use App\Helpers\Helpers;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
abstract class Form extends Component
|
||||
{
|
||||
private ?string $requestName = null;
|
||||
|
||||
abstract protected function getName(): string;
|
||||
abstract public function render(): View;
|
||||
|
||||
protected function getRequestName(): string
|
||||
{
|
||||
if (!is_null($this->requestName)) {
|
||||
return $this->requestName;
|
||||
}
|
||||
|
||||
$this->requestName = Helpers::formatAttributeNameToRequestName($this->getName());
|
||||
|
||||
return $this->requestName;
|
||||
}
|
||||
}
|
50
app/application/app/View/Components/Site/Forms/Input.php
Normal file
50
app/application/app/View/Components/Site/Forms/Input.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Input extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly string $type = 'text',
|
||||
private readonly ?string $value = '',
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
private function getValue(): string
|
||||
{
|
||||
return (string) old($this->getRequestName(), $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.site.forms.input', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'type' => $this->getType(),
|
||||
'value' => $this->getValue(),
|
||||
]);
|
||||
}
|
||||
}
|
54
app/application/app/View/Components/Site/Forms/Select.php
Normal file
54
app/application/app/View/Components/Site/Forms/Select.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Site\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Select extends Form
|
||||
{
|
||||
/**
|
||||
* @param string $title
|
||||
* @param string $name
|
||||
* @param array $list = [ [key => value], ... ]
|
||||
* @param null|string $value
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly array $list,
|
||||
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);
|
||||
}
|
||||
|
||||
private function getList(): array
|
||||
{
|
||||
return $this->list;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.site.forms.select', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'list' => $this->getList(),
|
||||
'value' => $this->getValue()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
57
app/application/app/View/Components/Volt/Forms/Checkbox.php
Normal file
57
app/application/app/View/Components/Volt/Forms/Checkbox.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Checkbox extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly string $checkboxValue,
|
||||
private readonly ?string $userValue = '',
|
||||
private readonly ?string $notCheckedValue = null
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getCheckboxValue(): string
|
||||
{
|
||||
return (string) $this->checkboxValue;
|
||||
}
|
||||
|
||||
private function getUserValue(): string
|
||||
{
|
||||
return (string) old($this->getRequestName(), $this->userValue);
|
||||
}
|
||||
|
||||
private function getNotCheckedValue(): ?string
|
||||
{
|
||||
return $this->notCheckedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.volt.forms.checkbox', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'checkboxValue' => $this->getCheckboxValue(),
|
||||
'userValue' => $this->getUserValue(),
|
||||
'notCheckedValue' => $this->getNotCheckedValue(),
|
||||
]);
|
||||
}
|
||||
}
|
26
app/application/app/View/Components/Volt/Forms/Form.php
Normal file
26
app/application/app/View/Components/Volt/Forms/Form.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use App\Helpers\Helpers;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
abstract class Form extends Component
|
||||
{
|
||||
private ?string $requestName = null;
|
||||
|
||||
abstract protected function getName(): string;
|
||||
abstract public function render(): View;
|
||||
|
||||
protected function getRequestName(): string
|
||||
{
|
||||
if (!is_null($this->requestName)) {
|
||||
return $this->requestName;
|
||||
}
|
||||
|
||||
$this->requestName = Helpers::formatAttributeNameToRequestName($this->getName());
|
||||
|
||||
return $this->requestName;
|
||||
}
|
||||
}
|
64
app/application/app/View/Components/Volt/Forms/Input.php
Normal file
64
app/application/app/View/Components/Volt/Forms/Input.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class Input extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly string $type = 'text',
|
||||
private readonly ?string $value = '',
|
||||
private readonly ?string $example = null,
|
||||
private readonly ?string $allowedCharacters = null,
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
private function getValue(): string
|
||||
{
|
||||
return (string) old($this->getRequestName(), $this->value);
|
||||
}
|
||||
|
||||
private function getExample(): ?string
|
||||
{
|
||||
return $this->example;
|
||||
}
|
||||
|
||||
public function getAllowedCharacters(): ?string
|
||||
{
|
||||
return $this->allowedCharacters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.volt.forms.input', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'type' => $this->getType(),
|
||||
'value' => $this->getValue(),
|
||||
'example' => $this->getExample(),
|
||||
'allowedCharacters' => $this->getAllowedCharacters(),
|
||||
]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class MultiCheckbox extends Form
|
||||
{
|
||||
/**
|
||||
* @param string $title
|
||||
* @param string $name
|
||||
* @param array $list = [ [key => value], ... ]
|
||||
* @param array $value
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly array $list,
|
||||
private readonly array $value = []
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getValue(): array
|
||||
{
|
||||
return old($this->getRequestName(), $this->value);
|
||||
}
|
||||
|
||||
private function getList(): array
|
||||
{
|
||||
return $this->list;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.volt.forms.multi_checkbox', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'list' => $this->getList(),
|
||||
'value' => $this->getValue()
|
||||
]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use App\Models\Role;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class PermissionsForRole extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly Role $role,
|
||||
private readonly array $value
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
private function getValue(): Collection
|
||||
{
|
||||
$value = old($this->getRequestName(), $this->value);
|
||||
return collect($value);
|
||||
}
|
||||
|
||||
private function getRole(): Role
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('components.volt.forms.permissions_for_role', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'permissions' => Permission::cases(),
|
||||
'role' => $this->getRole(),
|
||||
'value' => $this->getValue(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user