<65;192;33M<65;192;33M LabQuoteSystem — Relazione Finale

LabQuote System

Sistema web per la gestione di preventivi di esami di laboratorio

Studente Pietro Mozzoni
Classe 5° BINF
Disciplina TPSIT
A.S. 2025/2026
Docente prof. Santobuono

Tecnologie e Progettazione di Sistemi Informatici e di Telecomunicazioni

Copertina
Indice

Indice

01Introduzione al progetto
02Obiettivi del progetto
03Architettura 3-tier
04Modello client-server
05API REST
06Sessioni e cookie
07Tecnologie utilizzate
08Progettazione del database
09Funzionalità principali
10Flusso operativo completo
11Sicurezza e controllo dei dati
12Criticità incontrate e soluzioni adottate
13Conclusioni e sviluppi futuri
1 · Introduzione

1. Introduzione al progetto

Mockup interfaccia utente LabQuoteSystem
Interfaccia utente

LabQuoteSystem è un'applicazione web che permette a un cliente (oppure ad un operatore del laboratorio analisi) di consultare il catalogo degli esami disponibili, selezionarli e generare un preventivo che viene successivamente salvato in modo persistente nel database.

Il sistema risponde a un problema concreto e quotidiano nel settore sanitario: oggi il paziente che vuole conoscere il costo di un insieme di esami deve telefonare o recarsi fisicamente in laboratorio. Il progetto LabQuoteSystem digitalizza questo processo e permette di:

  • consultare il catalogo in tempo reale;
  • comporre un "carrello" di esami;
  • ottenere un preventivo numerato salvato a sistema, connesso all'utente che lo ha richiesto.

Il sistema si rivolge a due tipologie di utenti:

RuoloCosa può fare
Utente standardVisualizzare esami attivi, creare preventivi a proprio nome
AmministratoreFunzionalità dell'utente standard + gestione catalogo (modifica e disattivazione esami) + visualizzazione dei preventivi salvati
Mockup dashboard admin su smartphone
Dashboard admin
2 · Obiettivi

2. Obiettivi del progetto

Gli obiettivi funzionali e tecnici sono stati definiti in coerenza con la roadmap didattica del corso TPSIT 5°.

Obiettivi funzionali

7 obiettivi
  • Sistema di autenticazione con login, logout e gestione sessione persistente
  • Gestione ruoli (utente / amministratore) con permessi differenziati
  • Visualizzazione del catalogo esami con filtri per categoria e ricerca per nome
  • Selezione esami in un carrello persistente (persistanza anche dopo che la pagina viene ricaricata)
  • Creazione preventivo con dati del paziente e salvataggio nel database
  • Pannello amministratore con operazioni CRUD (create,read,update,delete) sugli esami
  • Visualizzazione storico preventivi lato admin

Obiettivi tecnici / didattici

5 obiettivi
  • Applicare correttamente l'architettura 3-tier
  • Esporre il backend tramite API REST con risposte in file di tipo JSON
  • Usare PDO + prepared statements per il DB
  • Implementare sessioni PHP per autenticazione
  • Separare frontend (JS + HTML + CSS) da backend (PHP)
3 · Architettura

3. Architettura 3-tier

Il progetto è organizzato secondo l'architettura a tre livelli (3-tier), una sistema che permette di separare in modo netto interfaccia, logica applicativa e persistenza dei dati.

Presentation Tier (Client)

Browser web con interfaccia in HTML, CSS e JavaScript

  • Pagine: index.html, login.html, admin.html
  • Script: main.js (utente), admin.js (amministratore)
  • Stili: style.css (un unico foglio condiviso da tutte le pagine)

Compiti del livello:

  • mostrare catalogo esami, carrello e preventivi;
  • gestire eventi utente (click, input, submit);
  • inviare richieste HTTP asincrone con fetch();
  • aggiornare il DOM in base alle risposte JSON.
Application Tier (Server)

Backend in PHP che espone API REST e gestisce la logica applicativa.

  • Endpoint: api/login.php, api/logout.php, api/exams.php, api/quotes.php
  • Configurazione: config/database.php (connessione PDO), config/auth.php (funzioni di login/logout)

Compiti del livello:

  • ricevere richieste GET, POST, PUT;
  • validare gli input lato server;
  • gestire sessioni PHP e controllare i ruoli;
  • coordinare l'accesso al database tramite PDO;
  • restituire risposte JSON con status code HTTP corretti.
Data Tier (Database)

MySQL ospita i dati persistenti.

  • 4 tabelle relazionali: users, exams, quotes, quote_items
  • Vincoli di integrità referenziale tramite chiavi esterne

Compiti del livello:

  • conservare credenziali utenti (convertite in hash con bcrypt);
  • conservare il catalogo esami;
  • conservare i preventivi e le righe di dettaglio;
  • garantire integrità referenziale tra le tabelle.
3 · Architettura

3. Architettura 3-tier

La separazione a livelli rende il progetto più ordinato, manutenibile e scalabile: si possono sostituire o aggiornare singolarmente le tre parti senza riscrivere tutto il sistema.

Schema architettura 3-tier: Client, Server PHP, Database MySQL
Schema architettura 3-tier
4 · Client-server

4. Modello client-server

La comunicazione tra browser e server segue il modello request-response su protocollo HTTP. Tutte le interazioni dell'utente con il sistema si traducono in scambi di questo tipo.

Flusso generale

  1. L'utente apre una pagina nel browser
  2. Il JavaScript della pagina (main.js o admin.js) usa fetch() per chiamare un endpoint REST
  3. Insieme alla richiesta, il browser invia automaticamente il cookie di sessione PHPSESSID
  4. Il server PHP legge la sessione, riconosce l'utente, valida l'input
  5. Il server interroga MySQL tramite PDO
  6. Il server costruisce una risposta JSON con status code HTTP appropriato
  7. Il client riceve la risposta e aggiorna il DOM (o fa redirect)
Diagramma di sequenza UML: utente, browser, server PHP, MySQL
Diagramma di sequenza UML
4 · Client-server

4. Modello client-server — Esempio richiesta-risposta

Esempio di richiesta-risposta

Richiesta Richiesta inviata dal browser
Screenshot richiesta HTTP POST verso api/quotes.php con cookie PHPSESSID e body JSON
Risposta Risposta restituita dal server
Screenshot risposta HTTP 201 Created con JSON del preventivo salvato
5 · API REST

5. API REST

Il backend è progettato secondo lo stile REST: le risorse del sistema (esami, preventivi) sono identificate da URL, e le operazioni sono espresse tramite i metodi HTTP standard.

Endpoint del progetto

URLMetodoFunzionePermessi
/api/login.phpPOSTAutenticazionePubblico
/api/logout.phpGETTermina la sessioneLoggato
/api/exams.phpGETLista esami attiviLoggato
/api/exams.php?requireRole=adminGETLista esami (anche inattivi)Admin
/api/exams.php?id=XGETSingolo esameLoggato
/api/exams.php?id=XPUTModifica esameAdmin
/api/quotes.phpPOSTCrea nuovo preventivoLoggato
/api/quotes.phpGETLista tutti i preventiviAdmin
5 · API REST

5. API REST — Convenzioni HTTP e JSON

Convenzioni HTTP usate

StatusSignificatoQuando viene restituito
200 OKOperazione riuscitaGET con dati restituiti
201 CreatedRisorsa creataPOST quotes.php riuscita
400 Bad RequestInput non validoEmail mal formattata, body JSON corrotto
401 UnauthorizedNon autenticatoNessuna sessione attiva
403 ForbiddenAutenticato ma non autorizzatoUtente non-admin che chiama endpoint admin
404 Not FoundRisorsa inesistenteID esame non trovato
405 Method Not AllowedMetodo non supportatoDELETE su un endpoint che gestisce solo GET/POST
500 Internal Server ErrorErrore inatteso del serverEccezione PDO non gestita

L'uso di status code semanticamente corretti permette al frontend di distinguere i casi: un 401 fa redirect a login.html, un 403 fa redirect a index.html, un 500 mostra un messaggio di errore generico.

5 · API REST

5. API REST — Formato risposta JSON

Tutte le risposte seguono uno schema coerente:

Screenshot esempio risposta JSON con campi ok, msg, data e error
6 · Sessioni

6. Sessioni e cookie

L'autenticazione è gestita con sessioni PHP, una tecnica server-side che permette di mantenere lo "stato" dell'utente tra una richiesta HTTP e l'altra (HTTP è un protocollo stateless).

Come lavorano insieme cookie e sessione

ElementoLatoContenutoDurata
Cookie PHPSESSIDClient (browser)Un identificatore alfanumerico casualeFino a chiusura del browser
File di sessioneServerDati reali: user_id, username, roleFinché esiste il file

L'identificatore di sessione viaggia nel cookie, ma i dati sensibili (id utente, ruolo) restano sempre sul server — il client non li vede mai, non può modificarli.

Schema cookie e sessione PHP
Cookie e sessione PHP
6 · Sessioni

6. Sessioni e cookie

Cosa viene salvato nella sessione

Nel file config/auth.php, dopo un login corretto:

Screenshot salvataggio variabili di sessione PHP dopo login

Verifica negli endpoint protetti

Ogni endpoint protetto inizia con:

Screenshot verifica sessione PHPSESSID negli endpoint protetti
7 · Tecnologie

7. Tecnologie utilizzate

Ogni tecnologia è stata scelta per uno specifico ruolo nell'architettura.

Frontend

TecnologiaRuolo nel progetto
HTMLda riscrivere
CSSda riscrivere
JavaScriptda riscrivere

Backend

TecnologiaRuolo nel progetto
PHPEsposizione delle API REST, gestione della logica server-side, validazione input, controllo permessi
PDOAstrazione per l'accesso a MySQL con prepared statements; previene SQL injection
Sessioni PHPAutenticazione e memorizzazione del ruolo utente

Database

TecnologiaRuolo nel progetto
MySQL 8Database relazionale per la persistenza dei dati

Strumenti di sviluppo

  • VS Code come IDE
  • XAMPP / Apache per ambiente di test locale
  • DevTools del browser per debugging delle chiamate API
  • phpMyAdmin per ispezione manuale del database
8 · Database

8. Progettazione del database

Il database 5binf_mozzonipietro_db è strutturato su 4 tabelle che modellano gli attori e gli oggetti del dominio.

Descrizione delle tabelle

users — utenti del sistema

  • id (PK, auto increment)
  • username (UNIQUE) — nome di login
  • password_hash — hash bcrypt della password (mai password in chiaro)
  • role (ENUM 'admin', 'user') — ruolo per il controllo permessi
  • active (BOOL) — flag per disattivare un account senza eliminarlo

exams — catalogo esami disponibili

  • id (PK)
  • name, price, category, description — dati anagrafici dell'esame
  • active (BOOL) — flag per disattivare un esame senza perderne lo storico
  • created_at, updated_at — timestamp gestiti automaticamente da MySQL
Diagramma ER del database
Diagramma ER
8 · Database

8. Progettazione del database

quotes — preventivi creati

  • id (PK)
  • patient_name, patient_email, patient_phone — dati del paziente
  • total (DECIMAL 10,2) — totale calcolato server-side
  • status (ENUM) — stato del preventivo
  • user_id (FK → users) — chi ha creato il preventivo
  • notes, created_at

quote_items — righe del preventivo (tabella di associazione N-M)

  • id (PK)
  • quote_id (FK → quotes) ON DELETE CASCADE — se cancello un preventivo, cancello anche le righe
  • exam_id (FK → exams) ON DELETE RESTRICT — non posso cancellare un esame referenziato in un preventivo
  • qty, price, subtotal — i prezzi sono congelati al momento della creazione, così se in futuro cambia il listino il preventivo storico resta corretto

Indici

Sono stati creati indici sulle colonne più filtrate per accelerare le query:

  • idx_category su exams(category) — filtro per categoria
  • idx_active su exams(active) — selezione solo attivi
  • idx_user_id su quotes(user_id) — preventivi di un utente
  • idx_created_at su quotes(created_at) — ordinamento cronologico
9 · Funzionalità

9. Funzionalità principali

10.1 Autenticazione

  • Login (login.html + api/login.php): username e password vengono inviati via POST, validati contro il DB con password_verify(), e in caso di successo viene creata la sessione PHP. I dati utente (non sensibili) vengono anche memorizzati in sessionStorage per dare dinamicità all'interfaccia grafica con campi che cambiano in base all'utente loggato.
  • Logout (api/logout.php): distrugge la sessione lato server, il client pulisce sessionStorage e localStorage (carrello).
  • Controllo accesso: ogni endpoint protetto verifica $_SESSION['user_id']; gli endpoint admin verificano anche $_SESSION['role'] === 'admin' (ruolo dell'utente).

10.2 Catalogo esami con ricerca e filtri

  • Lista esami attivi caricata via GET /api/exams.php
  • Filtro per categoria (dropdown popolato dinamicamente con le categorie degli esami)
  • Ricerca per nome con LIKE %......% server-side
  • Bottone "Resetta Filtri" per tornare alla vista completa

10.3 Carrello persistente

  • L'utente seleziona esami con checkbox accanto a ogni voce
  • Il carrello è memorizzato in localStorage con chiave lab_cart → persistenza dei dati in caso di refresh, chiusura/riapertura del browser, ecc.
  • Una barra sticky in basso mostra il numero di esami selezionati e il totale parziale calcolato in tempo reale
  • Bottone "Vedi Dettagli" apre una modale personalizzato con il riepilogo, dove si possono rimuovere singoli esami o svuotare il carrello
9 · Funzionalità

9. Funzionalità principali — Preventivo e admin

10.4 Conferma preventivo

  • Pulsante "Conferma Preventivo" nella modale del carrello
  • Validazione lato client: nome, email, telefono compilati e carrello non vuoto
  • POST /api/quotes.php con il payload JSON
  • Il server ricalcola il totale leggendo i prezzi reali dal DB (è possibile che l'utente cambi i prezzi in locale) e usando una transazione SQL atomica: o tutte le righe del preventivo vengono salvate, oppure rollback
  • Alert di conferma con il numero del preventivo creato

10.5 Pannello amministratore

Accessibile solo agli utenti con ruolo admin tramite il pulsante "griglia" nell'header (pulsante visibile solo agli utenti con ruolo admin).

La dashboard ha 2 sezioni:

  • Sezione 1 — Gestione esami: stessa lista del catalogo + per ogni esame un toggle "Attivo" (PUT per modificare lo stato immediata al server) e un bottone "Modifica" che apre 4 prompt nativi del browser in sequenza (nome, categoria, prezzo, descrizione)
  • Sezione 2 — Preventivi: lista di tutti i preventivi salvati nel sistema. Ogni preventivo è cliccabile per espandere i dettagli paziente, eventuali note, e la lista degli esami inclusi con quantità e subtotale.
Dashboard admin preventivi
Pannello admin — storico preventivi
10 · Flusso

10. Esempio di flusso operativo completo

Vediamo un caso d'uso reale: un paziente loggato crea un preventivo per 3 esami.

Passo-passo testuale (1–9)

  1. L'utente apre il catalogo degli esami (index.html)
  2. main.js parte e mostra il catalogo (chiamata GET /api/exams.php)
  3. L'utente compila nome, email, telefono
  4. L'utente seleziona 3 esami → il carrello si aggiorna istantaneamente nella barra inferiore
  5. Click su "Vedi Dettagli" → si apre il pop-up con il riepilogo
  6. Click su "Conferma Preventivo"
  7. Il client valida che tutti i campi siano compilati
  8. Il client costruisce il JSON e fa POST /api/quotes.php
  9. PHP verifica la sessione (session_start())
Flowchart creazione preventivo
Diagramma del flusso
10 · Flusso

10. Esempio di flusso operativo completo

Passo-passo testuale (10–17)

  1. PHP valida il body: nome paziente obbligatorio, email valida, almeno 1 item
  2. PHP apre una transazione (beginTransaction())
  3. PHP esegue una SELECT id, price FROM exams WHERE id IN (?, ?, ?) AND active = TRUE per ricalcolare i prezzi dal DB
  4. PHP fa INSERT INTO quotes ottenendo il nuovo quote_id
  5. PHP fa una INSERT INTO quote_items per ogni esame
  6. COMMIT → tutto va a buon fine
  7. Risposta HTTP 201 con {ok: true, quote_id, total}
  8. Il client mostra un alert di conferma, svuota il carrello, resetta il form
11 · Sicurezza

11. Sicurezza e controllo dei dati

La sicurezza è stata trattata su più livelli applicando il principio di difesa profonda: ogni controllo è ridondato tra client e server, ma la "verità" è sempre quella del server.

Protezioni implementate

RischioProtezione adottata
SQL InjectionTutte le query usano prepared statements PDO con placeholder ?. Nessuna concatenazione di stringhe in SQL.
Password in chiaroLe password sono salvate solo come hash bcrypt generati con password_hash() e verificati con password_verify().
Accesso non autorizzato alle APIOgni endpoint inizia con session_start() + controllo $_SESSION['user_id']. Senza sessione → 401.
Controllo del ruoloLe operazioni di scrittura sugli esami (PUT/POST/DELETE) richiedono $_SESSION['role'] === 'admin'. Senza il ruolo → 403.
Manipolazione dei prezziI prezzi del preventivo sono ricalcolati lato server leggendoli dalla tabella exams. Il client può inviare qualunque prezzo, viene ignorato.
Inconsistenza nei salvataggiIl preventivo viene salvato dentro una transazione SQL: se l'INSERT degli items fallisce, il rollback rimuove anche la riga in quotes.
Visualizzazione di informazioni interneI messaggi di errore PHP non espongono il dettaglio delle eccezioni nelle risposte JSON; il dettaglio finisce in error_log server-side.
Accesso a pagine protetteadmin.html ha una verifica server-side via GET /api/exams.php?requireRole=admin prima di mostrare il contenuto (visibility: hidden iniziale).
11 · Sicurezza

11. Sicurezza e controllo dei dati

Differenza tra controllo lato client e lato server

ControlloA cosa serveEsempio nel progetto
Client-sideMigliorare l'interfaccia grafica (feedback immediato, evita chiamate inutili)Disabilitazione del bottone "Cerca" se il campo è vuoto; check di sessionStorage.user.role per mostrare/nascondere il bottone dashboard
Server-sideSicurezza vera (il client può sempre essere manipolato)Validazione email con FILTER_VALIDATE_EMAIL, ricalcolo del totale, controllo del ruolo
12 · Criticità

12. Criticità incontrate e soluzioni adottate

Durante lo sviluppo sono emersi alcuni problemi reali, la cui risoluzione ha richiesto comprensione approfondita dei meccanismi sottostanti.

01

Bottone "Dashboard" non visibile per l'admin

Problema: anche loggati come admin, l'icona della dashboard non compariva nell'header.

Causa: la funzione checkAuth() leggeva il ruolo da sessionStorage.getItem('role'), ma in login.html veniva salvato solo user (un oggetto JSON contenente anche role), non una chiave separata role.

Soluzione: modifica del controllo da if (role === 'admin') a if (user && user.role === 'admin').

02

Distinguere "non loggato" da "loggato senza permessi"

Problema: sia l'utente non autenticato sia l'utente non-admin ricevevano lo stesso status 403, e il client li trattava entrambi facendo redirect al login.

Causa: mancanza di distinzione semantica nei codici HTTP.

Soluzione: ho introdotto la distinzione 401 (non autenticato) vs 403 (non autorizzato) e il frontend si modifica in modo diverso: 401 → login.html, 403 → index.html.

03

Manipolazione dei prezzi lato client

Riflessione: il carrello sta in localStorage ed è potenzialmente modificabile dall'utente. Se il server si fidasse del totale o dei prezzi inviati dal client, un utente smaliziato potrebbe risparmiare manipolando i valori.

Soluzione: quando il client invia POST /api/quotes.php, manda solo gli exam_id (e quantità). Il server ignora qualsiasi prezzo nel messaggio e ricalcola il totale leggendo i prezzi reali dalla tabella exams.

12 · Criticità

12. Criticità incontrate e soluzioni adottate

04

Cache aggressiva del browser sui file JS/CSS

Problema: dopo aver modificato main.js, le modifiche non avevano effetto.

Causa: il browser teneva in cache la vecchia versione del file.

Soluzione: uso di cache busting tramite query string sulle risorse: main.js?v=9, style.css?v=5. Quando aggiorno il file incremento la versione e il browser ricarica.

05

Lettura del ruolo da sessionStorage manipolabile

Riflessione: un utente potrebbe aprire DevTools e settare sessionStorage.user = {role: 'admin'} per accedere alla dashboard admin.

Soluzione: il controllo client su sessionStorage serve solo per UX (nascondere/mostrare il bottone). La sicurezza vera è la verifica server-side di $_SESSION['role'] su ogni chiamata API. Se l'utente "bara" lato client, vede una pagina vuota perché tutte le API rispondono 403.

13 · Conclusioni

13. Conclusioni e sviluppi futuri

Risultati raggiunti

Il progetto LabQuoteSystem realizza i seguenti obbiettivi:

  • Architettura 3-tier ben separata tra presentation, application e data tier
  • API REST funzionanti con risposte JSON e status code HTTP corretti
  • Autenticazione completa con sessioni PHP, cookie e gestione ruoli
  • Database relazionale normalizzato con vincoli di integrità
  • Sicurezza multistrato: prepared statements, hash password, validazione server-side, transazioni
  • UI responsiva con animazioni e feedback visivo

Competenze sviluppate

  • Progettazione e implementazione di API REST da zero
  • Uso di PDO in modo sicuro con prepared statements (prevenzioni delle SQL injection)
  • Gestione delle sessioni PHP e dei meccanismi di autenticazione
  • Sviluppo frontend con JavaScript e AJAX (async/await, fetch)
  • Debug di applicazioni full-stack tramite DevTools e log
13 · Conclusioni

13. Conclusioni e sviluppi futuri

Possibili sviluppi futuri

  1. Esportazione del preventivo in PDF lato server con eventuale email automatica al paziente
  2. Gestione stati del preventivo completa con possibilità di aggiornarlo nella sezione admin
  3. Storico per utente: ogni utente può vedere i propri preventivi passati in una sezione "I miei preventivi"
  4. Registrazione utenti da interfaccia grafica (attualmente gli utenti vanno inseriti manualmente nel DB)
  5. Carrello con quantità > 1 per consentire la prenotazione di più esami uguali (dato che la struttura DB lo prevede già: campo qty)

Considerazione finale

Il progetto ha mostrato che la separazione netta tra livelli architetturali e il rispetto degli standard HTTP/REST non sono solo "buone pratiche teoriche", ma producono concretamente codice più semplice da estendere e debuggare. Ogni problema che è emerso ha avuto un'unica origine identificabile in uno solo dei tre livelli, e questo ha permesso di risolvere bug senza propagare patch in tutto il sistema.

Allegato

Schema della struttura dei file

lab-quote-system/
├── api/
│   ├── exams.php          # GET/PUT esami (admin per scritture)
│   ├── quotes.php         # POST preventivo (utente) + GET (admin)
│   ├── login.php          # POST autenticazione
│   └── logout.php         # GET distruzione sessione
│
├── config/
│   ├── database.php       # Connessione PDO singleton
│   ├── auth.php           # Funzioni login() / logout()
│   └── password.php       # Costanti credenziali DB
│
├── database/
│   ├── schema.sql         # CREATE TABLE per le 4 tabelle
│   ├── seed.sql           # Dati di esempio (esami, utente admin)
│   └── queries.sql        # Query di utilità
│
└── public/
    ├── index.html         # Pagina utente (catalogo + carrello)
    ├── login.html         # Form di login
    ├── admin.html         # Dashboard amministratore (3 sezioni)
    ├── assets/            # Logo e favicon
    ├── css/
    │   └── style.css      # Foglio di stile unificato
    └── js/
        ├── main.js        # Logica pagina utente
        └── admin.js       # Logica dashboard admin
Slide Finale

Grazie per l'attenzione

LabQuoteSystem — TPSIT 5° BINF · A.S. 2025/2026

QR code per aprire la demo LabQuoteSystem
Scansiona per aprire

Prova la demo online

Scansiona il QR code oppure apri il link

labquote.pietromozzoni.com

Pietro Mozzoni · 5B INF

1 / 1