Files
reservas/app/Domain/Identity/Services/UserAdministrationService.php

136 lines
4.7 KiB
PHP

<?php
namespace App\Domain\Identity\Services;
use App\Domain\Audit\Models\AuditLog;
use App\Domain\Identity\Enums\RoleName;
use App\Domain\Identity\Models\Role;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class UserAdministrationService
{
/**
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:string,active:bool,roles:list<string>} $data
*/
public function create(User $actor, array $data): User
{
return DB::transaction(function () use ($actor, $data): User {
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'email_verified_at' => now(),
'department' => $data['department'],
'snipe_user_id' => $data['snipe_user_id'],
'password' => $data['password'],
'active' => $data['active'],
]);
$user->roles()->sync($this->roleIds($data['roles']));
AuditLog::create([
'actor_id' => $actor->id,
'event' => 'user.created',
'auditable_type' => $user->getMorphClass(),
'auditable_id' => $user->id,
'after' => $this->snapshot($user),
]);
return $user->load('roles');
}, 3);
}
/**
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:?string,active:bool,roles:list<string>} $data
*/
public function update(User $user, User $actor, array $data): User
{
return DB::transaction(function () use ($user, $actor, $data): User {
$locked = User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();
$before = $this->snapshot($locked);
$removesAdministrator = ! in_array(RoleName::Administrator->value, $data['roles'], true);
if ($locked->id === $actor->id && (! $data['active'] || $removesAdministrator)) {
throw ValidationException::withMessages([
'roles' => 'Não pode desativar a própria conta nem remover o seu perfil de administrador.',
]);
}
if ((! $data['active'] || $removesAdministrator)
&& $locked->hasRole(RoleName::Administrator)
&& $this->activeAdministratorCount() <= 1) {
throw ValidationException::withMessages([
'roles' => 'O portal tem de manter pelo menos um administrador ativo.',
]);
}
$attributes = [
'name' => $data['name'],
'email' => $data['email'],
'department' => $data['department'],
'snipe_user_id' => $data['snipe_user_id'],
'active' => $data['active'],
];
if (filled($data['password'])) {
$attributes['password'] = $data['password'];
}
$locked->update($attributes);
$locked->roles()->sync($this->roleIds($data['roles']));
$locked->load('roles');
AuditLog::create([
'actor_id' => $actor->id,
'event' => 'user.updated',
'auditable_type' => $locked->getMorphClass(),
'auditable_id' => $locked->id,
'before' => $before,
'after' => $this->snapshot($locked),
'metadata' => ['password_changed' => filled($data['password'])],
]);
return $locked;
}, 3);
}
/**
* @param list<string> $roles
* @return list<int>
*/
private function roleIds(array $roles): array
{
$ids = array_values(Role::query()
->whereIn('name', $roles)
->pluck('id')
->map(fn (int $id): int => $id)
->all());
if (count($ids) !== count(array_unique($roles))) {
throw ValidationException::withMessages(['roles' => 'Um ou mais perfis selecionados não existem.']);
}
return $ids;
}
private function activeAdministratorCount(): int
{
return User::query()
->where('active', true)
->whereHas('roles', fn ($query) => $query->where('name', RoleName::Administrator->value))
->count();
}
/** @return array<string, mixed> */
private function snapshot(User $user): array
{
return [
'name' => $user->name,
'email' => $user->email,
'department' => $user->department,
'snipe_user_id' => $user->snipe_user_id,
'active' => $user->active,
'roles' => $user->roles()->orderBy('name')->pluck('name')->all(),
];
}
}