Il sito per imparare gratuitamente a fare pagine Web

Corso PHP














Questa guida non è stampabile per volontà del suo stesso autore Claudio Curci.



Guida PHP scritta da Claudio Curci - Aggiornamenti di Max Kiusso

lezione 11: Iscriviamo i nostri utenti!

Sommario lezione

. Progettiamo l'applicazione
. La base dati
. Il modulo html (form)
. Il controllo sui dati immessi
. Finalmente, l'iscrizione!



Progettiamo l'applicazione

Siamo finalmente arrivati alla parte del corso dedicata alla gestione di utenti registrati. Vedremo le procedure di iscrizione (dalla form alla base dati), registrazione e login degli utenti. L'argomento è vasto e sarà "spalmato" su più lezioni.
Iniziamo subito progettando la struttura dell'applicazione.

Prima questione da analizzare: i dati che vogliamo registrare. Quali campi ci occorrono? Quali informazioni deve lasciarci l'utente per iscriversi al sito? Quali saranno obbligatorie e quali facoltative? Ciascuno di voi avrà esigenze diverse, per cui adotteremo qualche trucco per rendere agevole la personalizzazione dell'applicazione.

Poniamo, come esempio per la nostra lezione, il caso di un portale che richieda agli utenti la registrazione, per accedere a qualche servizio riservato.
I campi richiesti come obbligatori saranno:

. username, che dovrà essere al massimo di 12 caratteri (un limite ci consente di migliorare le prestazioni e le dimensioni del database) e diverso per ogni utente;

. password, anche qui imponiamo un massimo di 12 caratteri;

. campi per informazioni anagrafiche stringa (nome e cognome dell'utente, città di residenza e indirizzo);

. campi per informazioni anagrafiche scelte da un menù a tendina (la provincia di appartenenza: );

. campi per informazioni anagrafiche formattate secondo uno schema preciso (data di nascita);

. campi registro (data registrazione, data ultima modifica);

. campo per l'indirizzo email

Partendo da questa base e considerando le precedenti lezioni sulle form, potete gestire qualsiasi servizio di registrazione.
Decidiamo adesso quali campi vogliamo come obbligatori e quali facoltativi. Arbitrariamente, scegliamo come necessari i campi di login (username e password) e l'email. Tutti gli altri non saranno obbligatori. E' una scelta sensata visto che gli utenti italiani sono particolarmente restii nel compilare le form.


La base dati

Prendiamo il nostro tool preferito per la gestione di mysql (il dos, phpMyAdmin, Mysql Control Center ecc.) e creiamo la base dati.
Abbiamo svolto gran parte del lavoro prima, dobbiamo solo scegliere i nomi dei campi con i rispettivi tipi e dimensioni. Non è rilevante, ai fini della progettazione del database, sapere se un campo è obbligatorio o meno. Sarà' compito di PHP eseguire i controlli e sincerarsi della corretta compilazione delle informazioni.
Vi consiglio, sulla base di quanto appreso finora, di progettare il vostro database e di confrontare con quanto segue le vostre soluzioni.

Nome campoTipo di datoDimensione
id (contatore, auto_increment, chiave primaria)int11
usernamevarchar12
passwordvarchar12
nomevarchar30
cognomevarchar30
indirizzovarchar200
citta (evitate la "à" accentata!)varchar100
provinciaint11
indirizzo_emailvarchar100
data_nascitadate-
data_registrazionedatetime-
data_ultima_modificatimestamp-

Come avevate progettato la vostra base dati?
Per i campi testuali non credo ci sia bisogno di commenti, i valori inseriti come dimensione massima sono arbitrari (tranne per username e password che devono arrivare almeno ai 12 caratteri stabiliti come lunghezza minima in fase di progettazione).
Penso che 30 caratteri, ad esempio, siano più che sufficienti per contenere campi come il nome o il cognome di una persona ma se prevedete utenti stranieri (magari brasiliani) aumentate pure la dimensione del campo. Non esagerate per non sprecare spazio, ma con le dimensioni degli hard disk di oggi non dovete farvi prendere la mania dei campi precisi... all'ultimo carattere. Certo sarebbe stato uno spreco di risorse usare un char di 255 caratteri (che occupa comunque 255 byte, sia per memorizzare "ciao" che "una frase lunghissima come questa") per il nome.

Il campo provincia è invece.. numerico. Quando avete a che fare con un range di valori ben definito (come l'elenco delle provincie italiane), potete memorizzare i nomi effettivi in una tabella a parte. La prima regola dei database relazionali è quella di evitare la ridondanza delle informazioni. Pensate, ad esempio, di dover gestire un portale di Roma. Su 100 utenti, probabilmente 90 registrati inseriranno come provincia di residenza "Roma". Sarebbe uno spreco di risorse obbligare il computer a memorizzare tante volte la stessa informazione. Ne abbiamo parlato ampiamente nella lezione 5, quindi vi invito a rileggerla.
Alla base di questo discorso e della tecnica di progettazione dei database relazionali, creiamo quindi un'altra tabella "province" in cui inserire i nomi di tutte le provincie italiane (aggiungendone magari un'altra, "estera/altro"). La struttura è semplice:

id (contatore)
nome_provincia (varchar 100)

Nella tabella "utenti" il campo "provincia" sarà occupato quindi non dai caratteri che compongono il nome, ma dall'id relativo ricavato dall'altra tabella.

Ultima nota, il campo "data_ultima_modifica" è di tipo "timestamp". Questo campo viene automaticamente impostato da mysql alla data/ora odierna ogni volta che viene apportata una modifica al record. Non dovremo quindi occuparci di impostarlo nelle nostre query perchè è automatico. Quando avrete a che fare con base dati progettate da altri o scaricate da internet, troverete probabilmente questo campo chiamato convenzionalmente "ts".


Il modulo html (form)

Una volta creata la base dati, passiamo al codice html del modulo di iscrizione. Intanto decidiamo il tipo di campo per ciascun input:

Dato richiestoTipo campo formNote
usernametextimpostiamo l'attributo MAXLENGTH a 12, cosi da evitare valori maggiori della dimensione del campo nel db
passwordpasswordimpostiamo l'attributo MAXLENGTH a 12
nometextimpostiamo l'attributo MAXLENGTH a 30
cognometextimpostiamo l'attributo MAXLENGTH a 30
indirizzotextimpostiamo l'attributo MAXLENGTH a 200
cittatextimpostiamo l'attributo MAXLENGTH a 100
provinciaselectricorriamo ad un campo select (menù a tendina), il "VALUE" di ciascun valore sarà l'indice numerico ID della tabella "province"
indirizzo_emailtextimpostiamo l'attributo MAXLENGTH a 100
data_nascitaselectutilizziamo tre select (menù a tendina): una per il giorno, una per il mese, una per l'anno. L'utente è cosi agevolato nell'inserimento della data. Con PHP poi "incolleremo" i tre valori immessi ed avremo la stringa adatta al db

Se qualcosa ancora non vi è chiaro (magari il meccanismo dei menù a tendina), continuate a leggere: vedendo l'esempio pratico tutto sarà più semplice.
Prima di scrivere il codice html della form, dobbiamo iniziare a preoccuparci di un altro aspetto. Abbiamo infatti deciso di richiedere come obbligatori i campi username, password e indirizzo_email. La verifica dei dati immessi sarà compito della pagina php che elaborerà la form. Basterà controllare che i campi chiamati "username", "password" ed "indirizzo_email" abbiano un valore diverso dalla stringa vuota "".
Questo approccio funziona ma voglio insegnarvi un piccolo "trucchetto" che vi permetterà di gestire agevolmente qualsiasi tipo di form.
Immaginate di dover rendere obbligatori anche altri campi, come nome/cognome o magari di doverne aggiungere altri.
Se nella pagina che elabora i dati andate a controllare i campi immessi ad uno ad uno, ogni volta dovete riscrivere il codice ed aggiungere anche il nome del nuovo campo. Immaginate per un attimo di essere addetti alla reception di una conferenza. Immaginate di dover consentire l'accesso alla sala soltanto a chi si è iscritto ed ha pagato la quota. Prima dell'apertura, l'organizzatore vi chiama e vi dice "devi far entrare solo chi si chiama Anna Caffarelli, Bice Alice, Luigi Verdi, Aldo Cancelli.... ".. Pazzesco vero? Tutto più facile se chi ha pagato la quota ha ricevuto anche un tesserino da appendere alla giacca. Basta dare un'occhiata ed ecco che la persona è autorizzata ad entrare in sala.
Perchè non adottare questo approccio anche con PHP? Sappiamo come scorrere tutti i campi di una form (con un ciclo while sull'array $_POST, ricordate?). Il problema è riuscire a "mettere un tesserino" sui campi obbligatori.. se ci riuscissimo, PHP farebbe passare tutti i "non tesserati", mentre "perquisirebbe" gli altri verificando che contengano un valore. Come possiamo "tesserare" i campi obbligatori? Come al solito, pensateci e se non riuscite.. continuate a leggere.

Ciascun campo della form è identificato dall'attributo NAME. Possiamo sfruttare proprio il nome di un campo per far capire a PHP se è obbligatorio o meno. Basta far precedere i campi obbligatori da una stringa particolare (io di solito uso "ob_nomecampo"). A quel punto, basterà dire a PHP "se le prime 3 lettere del campo sono 'ob_', vuol dire che è obbligatorio: verifica che sia stato compilato". Nel dettaglio, vedremo questo codice più avanti, quando scriveremo il file di elabora_form. Per ora però bisogna tenere in considerazione il nome dei campi obbligatori. Il campo "username" non si chiamerà più cosi, ma "ob_username". Cosi via per password ed indirizzo_email.

Abbiamo visto prima che il campo "provincia" conterrà l'ID preso dalla tabella "province" (nel gergo dei database relazionali, queste tabelle di supporto vengono chiamate "lookup table").
Prima di creare il file della form, occorre riempire la tabella "province". Operazione un pò lunghetta :) ma necessaria. Vedrò di postare sul forum la query di inserimento di tutte le provincie italiane, cosi potete lanciarla da phpMyAdmin e riempire la tabella in pochi secondi. Per ora nulla vi vieta di riempirla con dati di prova (come ho fatto io per questo esempio).
La form dovrà contenere i dati della tabella, che ovviamente vi sconsiglio di ricopiare a mano (anche perchè se cambiate qualcosa dovete poi intervenire manualmente...). Possiamo eseguire una normalissima query e compilare cosi dinamicamente la nostra select html:

$stringa_query="select * from province";

Abbiamo altre tre select html (forse è meglio chiamarle "comboBox", altrimenti si crea confusione con le select SQL..) al termine della form, per l'inserimento della data di nascita. Possiamo scriverle a manina (creando 31 option per il campo "giorno", 12 per "mese" e un centinaio per "anno")... ma PHP è cosi bravo da aiutarci anche in questo caso con qualche ciclo for. Questo è il codice della pagina "form.php" (dato che contiene script, occhio all'estensione):

<?php
$connessione=mysql_connect("localhost", "root", "") or die(mysql_error());
$selezione_db=mysql_select_db("iscrizione") or die(msyql_error());
?>
<doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Iscriviti al mio sito!</title>
</head>
<body>
Iscriviti al mio sito, cosi potrai accedere alle aree riservate. La procedura è semplice: compila il modulo sottostante. Ti ricordo che i campi segnati con l'asterisco (*) sono obbligatori.
<form name="modulo" action="elabora_form.php" method="post">
<table>
<tr><td>Scegli un username*:</td><td><input type="text" name="ob_username" size="12" maxlength="12"></td></tr>
<tr><td>Scegli una password*:</td><td><input type="password" name="ob_password" size="12" maxlength="12"></td></tr>
<tr><td>Nome:</td><td><input type="text" name="nome" size="20" maxlength="30"></td></tr>
<tr><td>Cognome:</td><td><input type="text" name="cognome" size="20" maxlength="30"></td></tr>
<tr><td>Indirizzo:</td><td><input type="text" name="indirizzo" size="40" maxlength="200"></td></tr>
<tr><td>Città:</td><td><input type="text" name="citta" size="40" maxlength="100"></td></tr>
<tr><td>Provincia:</td><td><select name="provincia">
<?php
$stringa_query="select * from province";
$elenco_province=mysql_query($stringa_query) or die(mysql_error());
while($row_province=mysql_fetch_array($elenco_province)){
echo "<option value=\"" . $row_province['id'] . "\">" . $row_province['nome_provincia]' . "</option>\n";
}
?>
</select></td></tr>
<tr><td>Email*:</td><td><input type="text" name="ob_indirizzo_email" size="40" maxlength="100"></td></tr>
<tr><td>Data di nascita:</td><td>
<select name="giorno">
<?php
for($i=1;$i<32;$i++){
echo "<option value=\"" . $i . "\">" . $i . "</option>\n";
}
?>
</select> 
<select name="mese">
<?php
for($i=1;$i<13;$i++){
echo "<option value=\"" . $i . "\">" . $i . "</option>\n";
}
?>
</select> 
<select name="anno">
<?php
for($i=1920;$i<2012;$i++){
echo "<option value=\"" . $i . "\">" . $i . "</option>\n";
}
?>
</select>
</td></tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="iscriviti!" name="invio">
</tr>
</table>
</form>
</body>
</html>

Finora abbiamo utilizzato i cicli for partendo da $i=0 oppure $i=1, ma ovviamente possiamo inizializzare il contatore con altri valori (come 1920 per la data di nascita).


Il controllo sui dati immessi

Creiamo adesso il file elabora_form.php. Premetto che, per facilitare il discorso, utilizzerò un approccio semplice e non proprio elegantissimo. Una vera gestione dei moduli si avvale di funzioni ed oggetti. Quella che andremo però a vedere è una soluzione funzionante, da cui chi vorrà potrà partire per approfondire il discorso e creare codice più "professionale". Fermo restando che, in termini di prestazioni e risultato finale, il file "elabora_form.php" non compromette nulla. E' solo questione di stile e tecnica di programmazione, ma come sapete lo scopo del corso è di rendervi in grado di realizzare qualcosa di utile per le vostre pagine web, non di insegnare a programmare.

La prima operazione da svolgere è ovviamente la verifica dei dati immessi. Se i campi obbligatori sono stati compilati, procederemo all'iscrizione dell'utente. In caso contrario, restituiremo a video un messaggio di errore.
Abbiamo già accennato prima, con l'esempio della conferenza, al "distintivo" con cui contrassegnare i campi obbligatori. Con un ciclo while scorriamo l'intero array $_POST contenente i dati inviati dal modulo, e se le prime tre lettere di un campo sono "ob_", verifichiamo che abbia inserito almeno un carattere.
Possiamo scorrere i dati inviati con questo semplice ciclo, già visto altre volte:

<?php
while(list($chiave, $valore)=each($_POST)){
echo "per il campo " . $chiave . " hai inserito " . $valore . "<br>";
}
?>

$chiave memorizza il nome del campo, mentre $valore il valore inserito. Se le prime tre lettere di $chiave sono "ob_", dobbiamo controllare quanto inserito. Come fa PHP ad accorgersi del valore delle prime tre lettere di una stringa? Una delle funzioni predefinite del linguaggio, substr() prende tre parametri: una stringa e i due limiti di caratteri da restituire. Provate questo codice, che stampa solo i valori inseriti nei campi obbligatori:

<?php
while(list($chiave, $valore)=each($_POST)){
if(substr($chiave,0,3)=="ob_"){
echo "nel campo obbligatorio " . $chiave . " hai inserito il valore " . $valore . "<br>";
}
}
?>

Con questo ciclo possiamo controllare (verificando $valore) se l'utente ha compilato i campi obbligatori. Se ha mancato di compilarne uno, è bene usare l'istruzione break per evitare di fargli controllare inutilmente gli altri. Come facciamo poi a collegare il risultato del ciclo con il resto dello script? Dopo il while seguiranno le istruzioni di connessione al database ed iscrizione utente.. se non ha compilato uno dei campi obbligatori, bisogna "avvertire" PHP di non eseguire il resto del codice.

A questo scopo può tornarci utile l'uso di un "flag", una variabile temporanea (come quella che usavamo nelle prime lezioni per stampare una tabella con colori alternati). Inizializziamo la variabile a zero, prima del ciclo. Se viene rilevato un campo non compilato, il valore del flag diventa 1. Alla fine del ciclo, verifichiamo di nuovo il valore: se è rimasto pari a zero, vuol dire che durante il ciclo non sono stati rilevati campi obbligatori vuoti, e quindi possiamo precedere all'iscrizione.

<?php
$flag_controllo=0;
while(list($chiave, $valore)=each($_POST)){
if(substr($chiave,0,3)=="ob_"){
if($valore ==""){
echo "non hai compilato il campo obbligatorio " . $chiave . ", l'iscrizione non può essere processata.";
$flag_controllo=1;
break;
}
}
}
if($flag_controllo==0){
//procede all'iscrizione
}

?>

Prima di continuare con lo script di iscrizione utente, una precisazione. Molti di voi avranno già utilizzato, per controllare i dati immessi via form, il javascript. A differenza di PHP, javascript è eseguito lato utente e vi consente quindi di non dover ricaricare la form o una seconda pagina di risultato (come in questo esempio). L'utente che non ha compilato campi obbligatori, nel nostro caso, deve tornare alla pagina precedente e ricominciare tutto da capo.
In realtà è possibile con PHP eseguire i controlli ricaricando la pagina, ma facendo in modo che i risultati inviati rimangano memorizzati. E' una tecnica più complicata, per cui non ne ho parlato per evitare confusione.

Semmai torneremo in futuro su un argomento cosi vasto (sapete che attraverso i moduli html è possibile sferrare un attacco ad un sito con la cosidetta tecnica del "SQL INJECTION"? Con rischi del genere, tutti i campi della form vanno controllati in modo ben più "pesante" del semplice confronto con una stringa vuota...). In ogni caso, ricordate che i controlli javascript hanno l'unico scopo di evitare all'utente il fastidio di dover ricaricare la pagina. Per chi vuole intenzionalmente iscriversi senza compilare campi obbligatori, bastano due click di mouse per disabilitare l'interprete del browser e proseguire senza problemi. Sotto questo punto di vista sono controlli inutili (come gli script che disabilitano il tasto destro del mouse...).


Finalmente, l'iscrizione!

Arriva finalmente il momento di iscrivere l'utente. Lo script è molto semplice, una normale query. Ricordate di usare il carattere di "\" escape davanti ai campi stringa. Il campo data_registrazione è impostato alla data/ora corrente con la funzione mysql now(), mentre data_ultima_modifica non va impostato perchè, essendo timestamp, è automatico. Stesso discorso ovviamente per il campo ID, chiave primaria contatore.
Per la data di nascita (l'input avveniva da tre combo box ricordate?) basta "incollarle" con l'operatore ".", come nel codice qui sotto (il separatore "/" serve a mysql per distinguere anno/mese/giorno):

<?php
$connessione=mysql_connect("localhost","root","") or die(mysql_error());
$scelta_db=mysql_select_db("iscrizione") or die(mysql_error());
?>
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>iscrizione in corso...</title>
</head>
<body>
<?php
$flag_controllo=0;
while(list($chiave, $valore)=each($_POST)){
if(substr($chiave,0,3)=="ob_"){
if($valore ==""){
echo "non hai compilato il campo obbligatorio " . $chiave . ", l'iscrizione non può essere processata.";
$flag_controllo=1;
break;
}
}
}
if($flag_controllo==0){
//formatta la data di nascita
$data_nascita = $_POST['anno'] ."/". $_POST['mese'] ."/". $_POST['giorno'];
echo "la data di nascita è " . $data_nascita;
$stringa_query="insert into utenti (username,password,nome,cognome,indirizzo,citta,provincia,indirizzo_email,data_nascita, data_registrazione) values('" . $_POST['ob_username'] . "','" . $_POST['ob_password'] . "','" . addslashes( $_POST['nome'] . "', '" . addslashes( $_POST['cognome']) . "','" . addslashes( $_POST['indirizzo']) . "','" . addslashes( $_POST['citta'] ) . "'," . $_POST['provincia'] . ", '" . $_POST['ob_indirizzo_email'] . "','" . $data_nascita . "',now())";
$iscrizione_utente=mysql_query($stringa_query) or die(mysql_error());
}
?>
</body>
</html>

Termino qui la lezione lasciandovi però un compitino (visto che nel forum più di qualcuno li ha chiesti..).
Lo script realizzato ha una carenza non indifferente: permette l'iscrizione a più utenti con lo stesso username.
Con mysql_num_rows() e con il $flag_controllo, non è difficile rimediare con poche righe di codice.. buon lavoro :)

Scrivete pure sul forum se avete bisogno di ulteriori chiarimenti!



Se avete domande potete scrivere sul forum di supporto, gratuito e aperto a tutti.



Claudio Curci e Max Kiusso per Web-Link.it