4E e 4I: Creare un'applicazione in php di delivery che permette al corriere di selezionare in un file di testo il cliente con il suo indirizzo da un menu a tendina.

Guida Completa all'Applicazione di Delivery PHP

Questa guida contiene il codice per 6 file che devi creare in una singola cartella, più 2 file di testo vuoti.

Prerequisiti Fondamentali (Come Usare)

Prima di tutto, ecco i passaggi per far funzionare questo codice. Se salti questi punti, l'applicazione non funzionerà.

  1. Server Locale: Non puoi semplicemente fare doppio clic su questi file. Devi usare un server locale come XAMPP, MAMP o WAMP.

  2. Cartella di Progetto: Crea una cartella all'interno del tuo server (ad esempio, dentro xampp/htdocs/delivery_app). Metti tutti i file seguenti in quella cartella.

  3. Crea i File Vuoti: All'interno della tua cartella delivery_app, crea manualmente due file di testo vuoti:

    • users.txt

    • addresses.txt

  4. Permessi (Importante!): Il tuo server PHP ha bisogno del permesso di scrivere in questi file. Se usi Mac o Linux, apri un terminale, vai nella cartella del progetto e digita:

    chmod 666 users.txt
    chmod 666 addresses.txt
    

    (Su Windows con XAMPP, di solito non è necessario, ma se non scrive, è un problema di permessi).


1. register.php

(Pagina per la registrazione di nuovi utenti)

PHP
<?php
// Forza la visualizzazione degli errori (togli in produzione)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

session_start();
$error = '';

// Definisci il file degli utenti
define('USERS_FILE', 'users.txt');

// Funzione per controllare se l'utente esiste già
function user_exists($username) {
    if (!file_exists(USERS_FILE)) {
        return false;
    }
    $users = file(USERS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($users as $user_line) {
        // Separa la riga in base al primo carattere ':'
        list($stored_user, $stored_pass) = explode(':', $user_line, 2);
        if ($stored_user === $username) {
            return true;
        }
    }
    return false;
}

// Gestione del form quando viene inviato
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];
    $password_confirm = $_POST['password_confirm'];

    // 1. Controllo validazione
    if (empty($username) || empty($password)) {
        $error = 'Username e password sono obbligatori.';
    } elseif ($password !== $password_confirm) {
        $error = 'Le password non coincidono.';
    } elseif (user_exists($username)) {
        $error = 'Username già esistente. Scegline un altro.';
    } else {
        // 2. Criptare la password (con il metodo moderno e sicuro)
        // NON USARE crypt(), è obsoleto.
        $hashed_password = password_hash($password, PASSWORD_DEFAULT);
        
        // 3. Salvare l'utente nel file di testo
        // Formato: username:hashed_password
        $data_to_save = "$username:$hashed_password" . PHP_EOL;
        
        // file_put_contents con FILE_APPEND aggiunge dati senza cancellare il file
        if (file_put_contents(USERS_FILE, $data_to_save, FILE_APPEND | LOCK_EX) === false) {
            $error = 'Errore durante la registrazione. Controlla i permessi del file users.txt.';
        } else {
            // Registrazione avvenuta! Reindirizza al login
            header('Location: login.php?registered=1');
            exit;
        }
    }
}
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Registrazione</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h2>Registrati</h2>
        <?php if ($error): ?>
            <p class="error"><?php echo htmlspecialchars($error); ?></p>
        <?php endif; ?>
        
        <form action="register.php" method="POST">
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" required>
            
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" required>
            
            <label for="password_confirm">Conferma Password:</label>
            <input type="password" id="password_confirm" name="password_confirm" required>
            
            <button type="submit">Registrati</button>
        </form>
        <p>Hai già un account? <a href="login.php">Accedi</a></p>
    </div>
</body>
</html>

2. login.php

(Pagina per l'accesso degli utenti registrati)

PHP
<?php
// Forza la visualizzazione degli errori
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

session_start();
$error = '';
define('USERS_FILE', 'users.txt');

// Messaggio dopo la registrazione
$message = '';
if (isset($_GET['registered'])) {
    $message = 'Registrazione avvenuta con successo! Ora puoi accedere.';
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    if (empty($username) || empty($password)) {
        $error = 'Username e password sono obbligatori.';
    } else {
        // Cerca l'utente nel file
        $user_found = false;
        if (file_exists(USERS_FILE)) {
            $users = file(USERS_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            
            foreach ($users as $user_line) {
                list($stored_user, $hashed_password) = explode(':', $user_line, 2);
                
                if ($stored_user === $username) {
                    // Trovato! Ora verifica la password
                    if (password_verify($password, $hashed_password)) {
                        $user_found = true;
                        // Imposta la sessione
                        $_SESSION['username'] = $username;
                        header('Location: dashboard.php'); // L'utente va al suo pannello
                        exit;
                    }
                    break; // Trovato l'utente, ma password errata
                }
            }
        }
        
        if (!$user_found) {
            $error = 'Credenziali non valide.';
        }
    }
}
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h2>Login</h2>
        <?php if ($error): ?>
            <p class="error"><?php echo htmlspecialchars($error); ?></p>
        <?php endif; ?>
        <?php if ($message): ?>
            <p class="success"><?php echo htmlspecialchars($message); ?></p>
        <?php endif; ?>

        <form action="login.php" method="POST">
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" required>
            
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" required>
            
            <button type="submit">Accedi</button>
        </form>
        <p>Non hai un account? <a href="register.php">Registrati</a></p>
    </div>
</body>
</html>

3. dashboard.php

(Pannello dell'utente per inserire il proprio indirizzo)

PHP
<?php
session_start();

// Se l'utente non è loggato, reindirizza al login
if (!isset($_SESSION['username'])) {
    header('Location: login.php');
    exit;
}

$username = $_SESSION['username'];
define('ADDRESSES_FILE', 'addresses.txt');
$current_address = '';
$message = '';

// Funzione per leggere l'indirizzo corrente
function get_address($username) {
    if (!file_exists(ADDRESSES_FILE)) return '';
    $addresses = file(ADDRESSES_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($addresses as $line) {
        list($user, $address) = explode('|', $line, 2);
        if ($user === $username) {
            return $address;
        }
    }
    return '';
}

// Funzione per salvare l'indirizzo (complessa con file di testo)
function save_address($username, $new_address) {
    $lines = file_exists(ADDRESSES_FILE) ? file(ADDRESSES_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : [];
    $new_lines = [];
    $found = false;
    
    foreach ($lines as $line) {
        // Assicura che ci siano sempre 2 parti, anche se l'indirizzo è vuoto
        $parts = explode('|', $line, 2);
        if(count($parts) < 2) continue; // Salta righe malformate
        
        list($user, $address) = $parts;
        if ($user === $username) {
            $new_lines[] = "$username|$new_address";
            $found = true;
        } else {
            $new_lines[] = $line;
        }
    }
    
    if (!$found) {
        $new_lines[] = "$username|$new_address";
    }
    
    // Ricostruisce il file
    file_put_contents(ADDRESSES_FILE, implode(PHP_EOL, $new_lines), LOCK_EX);
}

// Gestione salvataggio indirizzo
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $address = trim($_POST['address']);
    if (!empty($address)) {
        save_address($username, $address);
        $message = 'Indirizzo salvato con successo!';
    } else {
         $message = 'Campo indirizzo vuoto.';
    }
}

// Carica l'indirizzo corrente da mostrare
$current_address = get_address($username);
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Dashboard</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <a href="logout.php" style="float: right;">Logout</a>
        <h2>Ciao, <?php echo htmlspecialchars($username); ?>!</h2>
        <p>Questo è il tuo pannello. Inserisci il tuo indirizzo di consegna.</p>

        <?php if ($message): ?>
            <p class="success"><?php echo htmlspecialchars($message); ?></p>
        <?php endif; ?>

        <form action="dashboard.php" method="POST">
            <label for="address">Il tuo Indirizzo:</label>
            <input type="text" id="address" name="address" value="<?php echo htmlspecialchars($current_address); ?>" placeholder="Es: Viale Pavia 28, Lodi" required>
            <button type="submit">Salva Indirizzo</button>
        </form>
    </div>
</body>
</html>

4. courier.php

(Il pannello del corriere con menu, mappa e animazione)

PHP
<?php
// (In un'app reale, anche questa pagina dovrebbe essere protetta da login)

define('ADDRESSES_FILE', 'addresses.txt');
$clienti = [];
$indirizzo_selezionato = '';

if (file_exists(ADDRESSES_FILE)) {
    $lines = file(ADDRESSES_FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        $parts = explode('|', $line, 2);
        if(count($parts) < 2) continue; // Salta righe malformate

        list($user, $address) = $parts;
        if (!empty($address)) { // Mostra solo utenti che hanno inserito un indirizzo
            $clienti[$user] = $address;
        }
    }
}

// Controlla se un cliente è stato selezionato (dal form)
if (isset($_GET['cliente']) && !empty($_GET['cliente'])) {
    $indirizzo_selezionato = $_GET['cliente'];
}
?>

<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <title>Pannello Corriere - Consegna</title>
    <link rel="stylesheet" href="style.css">
    
    <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.css" />
    
    <style>
        /* Stili della mappa (dalla tua versione) */
        #map { height: 400px; width: 100%; margin-top: 20px; border: 1px solid #ddd; }
        #consegna { display: none; font-size: 20px; color: green; margin-top: 10px; text-align: center; font-weight: bold;}
    </style>
</head>
<body>
    <div class="container">
        <h2>Pannello Corriere</h2>
        <p>Seleziona una consegna dal file dei clienti:</p>

        <?php if (empty($clienti)): ?>
            <p>Nessun cliente con indirizzo trovato.</p>
        <?php else: ?>
            <form action="courier.php" method="GET">
                <label for="cliente">Scegli il cliente:</label>
                <select id="cliente" name="cliente">
                    <option value="">-- Seleziona --</option>
                    <?php foreach ($clienti as $username => $indirizzo): ?>
                        <option value="<?php echo htmlspecialchars($indirizzo); ?>" 
                            <?php echo ($indirizzo === $indirizzo_selezionato) ? 'selected' : ''; ?>>
                            Cliente: <?php echo htmlspecialchars($username); ?> 
                            (<?php echo htmlspecialchars($indirizzo); ?>)
                        </option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Mostra Percorso</button>
            </form>
        <?php endif; ?>

        <div id="map"></div>
        <div id="consegna">✅ Consegna avvenuta!</div>
    </div> <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>

    <script>
        // Coordinate della paninoteca (partenza)
        // CAMBIA QUESTA COORDINATA CON LA TUA PARTENZA
        const partenza = [45.3167, 9.5000]; // Esempio: Lodi
        
        // Inizializza la mappa e il routing
        const map = L.map('map').setView(partenza, 13);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(map);
        
        // Inizializza il controllo di routing vuoto
        const routingControl = L.Routing.control({ 
            waypoints: [], 
            routeWhileDragging: false,
            addWaypoints: false, // Non permettere all'utente di aggiungere punti
            draggableWaypoints: false, // Non permettere di trascinare
            show: false // Nasconde il pannello del testo del percorso
        }).addTo(map);

        // Icona del camion
        const icon = L.icon({ iconUrl: 'https://img.icons8.com/ios-filled/50/000000/truck.png', iconSize: [32, 32] });
        const truckMarker = L.marker(partenza, { icon }).addTo(map);

        /**
         * Questa funzione avvia la geocodifica e l'animazione.
         */
        async function avviaConsegna(indirizzoDestinazione) {
            // Geocodifica dell'indirizzo
            const geoUrl = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(indirizzoDestinazione)}&format=json&addressdetails=1`;

            try {
                const response = await fetch(geoUrl);
                const data = await response.json();

                if (data.length === 0) {
                    alert('Indirizzo non trovato: ' + indirizzoDestinazione);
                    return;
                }

                const destinazione = [data[0].lat, data[0].lon];
                
                // Imposta i punti di partenza e arrivo sul controllo di routing
                routingControl.setWaypoints([
                    L.latLng(partenza[0], partenza[1]), 
                    L.latLng(destinazione[0], destinazione[1])
                ]);
                
                // Nascondi il marcatore statico
                truckMarker.setLatLng(partenza); // Resetta la posizione del camion

                // Ascolta l'evento 'routesfound' per avviare l'animazione
                routingControl.on('routesfound', function(e) {
                    const route = e.routes[0];
                    let index = 0;
                    
                    // Rimuovi l'ascoltatore per evitare animazioni multiple
                    routingControl.off('routesfound'); 

                    const moveTruck = () => {
                        if (index < route.coordinates.length) {
                            truckMarker.setLatLng(route.coordinates[index++]);
                            setTimeout(moveTruck, 100); // Velocità animazione
                        } else {
                            // Mostra il messaggio di consegna
                            document.getElementById('consegna').innerHTML = `✅ Consegna avvenuta a ${indirizzoDestinazione}!`;
                            document.getElementById('consegna').style.display = 'block';
                        }
                    };
                    
                    // Resetta lo stato della consegna
                    document.getElementById('consegna').style.display = 'none';
                    moveTruck(); // Avvia l'animazione
                });
            } catch (error) {
                alert('Si è verificato un errore durante la geocodifica. Riprova più tardi.');
            }
        }

        // --- IL TRIGGER ---
        // Questo è il codice che fa partire tutto.
        // Legge l'indirizzo selezionato che il PHP ha messo in una variabile JS.
        
        const indirizzoPHP = "<?php echo addslashes($indirizzo_selezionato); ?>";

        if (indirizzoPHP) {
            // Se un indirizzo è stato selezionato dal menu,
            // fai apparire la mappa (potrebbe essere nascosta)
            document.getElementById('map').style.display = 'block';
            
            // Avvia la funzione di consegna
            avviaConsegna(indirizzoPHP);
        } else {
            // Se nessun indirizzo è selezionato (prima visita), nascondi la mappa
            document.getElementById('map').style.display = 'none';
        }

    </script>
</body>
</html>

5. logout.php

(File semplice per distruggere la sessione)

PHP
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;
?>

6. style.css

(Foglio di stile per rendere l'app utilizzabile)

CSS
body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
    padding: 20px 0;
}

.container {
    background-color: #fff;
    padding: 2rem;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    width: 90%;
    max-width: 600px; /* Larghezza aumentata per la mappa */
}

h2 {
    text-align: center;
    color: #333;
    margin-top: 0;
}

form {
    display: flex;
    flex-direction: column;
}

label {
    margin-bottom: 5px;
    font-weight: bold;
    color: #555;
}

input[type="text"],
input[type="password"],
select {
    padding: 10px;
    margin-bottom: 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    width: 100%;
    box-sizing: border-box; /* Importante per il padding */
}

button {
    padding: 12px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
    transition: background-color 0.3s;
}

button:hover {
    background-color: #0056b3;
}

p {
    text-align: center;
}

.error {
    color: red;
    background-color: #ffebee;
    border: 1px solid red;
    padding: 10px;
    border-radius: 4px;
    text-align: center;
}

.success {
    color: green;
    background-color: #e8f5e9;
    border: 1px solid green;
    padding: 10px;
    border-radius: 4px;
    text-align: center;
}
Link della procedura:https://laboratoriosia.altervista.org/3DSIA/informatica/eserciziphp/filemanager_didattico2/fileman/uploads/esercizio_consegne_leaflet.zip

Analizziamo la pagina courier.php.

Questa pagina è il "pannello di controllo" del nostro corriere. È un ottimo esempio di come tre linguaggi (PHP, HTML e JavaScript) lavorano insieme per creare un'applicazione web dinamica.

Possiamo dividere la pagina in tre parti principali:

  1. Il Backend (PHP): Il lavoro sporco.

  2. Il Frontend (HTML): Lo scheletro della pagina.

  3. La Magia (JavaScript): Ciò che la rende interattiva.


1. Il Backend (PHP): Cosa c'è da consegnare?

[Immagine di PHP logo e un file di testo]

La prima cosa che succede, ancora prima che noi vediamo la pagina, è che il server esegue il codice PHP (all'inizio del file).

Il suo compito è uno solo: preparare la lista delle consegne.

  1. Legge il File: Il PHP apre il nostro "database" super-semplice, il file addresses.txt.

  2. Crea una Lista: Legge ogni riga (es: utente1|Via Roma 10) e la divide in due parti: il nome utente e l'indirizzo.

  3. Popola l'Array: Inserisce tutti i clienti e i loro indirizzi in un array PHP chiamato $clienti.

  4. Controlla la Scelta: Verifica se il corriere ha già scelto un cliente dall'URL (ad esempio, se l'URL è courier.php?cliente=Via+Roma+10). Se sì, salva l'indirizzo scelto nella variabile $indirizzo_selezionato.

Quando il PHP ha finito, ha in mano la lista di tutti i clienti pronti per essere mostrati nel menu.


2. Il Frontend (HTML): Cosa vede il corriere?

[Immagine di un modulo HTML con un menu a tendina]

L'HTML è la struttura della pagina. La parte più importante è il <form> (il modulo):

HTML
<form action="courier.php" method="GET">
    <label for="cliente">Scegli il cliente:</label>
    
    <select id="cliente" name="cliente">
        <?php foreach ($clienti as $username => $indirizzo): ?>
            <option value="<?php echo htmlspecialchars($indirizzo); ?>">
                Cliente: <?php echo htmlspecialchars($username); ?> 
            </option>
        <?php endforeach; ?>
    </select>
    
    <button type="submit">Mostra Percorso</button>
</form>

<div id="map"></div>
<div id="consegna"></div>

La cosa fondamentale da capire è l'unione tra PHP e HTML qui:

  • Stiamo usando un ciclo foreach di PHP dentro l'HTML per generare dinamicamente le <option> del menu a tendina. Per ogni cliente trovato nel file addresses.txt, il PHP scrive una riga nel menu.

  • Quando il corriere preme "Mostra Percorso", il form ricarica la pagina inviando l'indirizzo scelto nell'URL (grazie a method="GET").

  • Infine, abbiamo i due <div> vuoti (#map e #consegna) che JavaScript userà per disegnare la mappa e mostrare il messaggio di successo.


3. La Magia (JavaScript): L'autista virtuale

[Immagine di una mappa con un percorso e un'icona di un camion]

Questa è la parte più complessa e interessante. Si attiva dopo che la pagina è stata caricata nel browser del corriere.

Il JavaScript (in fondo alla pagina) fa tutto il lavoro pesante:

  1. Il "Trigger" (L'innesco):

    Il JS controlla per prima cosa se il PHP gli ha passato un indirizzo. Lo fa leggendo la variabile indirizzoPHP che abbiamo scritto nella pagina:

    JavaScript
    const indirizzoPHP = "<?php echo addslashes($indirizzo_selezionato); ?>";
    
    if (indirizzoPHP) {
        avviaConsegna(indirizzoPHP);
    }
    

    Se non c'è nessun indirizzo (è la prima volta che apriamo la pagina), non fa nulla e la mappa resta nascosta. Se c'è un indirizzo, chiama la funzione avviaConsegna().

  2. La Funzione avviaConsegna(indirizzo):

    Questa è la funzione principale che fa tutto:

    • Geocodifica (Nominatim): "JavaScript, cos'è 'Via Roma 10'?" Il JS non lo sa. Deve tradurre l'indirizzo in coordinate (latitudine e longitudine). Per farlo, usa fetch per "chiamare" un servizio esterno gratuito (Nominatim) e gli chiede: "Dammi le coordinate per questo indirizzo".

    • Disegna il Percorso (Leaflet Routing): Una volta ottenute le coordinate di destinazione, dice alla libreria Leaflet Routing Machine: "Ok, trova la strada migliore dalla nostra partenza (la paninoteca) a questa destinazione".

    • Animazione del Camion: Questa è la parte più "scenica". La libreria ci restituisce una "rotta" (un array di centinaia di piccole coordinate che formano il percorso). Il JS:

      1. Crea l'icona del camion (truckMarker).

      2. Avvia una funzione (moveTruck) che sposta il truckMarker sulla coordinata successiva della rotta.

      3. Aspetta 100 millisecondi (setTimeout).

      4. Si ripete (passo 2 e 3) fino a quando tutte le coordinate della rotta sono state percorse.

    • Consegna Avvenuta! Quando l'animazione finisce, il JS scrive "✅ Consegna avvenuta!" nel div #consegna e lo rende visibile.

Riassunto del Flusso

  1. Il PHP prepara la lista dei clienti leggendo addresses.txt.

  2. L'HTML usa la lista PHP per costruire un menu a tendina.

  3. Il corriere sceglie un cliente e preme "Mostra".

  4. La pagina si ricarica con l'indirizzo nell'URL (es: ..._cliente=Via+Roma+10).

  5. Il PHP (al secondo caricamento) vede l'URL e salva "Via Roma 10" in una variabile.

  6. Il JavaScript legge quella variabile PHP e avvia la mappa.

  7. Il JS chiede a Nominatim le coordinate, calcola il percorso e anima il camion fino alla destinazione.

Commenti

Post popolari in questo blog

Esercizi in Excel e fogli di Google

Le domande (e le risposte) all'orale di informatica esame di stato nei corsi: Sistemi Informativi aziendali (ex programmatori), Itis Informatica (ex Abacus), Liceo Tecnologico