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
https://laboratoriosia.altervista.org/3DSIA/informatica/eserciziphp/filemanager_didattico2/fileman/uploads/esercizio_consegne_leaflet.zip
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à.
Server Locale: Non puoi semplicemente fare doppio clic su questi file. Devi usare un server locale come XAMPP, MAMP o WAMP.
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.Crea i File Vuoti: All'interno della tua cartella
delivery_app, crea manualmente due file di testo vuoti:users.txtaddresses.txt
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
// 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
// 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
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
// (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
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;
?>
6. style.css
(Foglio di stile per rendere l'app utilizzabile)
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.zipAnalizziamo 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:
Il Backend (PHP): Il lavoro sporco.
Il Frontend (HTML): Lo scheletro della pagina.
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.
Legge il File: Il PHP apre il nostro "database" super-semplice, il file addresses.txt.
Crea una Lista: Legge ogni riga (es: utente1|Via Roma 10) e la divide in due parti: il nome utente e l'indirizzo.
Popola l'Array: Inserisce tutti i clienti e i loro indirizzi in un array PHP chiamato $clienti.
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:
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:
JavaScriptconst 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().
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:
Crea l'icona del camion (truckMarker).
Avvia una funzione (moveTruck) che sposta il truckMarker sulla coordinata successiva della rotta.
Aspetta 100 millisecondi (setTimeout).
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
Il PHP prepara la lista dei clienti leggendo addresses.txt.
L'HTML usa la lista PHP per costruire un menu a tendina.
Il corriere sceglie un cliente e preme "Mostra".
La pagina si ricarica con l'indirizzo nell'URL (es: ..._cliente=Via+Roma+10).
Il PHP (al secondo caricamento) vede l'URL e salva "Via Roma 10" in una variabile.
Il JavaScript legge quella variabile PHP e avvia la mappa.
Il JS chiede a Nominatim le coordinate, calcola il percorso e anima il camion fino alla destinazione.

Commenti
Posta un commento