Added the ability to manage a group of users.
This commit is contained in:
parent
ba7e52f8ac
commit
4083e2ec5e
@ -41,7 +41,7 @@ final class CreateUserAdmin extends Command
|
||||
$data = $validator->valid();
|
||||
|
||||
try {
|
||||
$role = $roleRepository->getRoleBySlug(SystemRole::Admin->value);
|
||||
$role = $roleRepository->getRoleByCode(SystemRole::Admin->value);
|
||||
if (is_null($role)) {
|
||||
$this->errorMessageAndStop('Administrator role not found.');
|
||||
}
|
||||
|
18
app/Contracts/Search.php
Normal file
18
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;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
@ -8,5 +7,6 @@ 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/Dto/Builder/Role.php
Normal file
10
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(
|
||||
|
||||
) { }
|
||||
}
|
27
app/Dto/QuerySettingsDto.php
Normal file
27
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;
|
||||
}
|
||||
}
|
24
app/Dto/Request/Private/Role/Index.php
Normal file
24
app/Dto/Request/Private/Role/Index.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Request\Private\Role;
|
||||
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Request\Dto;
|
||||
|
||||
final readonly class Index extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private Role $roleBuilderDto,
|
||||
private int $page
|
||||
) { }
|
||||
|
||||
public function getRoleBuilderDto(): Role
|
||||
{
|
||||
return $this->roleBuilderDto;
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
}
|
29
app/Dto/Request/Private/Role/StoreUpdate.php
Normal file
29
app/Dto/Request/Private/Role/StoreUpdate.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Request\Private\Role;
|
||||
|
||||
use App\Dto\Request\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;
|
||||
}
|
||||
}
|
52
app/Enums/Permission.php
Normal file
52
app/Enums/Permission.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum Permission: string
|
||||
{
|
||||
case Role = 'role';
|
||||
|
||||
public function getPermissions(): array
|
||||
{
|
||||
return $this->getBasePermissions();
|
||||
}
|
||||
|
||||
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'),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Rule;
|
||||
|
||||
final class RoleSyncPermissionsCommandHandlerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
92
app/Http/Controllers/Private/RolesController.php
Normal file
92
app/Http/Controllers/Private/RolesController.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Private;
|
||||
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Http\Requests\Private\Roles\IndexRequest;
|
||||
use App\Http\Requests\Private\Roles\StoreUpdateRequest;
|
||||
use App\Services\Private\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('private/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('private/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('private/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('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('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('roles.index')->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
30
app/Http/Requests/Private/Roles/IndexRequest.php
Normal file
30
app/Http/Requests/Private/Roles/IndexRequest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Private\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Builder\Role;
|
||||
use App\Dto\Request\Private\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('roles.index');
|
||||
return [
|
||||
'page' => ['nullable', 'numeric', 'min:1']
|
||||
];
|
||||
}
|
||||
|
||||
public function getDto(): Index
|
||||
{
|
||||
return new Index(
|
||||
roleBuilderDto: new Role(),
|
||||
page: (int) $this->input('page', 1)
|
||||
);
|
||||
}
|
||||
}
|
37
app/Http/Requests/Private/Roles/StoreUpdateRequest.php
Normal file
37
app/Http/Requests/Private/Roles/StoreUpdateRequest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Private\Roles;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Request\Private\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', [])
|
||||
);
|
||||
}
|
||||
}
|
21
app/Models/RolePermission.php
Normal file
21
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] ?? '';
|
||||
}
|
||||
}
|
24
app/Policies/Policy.php
Normal file
24
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;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return bool|null
|
||||
*/
|
||||
final public function before(User $user): ?bool
|
||||
{
|
||||
if ($user->is_admin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
34
app/Policies/RolePolicy.php
Normal file
34
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');
|
||||
}
|
||||
}
|
@ -11,7 +11,10 @@ use App\Captcha\Images\Head;
|
||||
use App\Captcha\Images\ImageManager;
|
||||
use App\Captcha\Images\Lines;
|
||||
use App\Services\Api\V1\CaptchaService;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Services\Search\Search;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
@ -29,6 +32,10 @@ final class AppServiceProvider extends ServiceProvider
|
||||
$this->app->bind(ImageBody::class, Body::class);
|
||||
$this->app->bind(ImageLines::class, Lines::class);
|
||||
|
||||
$this->app->bind(CreateSearchInstanceCommand::class, function () {
|
||||
return new CreateSearchInstanceCommand(Search::class);
|
||||
});
|
||||
|
||||
$this->app->bind(CaptchaService::class, function (Application $app) {
|
||||
return new CaptchaService(
|
||||
config: config('captcha', []),
|
||||
@ -54,5 +61,7 @@ final class AppServiceProvider extends ServiceProvider
|
||||
->uncompromised()
|
||||
: $rule;
|
||||
});
|
||||
|
||||
Paginator::useBootstrapFive();
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
\App\Models\Role::class => \App\Policies\RolePolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -2,12 +2,48 @@
|
||||
|
||||
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 getRoleBySlug(string $slug): ?Role
|
||||
final public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
private BuilderCommand $builderCommand
|
||||
) { }
|
||||
|
||||
public function getRoleById(int $id): ?Role
|
||||
{
|
||||
return Role::query()->where('slug', $slug)->first();
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
|
55
app/Rules/Permission.php
Normal file
55
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,15 @@ final class ServiceResultError extends ServiceResult implements ServiceResultErr
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function getErrorsOrMessage(): array|string
|
||||
{
|
||||
if (!empty($this->getErrors())) {
|
||||
return $this->getErrors();
|
||||
}
|
||||
|
||||
return $this->getMessage();
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return [
|
||||
|
23
app/ServiceResults/StoreUpdateResult.php
Normal file
23
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/Services/Private/RoleService.php
Normal file
165
app/Services/Private/RoleService.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Private;
|
||||
|
||||
use App\Dto\Builder\Role as RoleBuilderDto;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Request\Private\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(),
|
||||
];
|
||||
}
|
||||
}
|
15
app/Services/Role/BuilderCommand.php
Normal file
15
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;
|
||||
|
||||
class BuilderCommand
|
||||
{
|
||||
public function execute(Relation | Builder $query, RoleBuilderDto $roleBuilderDto): Relation | Builder
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
}
|
26
app/Services/Role/RoleCommandHandler.php
Normal file
26
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();
|
||||
}
|
||||
}
|
57
app/Services/Role/RoleSyncPermissionsCommandHandler.php
Normal file
57
app/Services/Role/RoleSyncPermissionsCommandHandler.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Role;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use App\Exceptions\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;
|
||||
}
|
||||
}
|
19
app/Services/Search/CreateSearchInstanceCommand.php
Normal file
19
app/Services/Search/CreateSearchInstanceCommand.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
80
app/Services/Search/Search.php
Normal file
80
app/Services/Search/Search.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Search;
|
||||
|
||||
use App\Models\Role;
|
||||
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();
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ namespace App\Services;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
abstract class Service
|
||||
@ -45,6 +47,11 @@ abstract class Service
|
||||
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);
|
||||
|
62
app/View/Components/Private/Forms/PermissionsForRole.php
Normal file
62
app/View/Components/Private/Forms/PermissionsForRole.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\View\Components\Private\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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getTitle(): string
|
||||
{
|
||||
return Str::ucfirst($this->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getValue(): Collection
|
||||
{
|
||||
$value = old($this->getRequestName(), $this->value);
|
||||
return collect($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Role
|
||||
*/
|
||||
private function getRole(): Role
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('private.components.forms.permissions_for_role', [
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'permissions' => Permission::cases(),
|
||||
'role' => $this->getRole(),
|
||||
'value' => $this->getValue(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -47,5 +47,12 @@
|
||||
"Profile saved successfully": "Profile saved successfully",
|
||||
"The password has been changed": "The password has been changed",
|
||||
"Default": "default",
|
||||
"The settings have been saved": "The settings have been saved"
|
||||
"The settings have been saved": "The settings have been saved",
|
||||
"Access is denied": "Access is denied!",
|
||||
"Delete": "Delete",
|
||||
"Do you want to delete?": "Do you want to delete?",
|
||||
"Create": "Create",
|
||||
"The group was successfully created": "The group was successfully created",
|
||||
"The group was successfully updated": "The group was successfully updated",
|
||||
"The group has been deleted": "The group has been deleted"
|
||||
}
|
||||
|
9
lang/en/permissions.php
Normal file
9
lang/en/permissions.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'Role' => 'User group',
|
||||
'Allowed to watch' => 'Allowed to watch',
|
||||
'Allowed to create' => 'Allowed to create',
|
||||
'Allowed to edit' => 'Allowed to edit',
|
||||
'Allowed to delete' => 'Allowed to delete',
|
||||
];
|
6
lang/en/sections.php
Normal file
6
lang/en/sections.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'Dashboard' => 'Dashboard',
|
||||
'User group' => 'User group',
|
||||
];
|
@ -141,6 +141,7 @@ return [
|
||||
'uppercase' => 'The :attribute must be uppercase.',
|
||||
'url' => 'The :attribute format is invalid.',
|
||||
'uuid' => 'The :attribute must be a valid UUID.',
|
||||
'no_type' => 'The :attribute can only use: :type.',
|
||||
'attributes' => [
|
||||
'address' => 'address',
|
||||
'age' => 'age',
|
||||
@ -215,5 +216,7 @@ return [
|
||||
'year' => 'year',
|
||||
'lang' => 'language',
|
||||
'timezone' => 'timezone',
|
||||
'code' => 'code',
|
||||
'permissions' => 'permissions',
|
||||
],
|
||||
];
|
||||
|
@ -47,5 +47,12 @@
|
||||
"Profile saved successfully": "Профиль успешно сохранен",
|
||||
"The password has been changed": "Пароль был изменен",
|
||||
"Default": "По умолчанию",
|
||||
"The settings have been saved": "Настройки были сохранены"
|
||||
"The settings have been saved": "Настройки были сохранены",
|
||||
"Access is denied": "Доступ запрещён!",
|
||||
"Delete": "Удалить",
|
||||
"Do you want to delete?": "Вы хотите удалить?",
|
||||
"Create": "Создать",
|
||||
"The group was successfully created": "Группа успешно создана",
|
||||
"The group was successfully updated": "Группа успешно обновлена",
|
||||
"The group has been deleted": "Группа была удалена"
|
||||
}
|
||||
|
9
lang/ru/permissions.php
Normal file
9
lang/ru/permissions.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'Role' => 'Группа пользователей',
|
||||
'Allowed to watch' => 'Разрешено смотреть',
|
||||
'Allowed to create' => 'Разрешено создать',
|
||||
'Allowed to edit' => 'Разрешено редактировать',
|
||||
'Allowed to delete' => 'Разрешено удалять',
|
||||
];
|
7
lang/ru/sections.php
Normal file
7
lang/ru/sections.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'Dashboard' => 'Dashboard',
|
||||
'User group' => 'Группа пользователей',
|
||||
];
|
||||
|
@ -141,6 +141,7 @@ return [
|
||||
'uppercase' => 'Значение поля :attribute должно быть в верхнем регистре.',
|
||||
'url' => 'Значение поля :attribute имеет ошибочный формат URL.',
|
||||
'uuid' => 'Значение поля :attribute должно быть корректным UUID.',
|
||||
'no_type' => 'Значение поля :attribute может использовать только: :type.',
|
||||
'attributes' => [
|
||||
'address' => 'адрес',
|
||||
'age' => 'возраст',
|
||||
@ -215,5 +216,7 @@ return [
|
||||
'year' => 'год',
|
||||
'lang' => 'язык',
|
||||
'timezone' => 'часовой пояс',
|
||||
'code' => 'код',
|
||||
'permissions' => 'разрешения',
|
||||
],
|
||||
];
|
||||
|
@ -0,0 +1,24 @@
|
||||
<div class="pt-3">
|
||||
<div class="h5 pb-3">{{ $title }}</div>
|
||||
@error($requestName)
|
||||
<span class="invalid-feedback d-block pb-3">{{ $message }}</span>
|
||||
@enderror
|
||||
@foreach($permissions as $mainPermission)
|
||||
<div class="row ps-3">
|
||||
<div class="mb-3">
|
||||
<span class="h6 fw-bold">{{ $mainPermission->getTitle() }}</span>
|
||||
</div>
|
||||
<ul>
|
||||
@foreach($mainPermission->getPermissions() as $keyPermission => $permission)
|
||||
<li class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="{{ $mainPermission->formatValue($keyPermission) }}" name="{{ $name }}" @checked($role->is_admin || $value->search($mainPermission->formatValue($keyPermission)) !== false) @disabled($role->is_admin) id="form-permission-{{ $requestName }}-{{ $mainPermission->name }}-{{ $keyPermission }}">
|
||||
<label class="form-check-label" for="form-permission-{{ $requestName }}-{{ $mainPermission->name }}-{{ $keyPermission }}">
|
||||
{{ $permission }}
|
||||
</label>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<hr>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
@ -7,12 +7,28 @@
|
||||
<span class="mt-1 ms-1 sidebar-text">Captcha service</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item {{ request()->route()->named('home') ? 'active' : '' }}">
|
||||
<li @class([
|
||||
'nav-item',
|
||||
'active' => request()->route()->named('home')
|
||||
])>
|
||||
<a href="{{ route('home') }}" class="nav-link">
|
||||
<span class="sidebar-icon">
|
||||
<svg class="icon icon-xs me-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path><path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path></svg>
|
||||
</span>
|
||||
<span class="sidebar-text">Dashboard</span>
|
||||
<span class="sidebar-text">{{ __('sections.Dashboard') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@can('viewAny', \App\Models\Role::class)
|
||||
<li @class([
|
||||
'nav-item',
|
||||
'active' => request()->route()->named('roles.*'),
|
||||
])>
|
||||
<a href="{{ route('roles.index') }}" class="nav-link">
|
||||
<span class="sidebar-icon">
|
||||
<svg class="icon icon-xs me-2" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path><path fill-rule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z" clip-rule="evenodd"></path></svg>
|
||||
</span>
|
||||
<span class="sidebar-text">{{ __('sections.User group') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
</ul>
|
||||
|
5
resources/views/private/roles/_from.blade.php
Normal file
5
resources/views/private/roles/_from.blade.php
Normal file
@ -0,0 +1,5 @@
|
||||
@csrf
|
||||
<x-private.forms.input :title="__('validation.attributes.name')" name="name" type="text" :value="$role->name" required autofocus />
|
||||
<x-private.forms.permissions_for_role :title="__('validation.attributes.permissions')" name="permissions[]" :value="$role->permissions->pluck('permission')->toArray()" :role="$role" />
|
||||
<x-private.forms.input :title="__('validation.attributes.code')" name="code" type="text" :value="$role->code" :disabled="!empty($role->id)" required />
|
||||
<button class="btn btn-primary" type="submit">{{ __('Save') }}</button>
|
8
resources/views/private/roles/_top.blade.php
Normal file
8
resources/views/private/roles/_top.blade.php
Normal file
@ -0,0 +1,8 @@
|
||||
@can('create', \App\Models\Role::class)
|
||||
<div class="mb-4">
|
||||
<a href="{{ route('roles.create') }}" class="btn btn-secondary d-inline-flex align-items-center me-2">
|
||||
<svg class="icon icon-xs me-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
|
||||
{{ __('Create') }}
|
||||
</a>
|
||||
</div>
|
||||
@endcan
|
16
resources/views/private/roles/create.blade.php
Normal file
16
resources/views/private/roles/create.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
@section('meta_title', __('sections.User group'))
|
||||
@section('h1', __('sections.User group'))
|
||||
<x-private.layout>
|
||||
@include('private.roles._top')
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<div class="card border-0 shadow components-section">
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('roles.store') }}">
|
||||
@include('private.roles._from')
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-private.layout>
|
17
resources/views/private/roles/edit.blade.php
Normal file
17
resources/views/private/roles/edit.blade.php
Normal file
@ -0,0 +1,17 @@
|
||||
@section('meta_title', __('sections.User group'))
|
||||
@section('h1', __('sections.User group'))
|
||||
<x-private.layout>
|
||||
@include('private.roles._top')
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<div class="card border-0 shadow components-section">
|
||||
<div class="card-body">
|
||||
<form method="post" action="{{ route('roles.update', $role) }}">
|
||||
@method('PUT')
|
||||
@include('private.roles._from')
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-private.layout>
|
54
resources/views/private/roles/index.blade.php
Normal file
54
resources/views/private/roles/index.blade.php
Normal file
@ -0,0 +1,54 @@
|
||||
@section('meta_title', __('sections.User group'))
|
||||
@section('h1', __('sections.User group'))
|
||||
<x-private.layout>
|
||||
@include('private.roles._top')
|
||||
<div class="card border-0 shadow mb-4">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-centered table-nowrap mb-0 rounded">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th class="border-0">{{ __('validation.attributes.name') }}</th>
|
||||
<th class="border-0">{{ __('validation.attributes.code') }}</th>
|
||||
<th class="border-0 rounded-end" style="width: 150px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($roles as $role)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('roles.edit', $role) }}" class="fw-bold">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="align-text-top" viewBox="0 0 16 16">
|
||||
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||
</svg>
|
||||
{{ $role->name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $role->code }}</td>
|
||||
<td>
|
||||
@if($role->is_remove)
|
||||
<form method="post" action="{{ route('roles.destroy', $role) }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-danger click-confirm">
|
||||
{{ __('Delete') }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-footer border-0 d-flex flex-column flex-lg-row align-items-center justify-content-between">
|
||||
<nav aria-label="Пример навигации по странице">
|
||||
{{ $roles->links() }}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@push('scripts')
|
||||
@include('private._scripts._click-confirm', ['alert' => __('Do you want to delete?')])
|
||||
@endpush
|
||||
</x-private.layout>
|
@ -28,4 +28,7 @@ Route::middleware(['auth', 'verified', 'user.locale'])->group(function () {
|
||||
Route::get('settings', [\App\Http\Controllers\Private\ProfileController::class, 'settings'])->name('settings');
|
||||
Route::put('settings', [\App\Http\Controllers\Private\ProfileController::class, 'updateSettings'])->name('update-settings');
|
||||
});
|
||||
|
||||
Route::resource('users', \App\Http\Controllers\Private\UsersController::class)->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])->where(['user' => '[0-9]+']);
|
||||
Route::resource('roles', \App\Http\Controllers\Private\RolesController::class)->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])->where(['role' => '[0-9]+']);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user