belongsTo(GruppoServizio::class, 'gruppo_servizio_id'); } /** L'utenza, se è sorvegliante di un gruppo. */ public function utente() { return $this->hasOne(User::class, 'proclamatore_id'); } /** * Un sorvegliante che non è più attivo — reso non attivo, spostato nel * cestino o anonimizzato — non guida più il gruppo: il gruppo si scioglie * (GruppiServizio::sciogli), da qualunque pagina arrivi la modifica. Un * membro nel cestino lascia il gruppo. */ protected static function booted(): void { static::updated(function (self $proclamatore) { if ($proclamatore->wasChanged('attivo') && ! $proclamatore->attivo) { $proclamatore->lasciaIlGruppo('sorvegliante non più attivo'); } }); static::deleted(function (self $proclamatore) { $proclamatore->lasciaIlGruppo('sorvegliante eliminato'); }); } /** Il gruppo attivo di cui è sorvegliante, se c'è. */ public function gruppoSorvegliato(): ?GruppoServizio { return GruppoServizio::where('sorvegliante_id', $this->id)->first(); } private function lasciaIlGruppo(string $motivo): void { $gruppo = $this->gruppoSorvegliato(); if ($gruppo) { app(\App\Services\GruppiServizio::class)->sciogli($gruppo, $motivo); return; } if ($this->trashed() && $this->gruppo_servizio_id) { static::withTrashed()->whereKey($this->id)->update(['gruppo_servizio_id' => null]); $this->gruppo_servizio_id = null; } } protected function casts(): array { return [ 'nome' => 'encrypted', 'cognome' => 'encrypted', 'attivo' => 'boolean', 'gruppo_servizio_id' => 'integer', ]; } public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly(['attivo']) // Do NOT log nome/cognome in audit (encrypted, GDPR) ->logOnlyDirty() ->dontSubmitEmptyLogs(); } /** * Full name (decrypted, only visible in PHP/UI). */ public function getNomeCompletoAttribute(): string { return trim($this->cognome . ' ' . $this->nome); } /** * L'opzione per il campo di scelta con ricerca (): si cerca per * cognome e nome. Nome e cognome sono cifrati nel database: la ricerca * avviene sui valori già decifrati, nella pagina, mai in SQL. */ public function opzioneScelta(?string $motivo = null): array { // Il gruppo di servizio come nota, solo se già caricato (opzioniScelta). $gruppo = $this->relationLoaded('gruppo') ? $this->gruppo_etichetta : null; return array_filter([ 'valore' => (string) $this->id, 'etichetta' => $this->nome_completo, 'dettaglio' => $gruppo !== null ? lcfirst($gruppo) : null, 'chiavi' => [(string) $this->cognome, (string) $this->nome], 'motivo' => $motivo, ], fn ($v) => $v !== null); } /** * Le opzioni di un elenco di proclamatori, in ordine di cognome e nome. * L'ordine si fa qui: in SQL ordinerebbe i testi cifrati. Con $conGruppo * chi fa parte di un gruppo di servizio lo dice accanto al nome (non serve * dove sono tutti dello stesso gruppo, come in "Il mio gruppo"). */ public static function opzioniScelta(iterable $proclamatori, bool $conGruppo = true): array { if ($conGruppo && $proclamatori instanceof \Illuminate\Database\Eloquent\Collection) { $proclamatori->loadMissing('gruppo'); } return collect($proclamatori) ->sortBy(fn (self $p) => mb_strtolower($p->cognome . ' ' . $p->nome)) ->map(fn (self $p) => $p->opzioneScelta($p->attivo ? null : 'non attivo')) ->values() ->all(); } /** * "Sorvegliante del gruppo Zisa" o "Gruppo Zisa"; null fuori dai gruppi. * Senza gruppo_servizio_id non fa query. */ public function getGruppoEtichettaAttribute(): ?string { $gruppo = $this->gruppo; if (! $gruppo) { return null; } return $gruppo->sorvegliante_id === $this->id ? "Sorvegliante del gruppo {$gruppo->nome}" : "Gruppo {$gruppo->nome}"; } public function assegnazioni() { return $this->hasMany(Assegnazione::class, 'proclamatore_id'); } public function assegnazioniAperte() { return $this->hasMany(Assegnazione::class, 'proclamatore_id') ->whereNull('returned_at'); } public function scopeAttivi($query) { return $query->where('attivo', true); } /** * Anonymize this proclamatore (GDPR right to be forgotten). */ public function anonimizza(): void { $this->nome = 'Anonimo'; $this->cognome = 'Proclamatore #' . $this->id; $this->attivo = false; $this->gruppo_servizio_id = null; $this->save(); $this->delete(); // soft delete // L'utenza di un ex sorvegliante (già disattivata: il sorvegliante di // un gruppo attivo non si anonimizza) porta nome ed email in chiaro. $utente = $this->utente()->first(); if ($utente) { $utente->syncRoles([]); $utente->forceFill([ 'name' => 'Anonimo', 'email' => null, 'username' => "anonimo.{$utente->id}", 'password' => \Illuminate\Support\Str::random(40), 'remember_token' => null, 'disattivato_at' => $utente->disattivato_at ?? now(), ])->save(); } } }