'datetime', 'pubblicata_at' => 'datetime', ]; } /** * withTrashed: un territorio nel cestino resta collegato. Senza, la * relazione tornava null, l'editor mostrava la piantina come scollegata e * il primo salvataggio cancellava il collegamento — che al ripristino del * territorio non c'era piu'. */ public function territorio() { return $this->belongsTo(Territorio::class, 'territorio_id')->withTrashed(); } /** * La geometria decodificata (Polygon/MultiPolygon GeoJSON) o null se * il contenuto non è più interpretabile. */ public function geometria(): ?array { $decoded = json_decode((string) $this->geojson, true); return is_array($decoded) ? $decoded : null; } /** Collegata a un territorio vivo: è la condizione per pubblicare. */ public function collegata(): bool { return $this->territorio_id !== null && $this->territorio !== null && ! $this->territorio->trashed(); } public function territorioNelCestino(): bool { return $this->territorio !== null && $this->territorio->trashed(); } /** * Come si chiama questa piantina nell'interfaccia: il numero del * territorio se c'è, altrimenti il nome scelto a mano, altrimenti un * ripiego che almeno la distingue dalle altre. */ public function etichetta(): string { if ($this->territorio !== null) { return 'Terr. ' . $this->territorio->numero; } $nome = trim((string) $this->nome); return $nome !== '' ? $nome : 'Piantina #' . $this->id; } // ─── Bozza e pubblicazione ────────────────────────────────────────────── /** * Impronta di tutto cio' che finisce nel PDF: confine, territorio e i * suoi dati stampati sulla cartolina (numero, zona come «Località», * confini). Se cambia uno qualunque di questi, il PDF in vigore non * corrisponde piu' e la piantina ha modifiche da pubblicare — anche quando * il disegno e' rimasto lo stesso. */ public function impronta(): string { $territorio = $this->territorio; // Il territorio entra con il numero, non con l'id: il numero è unico e // il ripristino di un backup ricrea i territori con id nuovi, che // facevano risultare da pubblicare ogni piantina appena ripristinata. return hash('sha256', json_encode([ $this->geometria(), $territorio ? trim((string) $territorio->numero) : null, $territorio?->zona?->nome, $territorio ? trim((string) $territorio->confini) : null, ])); } public function stato(): string { if ($this->pubblicata_at === null) { return self::STATO_BOZZA; } return $this->impronta() === $this->impronta_pubblicata && $this->pdfInUsoDallaPiantina() ? self::STATO_PUBBLICATA : self::STATO_MODIFICATA; } /** * Il PDF in vigore del territorio è ancora quello prodotto dall'ultima * pubblicazione. Se nel frattempo è stato sostituito per altre vie * (ripristino di una versione, caricamento a mano, import), quello che * arriva ai proclamatori non è più questo disegno, anche se l'impronta * torna. Si confronta il percorso, che il backup conserva: prima si * cercava l'id del territorio nel nome del file, e dopo un ripristino * (id nuovi) non tornava più. */ public function pdfInUsoDallaPiantina(): bool { return $this->pdf_pubblicato !== null && $this->territorio !== null && $this->territorio->pdf_path === $this->pdf_pubblicato; } /** Il testo dell'etichetta di stato, uguale in ogni pagina. */ public function statoEtichetta(): string { return match ($this->stato()) { self::STATO_BOZZA => 'Bozza', self::STATO_PUBBLICATA => 'Pubblicata', self::STATO_MODIFICATA => 'Modifiche da pubblicare', }; } /** * Perché non si può pubblicare, o null se si può. Il testo va a schermo * accanto al pulsante: dice cosa fare, non solo che manca qualcosa. */ public function motivoNonPubblicabile(): ?string { if ($this->geometria() === null) { return 'Disegna il confine della piantina.'; } if ($this->territorioNelCestino()) { return 'Il territorio collegato è nel cestino: ripristinalo o collega un altro territorio.'; } if (! $this->collegata()) { return 'Collega la piantina a un territorio: il PDF è quello del territorio.'; } return null; } /** Il confine pubblicato è diverso da quello in bozza: si può scartare. */ public function bozzaScartabile(): bool { return $this->geojson_pubblicato !== null && json_decode($this->geojson_pubblicato, true) != $this->geometria(); } }