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 8: Un guestbook in PHP

Sommario lezione

. Strutturiamo un database per il nostro guestbook
. L'interfaccia utente e le pagine di elaborazione
. Miglioriamo l'interfaccia




Strutturiamo un database per il nostro guestbook

Tenetevi forte perchè in questa lezione creeremo la nostra prima, vera, applicazione in PHP: un guestbook!
Approfitteremo dell'idea lanciata da un utente nel forum per illustrare anche le tecniche e le istruzioni di connessione ad un database Mysql attraverso PHP.

Prima di iniziare però vi ricordo che è difficile trovare un hosting gratuito che metta a disposizione un database mysql. Se volete potete creare un guestbook in locale sul vostro computer, ma ovviamente non potete farlo interagire con il vostro sito. Se qualche utente trova in giro per la rete un servizio di hosting gratuito che consenta di disporre anche di un database mysql, non manchi di segnalarcelo.

Utilizzerò per questa guida un approccio semplice, un "passo-passo". Ho anche cambiato lievemente l'impostazione grafica perchè il codice contenuto nelle textarea non mi piaceva (tra l'altro obbliga il browser alla fastidiosa scrollbar orizzontale).
Per prima cosa, creiamo un database. Avete già creato, nelle precedenti lezioni, il database "corso_php" (grazie a PhpMyAdmin o a MySqlControlCenter). Sul vostro server locale potete creare quanti database volete, online no (a meno che non abbiate un contratto di housing..).

Quindi, se poi volete trasferire tutto online, rischiate di occupare spazio inutilmente con le tabelle dei test fatti nelle altre lezioni. Creiamo allora un nuovo database, chiamandolo "guestbook".
Utilizziamo phpMyAdmin:

- lanciate EasyPhp/XAMPP

- puntate il browser sull'indirizzo di phpMyAdmin

- digitate il nome del nuovo database nell'apposito spazio (abbiamo deciso di chiamarlo "guestbook" ma il nome è arbitrario, tra l'altro online viene di solito deciso dal provider)




Cliccate su "crea". Il db viene creato e phpMyAdmin restituisce una nuova pagina contenente il messaggio "non ci sono tabelle nel database". Questo è proprio quello che andremo a fare tra poco, sempre con phpMyAdmin (o MCC, l'interfaccia stile Windows vi consente di eseguire le stesse operazioni senza problemi).

A questo punto dobbiamo però staccarci dal monitor e prendere carta e penna per progettare il database. E' un'operazione fondamentale. Analizziamo l'applicazione che vogliamo creare, partendo dalla.. fine.
Il risultato che vogliamo è questo:

-una pagina html con un form per l'inserimento di messaggi dagli utenti (simile a quella realizzata la scorsa volta per l'invio di email ricordate?)

-una pagina php che inserisca i dati inviati dalla form in un database (altrimenti ce li perderemmo per strada, non avendo un supporto per memorizzarli..) e restituisca un messaggio all'utente (il classico "grazie per aver inserito un messaggio")

-una pagina php che estragga dal database l'elenco di tutti i messaggi inseriti, in ordine cronologico, e li mostri a video

Un'applicazione del genere necessita di un'unica tabella, quindi la fase di analisi e progettazione è breve. Prendete però fin da ora l'abitudine di sedervi a tavolino e studiare PRIMA di eseguire. Come dice la regola delle 11P, Prima Pensa Poi Programma Perché Programmi Poco Pensati Portano Pesanti Problemi.

La tabella deve contenere pertanto:
- un campo che memorizzi i nomi degli utenti che scrivono nel guestbook;
- un campo che memorizzi il messaggio inserito;
- un campo che memorizzi la data e l'ora di inserimento

Come step successivo, decidiamo il nome dei campi. E' bene evitare caratteri strani (niente spazi, niente accenti, niente punteggiatura, niente numeri):
- campo "nome_utente";
- campo "messaggio";
- campo "data_ora"

Adesso decidiamo (stiamo lavorando ancora sul foglietto di carta) il tipo di campo e la lunghezza.

- "nome_utente" e "messaggio" sono campi testuali. Per risparmiare memoria e migliorare le prestazioni, è bene evitare dimensioni esagerate. Sarebbe assurdo comprare un terreno di 2 ettari per piantarci qualche pianta di pomodoro.
Il campo "nome_utente" contiene una stringa di lunghezza variabile, comunque contenuta. Difficilmente un utente inserirebbe un "nome/cognome" o un "alias" superiore ai 40 caratteri.
Diverso il discorso per il messaggio. Può contenere un semplice "ciao" come un testo lungo. Anche qui però difficilmente un guest ("visitatore") del nostro sito può aver interesse a scrivere migliaia di caratteri.
MySql offre diversi tipi di campo per la memorizzazione di testi:

- campi di tipo CHAR e VARCHAR, che possono contenere fino a 255 caratteri;

- campi di tipo TEXT (TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT) per testi lunghi.

Nel primo caso, "nome_utente", possiamo tranquillamente utilizzare un campo del primo tipo, CHAR o VARCHAR. Per "messaggio" è meglio ricorrere ad un TEXT (i 255 caratteri di un CHAR sono tanti per un nome/cognome ma pochi per un testo). Un TEXT contiene fino a 65535 caratteri.

Che differenza passa tra un campo di tipo CHAR ed uno VARCHAR? Il primo campo ha lunghezza fissa.
Se diamo a Mysql istruzione di creare un campo CHAR di lunghezza 22 -la sintassi è: CHAR(22)- ogni record occuperà 22 caratteri. Se inseriamo in un campo del genere la parola "lampadina", Mysql scriverà nei primi 9 caratteri il testo, e negli altri 13 inserirà degli spazi bianchi. Un campo CHAR di lunghezza 22 occuperà quindi sempre 22 bytes.
Se dichiariamo un campo VARCHAR(22), e inseriamo la parola "lampadina", Mysql scriverà nei primi 9 caratteri il testo, nel decimo memorizzerà la lunghezza (ovvero il numero 9) e non utilizzerà gli altri 21 bytes.
A prima vista, il campo VARCHAR offre una migliore gestione della memoria. Memorizza soltanto i caratteri effettivamente utilizzati, più uno aggiuntivo per ricordare il numero di byte da occupare.
In un campo di lunghezza ben definita (come un codice fiscale, sempre 16 caratteri), è bene però utilizzare il campo CHAR(16): siamo sicuri che ogni record occuperà tutti i byte, senza sprechi.
In un campo VARCHAR(16) i primi 16 byte sarebbero occupati dai caratteri,il 17esimo da un numero per memorizzare la lunghezza. Occuperemmo quindi un byte in più rispetto al CHAR.
La regola è questa: usiamo VARCHAR quando abbiamo a che fare con stringhe variabili, CHAR quando dobbiamo memorizzare testi di lunghezza sempre uguale.
Nel nostro caso, visto che il "nome utente" può essere "aldo" come "Diego Armando", meglio usare un campo di lunghezza variabile. 255 caratteri sono comunque troppi, perciò limitiamolo a 40. Il primo campo sarà quindi:

nome_utente: varchar(40)

Infine, il campo data_ora. Mysql offre sia un campo "DATE" (che memorizza solo giorno/mese/anno), sia un "DATETIME" (in cui è possibile inserire anche l'orario). Utilizziamo il secondo tipo.
Con phpMyAdmin creiamo quindi la nostra tabella, chiamandola "messaggi":



Click su "esegui" e inseriamo le specifiche nella pagina successiva:



Infine, click su "salva". PhpMyAdmin stampa a video il messaggio "tabella messaggi è stato creato."



L'interfaccia utente e le pagine di elaborazione

A questo punto, possiamo chiudere phpMyAdmin e passare finalmente a PHP, inserendo i dati via codice!

Prepariamo la nostra form. Si tratta di normale HTML, se avete seguito la precedente lezione vi dovrebbe essere tutto chiaro:

file "form.html"

<form name="moduloGuest" action="elabora_guest.php" method="post">
<table>
   <tr>
     <td>Il tuo nome:</td><td><input type="text" name="nome_utente" size="30" maxlength="40"></td>
   </tr>
   <tr>
     <td valign="top">Il tuo messaggio:</td><td><textarea name="testo" cols="30" rows="6"></textarea></td>
   </tr>
   <tr>
     <td align="center" colspan="2"><input type="submit" value="Invia il messaggio!"></td>
   </tr>
</table>
</form>

(ho omesso i tag tradizionali di intestazione -head, body ecc.- ovviamente potete con normale html configurare a vostro piacere la pagina)

Passiamo adesso alla pagina "elabora_guest.php", fulcro dell'applicazione. Nella scorsa lezione abbiamo visto e chiarito il meccanismo delle form (le variabili $_POST e $_GET), adesso manca lo scambio con il database.
Come phpMyAdmin e MCC, una pagina PHP può interagire con un db mysql soltanto se ne conosce le coordinate.
Inseriamo allora, preferibilmente nella prima riga della pagina, le istruzioni di connessione:

<?php
$connessione=mysql_connect("localhost", "root", "");
$selezione_db=mysql_select_db("guestbook", $connessione);
?>

Le istruzioni sopra riportate utilizzano funzioni predefinite del linguaggio (mysql_connect e mysql_select_db) per effettuare la connessione ad un database mysql.
PHP può interagire anche con altri db, compreso "o' cess" alias Access, ma la stragrande maggioranza degli script e dei siti utilizza mysql, altro prodotto open source e gratuito.
La funzione mysql_connect richiede tre parametri: l'host (nel nostro caso in locale, in un db internet è qualcosa del tipo "sql.nomesito.com" oppure un indirizzo IP), l'username e la password (che nelle installazioni di default di easyphp sono "root" e vuota).
La funzione restituisce un puntatore, da memorizzare in una variabile ($connessione).
Una volta connesso ad un host, mysql necessita di sapere su quale database operare. Nel localhost abbiamo già diversi db, quelli creati di default (test, mysql ecc.) e quelli del nostro corso (come "corso_php"). Scegliamo "guestbook".

Ora dobbiamo inserire i dati provenienti dalla form. Utilizziamo per questo una query SQL.
Come detto nelle passate lezioni, SQL è un linguaggio (quasi) standard usato da diversi tipi di database per scambiare e manipolare dati. La funzione mysql_query() riceve come parametro una stringa SQL e la invia a Mysql per l'esecuzione.
Esula dagli obiettivi di questo corso (servirebbe un manuale di 1000 pagine!) parlare approfonditamente di SQL, comunque con un giro su internet o un salto in libreria potete chiarire meglio il discorso.
L'istruzione base SQL per l'inserimento dati in un db è:

INSERT INTO nomeTabella (nomeCampi) VALUES (valori)

Nel nostro caso avremmo qualcosa del genere:

INSERT INTO messaggi (nome_utente, messaggio, data_ora) VALUES ($_POST['nome_utente'], $_POST['testo'], now())

now() è una comodissima funzione mysql (quindi interna al motore del database, non di php) che invia automaticamente la data/ora correnti.


In realtà la query sopra descritta non funzionerebbe. Il problema è che, quando abbiamo a che fare con sequenze di caratteri (stringhe), dobbiamo usare un delimitatore per far capire a mysql quando il testo inizia e quando finisce.
Il delimitatore da utilizzare è l'apice. MySQL può usare anche il doppio apice per delimitare le stringhe, ma non è compatibile con tutti i tipi di database esistenti, quindi useremo l'apice singolo.
Rivediamo la query precedente con la sintassi:

INSERT INTO messaggi (nome_utente, messaggio, data_ora) VALUES ('$_POST['nome_utente']', '$_POST['testo']', now())

L'istruzione PHP sarebbe quindi:
mysql_query("insert into messaggi (nome_utente, messaggio, data_ora) values ('" . $_POST['nome_utente'] . "', '" . $_POST['testo'] . "', now())");

Ecco il file elabora_guest.php:

<?php
$connessione=mysql_connect("localhost", "root", "");
$selezione_db=mysql_select_db("guestbook", $connessione);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Corso PHP</TITLE>
</HEAD>
<BODY>
<?php
$inserimento=mysql_query("insert into messaggi (nome_utente, messaggio, data_ora) values ('" . $_POST['nome_utente'] . "', '" . $_POST['testo'] . "', now())");
echo "messaggio inviato con successo";
?>
</BODY>
</HTML>

Salvate sia form.html che elabora_guest.php in una directory dentro la root di easyPHP/XAMPP e testate lo script. Tornate poi in PhpMyAdmin e verificate che il record sia stato creato:









Divertiamoci inserendo altri messaggi, con nomi e testi ognuno diverso dall'altro, in modo da avere record a sufficienza per testare la pagina di visualizzazione.



Passiamo adesso al file "read_guest.php".

Lo scopo di questo file è inverso rispetto ad elabora_guest: dobbiamo estrarre tutti i dati dalla tabella (ordinati in base alla data) e mostrarli a video.

La sintassi sql per estrarre dati è:

SELECT campi FROM tabella ORDER BY campo

Nel nostro caso, quindi:

SELECT nome_utente, messaggio, data_ora FROM messaggi ORDER BY data_ora

Visto che dobbiamo estrarre tutti i tre campi della tabella, possiamo ricorrere ad una "scorciatoia" sql: l'asterisco. La query sopra descritta è l'equivalente di:

SELECT * FROM messaggi ORDER BY data_ora

Sappiamo adesso come estrarre dati da una tabella. Rimane un'ultimo ostacolo: come possiamo scorrerli? Cosa ritorna una query: una variabile, un oggetto, un array... cosa?

In effetti, una query SQL ritorna un insieme indefinito. E' come se restituisse un armadio pieno di giornali, cd, riviste (variabili) in maniera disordinata, senza cassetti per ordinarli. Siamo noi che, a seconda dell'utilizzo che abbiamo in mente, dobbiamo disporre nel giusto modo i valori.
Provate questo script,salvandolo come "read_guest.php":

<?php
  $connessione=mysql_connect("localhost", "root", "");
  $selezione_db=mysql_select_db("guestbook", $connessione);
?>
<HTML>
<HEAD>
<TITLE>Corso PHP</TITLE>
</HEAD>
<BODY>
<?php
  $lettura_risultati=mysql_query("select * from messaggi order by data_ora");
  echo "il risultato della query è: " . $lettura_risultati;
?>
</BODY>
</HTML>

A video verrà stampato:

il risultato della query è: Resource id #2.

Una query ritorna dunque una RISORSA. A seconda dell'utilizzo, saremo noi a decidere se sistemare i "pezzi" in un array, in variabili singole o altro.

Nel nostro caso abbiamo bisogno di scorrere tutti i risultati della query e stamparli a video. L'ideale sarebbe estrarre tutti i dati in array associativi (qualcosa del tipo "risultato['nome_utente']='pinco pallo', "risultato['messaggio']='super web-link' ecc.). Abbiamo già visto l'utilizzo del ciclo while per scorrere un insieme di questo tipo.

La funzione mysql_fetch_array() fa proprio questo: piazza i risultati di una "Resource" mysql in un array associativo, un record per ogni tornata.

Date un'occhiata a questo codice, se avete seguito le precedenti lezioni dovrebbe risultare più o meno familiare:

<?php
$connessione=mysql_connect("localhost", "root", "");
$selezione_db=mysql_select_db("guestbook", $connessione);
?>
<HTML>
<HEAD>
<TITLE>Corso PHP</TITLE>
</HEAD>
<BODY>
<?php
  $lettura_risultati=mysql_query("select * from messaggi order by data_ora");
  while( $scatola_temporanea = mysql_fetch_array($lettura_risultati) ){
     $nome_utente = $scatola_temporanea['nome_utente'];
     $testo_messaggio = $scatola_temporanea['messaggio'];
     $data_inserimento = $scatola_temporanea['data_ora'];
     echo "L'utente " . $nome_utente . ", in data " . $data_inserimento . ", ha scritto:<br><br>" . $testo_messaggio . "<br><br><br>";
  } //fine ciclo while che scorre la query e piazza i risultati nell'array temporaneo
?>
</BODY>
</HTML>

L'output sarà questo:






Miglioriamo l'interfaccia

Non è obbligatorio trasferire i dati dall'array associativo restituito dal database a delle variabili locali, anzi, è dispendioso a livello di codice che a livello di risorse che vengono utilizzate. È una operazione che va fatta solo quando si devono manipolare i dati estratti, ma non è il nostro caso. Il codice sopra, quindi, può essere scritto anche nel modo seguente:

<?php
$connessione=mysql_connect("localhost", "root", "");
$selezione_db=mysql_select_db("guestbook", $connessione);
?>
<HTML>
<HEAD>
<TITLE>Corso PHP</TITLE>
</HEAD>
<BODY>
<?php
  $lettura_risultati=mysql_query("select * from messaggi order by data_ora");
  while( $scatola_temporanea = mysql_fetch_array($lettura_risultati) ){
     echo "L'utente " . $scatola_temporanea['nome_utente'] . ", in data " . $scatola_temporanea['data_ora'] . ", ha scritto:<br><br>" . $scatola_temporanea['messaggio'] . "<br><br><br>";
  } //fine ciclo while che scorre la query e piazza i risultati nell'array temporaneo
?>
</BODY>
</HTML>


Prima di lasciarvi ai "compiti a casa" (mi raccomando scrivete sul forum se avete problemi o chiarimenti in merito), cerchiamo di migliorare la query.
Intanto, è evidente che il formato data/ora restituito da mysql è poco piacevole nella visualizzazione. La frase "in data 2004-03-14 14:32:55" non è granchè elegante.

MySql permette di formattare i campi data/ora già in fase di query. Eseguite questa istruzione (sostituendo il contenuto delle righe php nel file read_guest):

<?php
  $lettura_risultati=mysql_query("select nome_utente, messaggio, date_format(data_ora, '%d/%m/%Y - ore %H:%i:%s') as data_formattata from messaggi order by data_ora");
  while($scatola_temporanea=mysql_fetch_array($lettura_risultati)){
     echo "L'utente " . $scatola_temporanea['nome_utente'] . ", in data " . $scatola_temporanea['data_formattata'] . ", ha scritto:<br><br>" . $scatola_temporanea['messaggio'] . "<br><br><br>";
  } //fine ciclo while che scorre la query e piazza i risultati nell'array temporaneo
?>

eseguite di nuovo la pagina:



Visto la potenza di Mysql? Abbiamo utilizzato all'interno della query una funzione predefinita del database, date_format(), che riceve due parametri. Uno è il nome del campo da formattare, (data_ora) e l'altro è il formato. Inserendo un testo (come "in data"), questo viene stampato nel risultato; inserendo un carattere particolare preceduto dal percentuale ("%d"), questo viene stampato prendendo come base il dato orario.
I caratteri speciali per la formattazione di un campo data sono:

%d (stampa il giorno);
%m (stampa il mese);
%Y (stampa l'anno in 4 cifre);
%H (stampa l'ora nel formato 1..24);
%i (stampa i minuti)
%s (stampa i secondi)

Provate a sostituire i parametri con altre lettere dell'alfabeto (%a, %b..) e divertitevi :)

Possiamo divertirci ancora di più, inserendo del comune testo all'interno di "date_format()" (finchè non incontra il simbolo di "%", la funzione stampa a video quanto trova):

<?php
 $lettura_risultati=mysql_query("select nome_utente, messaggio, date_format(data_ora, 'in data %d/%m/%Y - ore %H:%i:%s') as data_formattata from messaggi order by data_ora");
  while($scatola_temporanea=mysql_fetch_array($lettura_risultati)){
    echo "L'utente " . $scatola_temporanea['nome_utente'] . ", " . $scatola_temporanea['data_formattata'] . ", ha scritto:<br><br>" . $scatola_temporanea['messaggio'] . "<br><br><br>";
  } //fine ciclo while che scorre la query e piazza i risultati nell'array temporaneo
?>

Compitini da fare a casa e discutere sul forum:

- personalizzate la prima parte delle pagine finora elaborate con la funzione include() vista nella scorsa lezione. Finora avete a che fare con due pagine di connessione; ma cosa succederebbe se doveste gestire un intero portale con migliaia di file? Ad ogni trasferimento online dovreste cambiare migliaia di parametri di connessione al database (quelli del provider al posto del locale)..

- questo script funziona ma ha dei difetti. Cosa succede se richiamate la pagina read_guest quando non è mai stato inserito un messaggio nel db? E cosa succede se l'utente va a capo nella form? Il testo viene mandato a capo anche nel file read_guest? E se inserite un errore nella query di insert (chessò, invece di INSERT scrivete "INSERTIS"), la query che fine fa? E il messaggio "testo inserito con successo" viene comunque visualizzato? Trovate il maggior numero di bug possibili in questo script e segnalateli nel forum. Nella prossima lezione li correggeremo :)

- credo non ci sia bisogno di dirlo.. personalizzate l'html delle pagine.. un guestbook in Times New Roman non fa bella figura :)

Aspetto le vostre risposte e i vostri dubbi sul forum!



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



Claudio Curci e Max Kiusso per Web-Link.it