} $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} $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 $roles * @return list */ 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 */ 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(), ]; } }