Added the ability to manage a group of users.

This commit is contained in:
2023-07-16 19:21:09 +06:00
parent ba7e52f8ac
commit 4083e2ec5e
45 changed files with 1173 additions and 10 deletions

View 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(),
];
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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);
}
}

View 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();
}
}

View File

@@ -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);