Files
kreios/backoffice/app/Models/Modello.php
T
carmine.savino 1208187a17 feat: integrate Draco compression for 3D models in backoffice
- Added support for uploading and compressing GLB models using Draco in the backoffice.
- Implemented commands for importing and re-compressing 3D models.
- Updated model resource to handle 3D model uploads and validations.
- Introduced a new script for compressing GLB files with Draco.
- Modified Vite configuration to remove GLB files from the build output.
- Updated package dependencies to include GLTF-transform libraries for handling 3D models.
- Added tests to ensure proper functionality of 3D model handling and compression.
2026-11-01 10:35:50 +01:00

171 lines
5.5 KiB
PHP

<?php
namespace App\Models;
use App\Support\Configuratore\Media;
use App\Support\Configuratore\Modelli3d;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class Modello extends Model
{
protected $table = 'modelli';
// Categorie usate dal configuratore (es. per scegliere altezza_maniglia_<categoria>.json).
public const CATEGORIE = [
'finestre' => 'Finestre',
'porte' => 'Porte',
'scorrevoli' => 'Scorrevoli',
'scuretti' => 'Scuretti',
'accessori' => 'Accessori',
];
// Moduli base del configuratore (sezioni del menu e voci di prezzo); quelli dei gruppi
// di opzioni si aggiungono da GruppoOpzioni (vedi moduliDisponibili).
public const MODULI = [
'listino' => 'Listino',
'modello' => 'Finiture',
'finitura_esterna' => 'Finitura esterna (bicolore)',
'maniglia' => 'Maniglia',
'altezza_maniglia' => 'Altezza maniglia',
'vetro' => 'Vetro',
'avvolgibile' => 'Avvolgibile',
'select_finiture' => 'Select finiture',
];
protected $fillable = [
'sistema_id', 'codice', 'nome', 'descrizione', 'titolo', 'icona', 'ordine', 'attivo',
'cartella', 'file_modello', 'categoria', 'cartella_texture', 'moduli',
'regola_vetro_id', 'listino_aggiornato_at',
];
protected $casts = [
'ordine' => 'integer',
'attivo' => 'boolean',
'moduli' => 'array',
'listino_aggiornato_at' => 'datetime',
'modello_3d_dimensione' => 'integer',
'modello_3d_draco_dimensione' => 'integer',
'modello_3d_aggiornato_at' => 'datetime',
];
public function sistema(): BelongsTo
{
return $this->belongsTo(Sistema::class);
}
public function listino(): HasMany
{
return $this->hasMany(ListinoPrezzo::class);
}
public function regolaVetro(): BelongsTo
{
return $this->belongsTo(RegolaVetro::class);
}
/** Opzioni personalizzate del modello (sostituiscono i gruppi del sistema). */
public function opzioni(): HasMany
{
return $this->hasMany(Opzione::class);
}
/** Tutte le finiture del profilo del sistema (per scegliere quali usare nel modello). */
public function finitureDelSistema(): HasMany
{
return $this->hasMany(Finitura::class, 'sistema_id', 'sistema_id')->where('tipo', 'profilo');
}
/** Moduli selezionabili: quelli base più uno per ogni gruppo di opzioni con un controllo. */
public static function moduliDisponibili(): array
{
$gruppi = GruppoOpzioni::query()
->where('ui', '!=', 'nessuna')
->orderBy('ordine')
->pluck('etichetta', 'modulo')
->all();
return self::MODULI + $gruppi;
}
public function haModulo(string $modulo): bool
{
return in_array($modulo, $this->moduli ?? [], true);
}
public function scopeAttivi(Builder $query): Builder
{
return $query->where('attivo', true)->orderBy('ordine')->orderBy('id');
}
/**
* Modelli mostrati nel configuratore: attivi, con un listino (senza prezzi non si possono
* vendere) e con il modello 3D caricato.
*/
public function scopeNelConfiguratore(Builder $query): Builder
{
return $query->attivi()->whereNotNull('modello_3d_draco')->whereHas('listino');
}
/** URL del modello 3D compresso (Draco) letto dal configuratore. */
public function getModello3dUrlAttribute(): ?string
{
return Media::url($this->modello_3d_draco);
}
public function getIconaUrlAttribute(): ?string
{
return $this->icona ? Storage::disk('public')->url($this->icona) : null;
}
/**
* Rinumera l'ordine dei modelli del sistema mettendo i disattivi in fondo,
* senza cambiare l'ordine relativo all'interno dei due gruppi.
*/
public static function normalizzaOrdine(int $sistemaId): void
{
$ids = static::query()
->where('sistema_id', $sistemaId)
->orderByDesc('attivo')
->orderBy('ordine')
->orderBy('id')
->pluck('id');
DB::transaction(function () use ($ids) {
foreach ($ids as $i => $id) {
// Query diretta: niente eventi del modello (e niente ricorsione).
static::query()->whereKey($id)->update(['ordine' => $i + 1]);
}
});
}
protected static function booted(): void
{
// Attivando o disattivando un modello (o creandone uno), i disattivi tornano in fondo.
static::saved(function (Modello $modello): void {
if ($modello->wasRecentlyCreated || $modello->wasChanged(['attivo', 'sistema_id'])) {
static::normalizzaOrdine($modello->sistema_id);
}
});
// Rimuove il file dell'icona sostituita o del modello eliminato.
static::updated(function (Modello $modello): void {
$vecchia = $modello->getOriginal('icona');
if ($modello->wasChanged('icona') && $vecchia) {
Storage::disk('public')->delete($vecchia);
}
});
static::deleted(function (Modello $modello): void {
if ($modello->icona) {
Storage::disk('public')->delete($modello->icona);
}
app(Modelli3d::class)->elimina($modello);
});
}
}