Laboratorio di Sistemi Informativi
Inserimento dati con PHP
Supponiamo ora di voler creare una pagina per l'inserimento di nuovi
voli nella base di dati. La struttura generale sarà simile alla
pagina per l'interrogazione fatta finora: dovrà comparire una
form in cui si richiede all'utente di immettere i dati che si vogliono
inserire e poi, cliccando su un pulsante, il controllo passerà
allo script che esegue l'operazione di aggiornamento della base di dati.
Per effettuare un inserimento o una modifica su un database si usano
gli stessi comandi visti finora. Semplicemente la query che si
specifica con il comando mysql_query
sarà una INSERT (o
DELETE, o UPDATE) invece di una SELECT.
ATTENZIONE! Ricordatevi che il
risultato di mysql_query,
nel caso di una query di modifica, non è un oggetto risorsa,
come per le SELECT, ma un booleano: true nel caso di sucesso, false nel caso di insuccesso.
Possono essere utili anche la seguenti nuove funzioni:
int mysql_affected_rows([resource connessione])
: restituisce il numero di righe che sono state inserite (o cancellate, o modificate) dall'ultima query eseguita sulla connessione. Se l'ultima query è fallita, il risultato è -1.
int mysql_insert_id([resource connessione])
: se la query precedente era una query INSERT e nella tabella su cui è avvenuto l'inserimento era presente un campo di tipo AUTO_INCREMENT
, restituisce il valore generato automatica da MySQL per quel campo. Altrimenti restituisce sempre 0.
Come esempio di base di un sistema per l'inserimento di nuovi voli, consideriamo le due pagine inserimento.html e inserimento.php. Rispetto agli ultimi script analizzati, siamo tornati ad una soluzione con due file separati, uno per la FORM, l'altro per eseguire l'operazione di inserimento vero e proprio.
Da notare nello script inserimento.php
è il trattamento della
data. Si è voluto fare in modo che, quando l'utente non
inserisce nulla nel campo data della form, venga automaticamente
inserito nella base di dati la data e l'ora corrente. Per far
ciò si controlla se $_GET['data']
è la stringa vuota, nel qual caso si fa in modo che, come valore
per il campo ora nel
successivo comando INSERT, venga messa la chiama alla funzione
intrinseca NOW() di
MySQL. A parte questa particolarità, sia la pagine HTML che
quella in PHP sono banali.
Questa coppia di pagine ha un grosso difetto, a cui si è
già accennato in precedenza quando abbiamo analizzato i metodi
GET e POST. Se noi inseriamo un nuovo volo e poi ricarichiamo la pagina
inserimento.php, viene automaticamente reinserito un nuovo volo con gli
stessi dati!! Ovviamente questo non è il comportamento che
desideriamo. Si può risolvere in parte il problema trasformando
lo script in modo da usare il metodo POST per il passaggio dei
parametri: in questo modo, quando l'utente cerca di ricaricare la
pagina, viene avvertito che l'operazione potrebbe causare degli effetti
collaterali.
Esercizio 1
Modificare inserimento.html
e inserimento.php in
modo da utilizzare il
metodo POST invece del
metodo GET
Inserimento dati: struttura 3 fasi
La soluzione vista nella lezione precedente, che si ottiene utilizzando
il metodo POST anziché
il metodo GET, non è comunque ottimale. L'utente, infatti, non
può mettere la pagina di conferma in un bookmark: se si salva
inserimento.php in un
bookmark e si tenta successivamente di ritornare
a quella pagina, lo script PHP viene rieseguito e questo causa
l'inserimento di un nuovo volo. La soluzione migliore è quella
di adottare, per le modifiche alla base di dati, una struttura basata
su tre fasi:
- all'utente viene presentata la form di richiesta dati;
- una volta che l'utente ha sottomesso la form, i dati sono
inseriti;
- una volta avvenuto l'inserimento, lo script
richiama automaticamente un nuova pagina che si occupa di visualizzare
il messaggio di conferma all'utente.
In questo modo, quando l'utente clicca sul pulsante di sottomissione
della form, quello che gli viene
visualizzato alla fine è solo la pagina di conferma. Questa
pagina può essere tranquillamente inserita nei bookmark
perché l'unica cosa che fa è visualizzare un messaggio
che indica il successo dell'operazione, senza effettuare ulteriori
modifiche nella base di dati. Questa tecnica è implementata in inserimento2.html, inserimento2.php e inserimento2-conferma.php.
Notare, tra l'altro, che la pagina di conferma è stata
arricchita di informazioni sul volo effettivamente inserito.
È interessante esaminare come avviene il passaggio automatico
dall'esecuzione di inserimento2.php all'esecuzione
di
inserimento2-conferma.php.
Una spiegazione precisa richiederebbe lo
studio del protocollo HTTP, per cui diamo una spiegazione di massima di
quanto succede.
Il punto fondamentale è che quando un browser richiede al server una pagina web, il server risponde inviando, prima della pagina stessa, una serie di informazioni che costituiscono il cosidetto header
(intestazione). Esempi di informazioni che possono essere presenti nell'header sono il tipo di server Web o il formato usato per la codifica della pagina web (ASCII, Unicode, etc..).
Da PHP è possibile specificare degli header aggiuntivi da mandare al browser oltre a quelli standard. Questo è possibile tramite la funzione
- void header (string nuovoheader):
specifica di inviare come intestazioni, oltre ai dati di default, anche
la stringa nuovoheader.
Tra gli header più interessanti c'è sicuramente Location
. Specificando come header una stringa del tipo "Location: <nuovoURL>
" si dice al browser che in effetti la pagina corrente non contiene alcun dato, e che il browser deve automaticamente provvedere a caricare la pagina specificata da <nuovoURL>
. Così, basta indicare
header("Location: http://localhost/~studente/inserimento2-conferma.php")
alla fine di inserimento2.php
perché il browser venga automaticamente reindirizzato alla pagina di conferma. In effetti il comando presente in inserimento2.php
è più complesso per due motivi:
- si vuole passare a inserimento2-conferma.php
l'identificatore del
nuovo volo inserito, in modo che venga poi visualizzata un pagina di
conferma con tutti i dati del nuovo volo.
- si vuole fare in modo che sia possibile spostare a piacimento gli
script che formano la nostra applicazione senza dover modificare il
sorgente. Se noi inserissimo direttamente l'URL
http://localhost/~studente/inserimento2-conferma.php
nel codice
sorgente e poi decidessimo di spostare tutte l'applicazione relativa al
database airdb dentro la cartella ~studente/public_html/airdb,
occorrerebbe modificare l'URL presente nel codice con
http://localhost/~studente/airdb/inserimento2-conferma.php.
Stesso
problema se la nostra macchina venisse messa in rete con un
nome proprio e quindi, invece di chiamarsi localhost, prendesse il nome di
pincopallino.unich.it.
Per risolvere il primo problema, si utilizza la funzione mysql_insert_id() e si passa il
parametro id nella URL.
Per risolvere il secondo problema, si utilizza la variabile predefinita
$_SERVER['HTTP_HOST'] che
contiene il nome della macchina su cui sta lo script e $_SERVER['PHP_SELF'] che
contiene l'indirizzo della pagina in esecuzione, all'interno della
macchina. Nel nostro caso $_SERVER['HTTP_HOST']
contiene localhost e $_SERVER['PHP_SELF']
contiene /~studente/inserimento2.php.
Quello che dobbiamo fare è estrarre, da $_SERVER['PHP_SELF'], il
percorso completo della pagina con l'eccezione dell'ultima componente (inserimento2.php) perchè
a quest'ultima dobbiamo sostituire il nome della pagina di conferma da
richiamare (inserimento2-conferma.php).
A questo provvede la funzione dirname:
- string dirname (string percorso_file): dato
un percorso ad un file, restituisce lo stesso percorso ma senza il nome
del file finale (quindi soltanto l'elenco delle directory attraversate).
Dunque, il comando da dare è
header("Location:
http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF'])
."/inserimento2-conferma.php?id=$id");
dove la variabile $id
deve contenere l'identificatore del nuovo volo inserito, ottenuto dalla
funzione mysql_insert_id().
Posizione del comando header
Nella maggior parte delle distribuzioni, perché la funzione header abbia effetto, occorre che nessun altro output sia stato inviato al browser. Ad esempio nella pagina
prova
<?php header('headerprova');?>
il comando header
non funziona (e genera un errore) perché viene chiamato dopo che è stato generata come output la stringa prova. Al contrario la pagina seguente funziona:
<?php header('headerprova');?>
prova
Nella Fedora 4, invece, è stata abilitata la funzione di bufferizzazione dell'output che nasconde questo problema. Vuol dire che l'output non viene mandato immediatamente al browser, ma viene immagazzinato internamente da PHP e inviato solo quando l'esecuzione della pagina è terminata. In questo modo, è possibile dare il comando header
anche quando è stato già prodotto dell'output. Siccome questo comportamento non è standard in altri ambienti, è preferibile non abituarsi.
È possibile disabilitare la bufferizzazione aggiungendo al file .htaccess
la seguente linea:
php_value output_buffering 0
Esercizio 2
Utilizzando la funzione header(),
scrivere una funzione redirect_browser()
che accetta come parametro un percorso relativo e redirige il
browser alla pagina ivi indicata. In pratica, alla fine deve essere
possibile dare il comando
redirect_browser("inserimento2-conferma.php?id=$id");
invece di
header("Location:
http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF'])
."/inserimento2-conferma.php?id=$id");
Esercizio 3
Inglobare inserimento2.html,
inserimento2.php e
inserimento2-conferma.php
in un unico file, come già fatto per
form-totale.php.
Esercizio 4
L'interfaccia utente utilizzata finora è molto spartana. In un
sistema vero
occorrerebbe fornire una form per l'immissione dati più
amichevole, simile a quella usata per la richiesta dei voli dati gli
aeroporti di partenza e arrivo, in cui per ogni campo vi è un
insieme ben definito di valori possibili tra cui scegliere. Come
esercizio, fare le modifiche appropriate.
Nota. Fornire una
interfaccia utente più ricca ha anche un altro effetto
collaterale: quello di rispettare i vincoli di integrità tra i
dati. Con gli script visti sopra, è possibile inserire un volo
che abbia aeroporti di partenza o di arrivo sconosciuti o con un
idaereo
inesistente. Questi vincoli di integrità possono anche essere
forzati a livello di DBMS (anche se non abbiamo visto come) ma è
spesso più efficiente forzarli a livello dell'applicazione
impedendo che l'utente possa inserire dati sbagliati.
I file utilizzati in questa lezione: airdb2.sql, error.php, e .htaccess