L'ultima (e fino ad ora l'unica) volta che abbiamo
trattato lo Zend
Framework in questa sede abbiamo focalizzato la nostra
attenzione sul
modulo Zend_Controller, che espone una serie di funzionalità
che
facilitano l'implementazione e la manutenzione di applicazioni web
aderenti al pattern MVC. Oggi voglio spostarmi in un campo differente e
parlare delle soluzioni proposte dal framework per la gestione dei
database.
PHP ha un buon supporto per i database, ma ha il difetto di
fornire
troppe soluzioni alternative per attuare
l'interfacciamento con i
server di dati. In mezzo a questa quantità veramente enorme
di
classi e funzioni mi pare una buona scelta optare per una via univoca
da perseguire in tutti i propri applicativi. La soluzione migliore
è sicuramente quella di affidarsi ad un layer di astrazione
potente e funzionale, che ci permetta di interfacciarci con sistemi
diversi ed architetture differenti; PHP offre un paio di buone
soluzioni a questo proposito, che sono PDO ed SDO.
La seconda
soluzione, di cui parleremo in modo più approfondito in
un'altra
sede, offre anche funzionalità per accedere univocamente ad
altre fonti di dato, quale l'XML. La prima invece si focalizza
solamente sui database e, anche se con qualche bug di troppo non ancora
risolto, offre una buona serie di funzionalità. La soluzione
proposta dallo Zend Framework si basa appunto su questa libreria, che
cerca di migliorare con l'aggiunta di interessanti routine di
utilità.
Il modulo Zend_Db
Il modulo Zend_Db rappresenta il layer di astrazione
basato su PDO
utilizzato dal framework per accedere ai database. L'utilizzo di PDO
come libreria nativa permette al layer di astrazione di fornire
l'accesso a tutti i sistemi di database supportati dalla libreria
stessa. Tra questi ricordiamo MySQL, PostgreSQL, Microsoft SQL Server,
SQLite ed altri. La creazione di un oggetto per interfacciarsi ad un
database è eseguita tramite l'utilizzo del pattern factory:
Connessione ad un db tipo MySQL
<?php
/* Presuppongo che il Framework
Zend sia nel vostro include_path */
require_once 'Zend/Db.php';
// Opzioni di connessione
$options = array ('host'
=> '127.0.0.1',
'username' => 'utente',
'password' => 'password',
'dbname' => 'test');
$db = Zend_Db::factory('pdoMysql', $options);
?>
La funzione statica factory della classe Zend_Db accetta come
parametri una stringa rappresentante il driver di connessione che si
desidera utilizzare ed un array di opzioni specifiche per il driver e
necessarie per connettersi correttamente alla fonte di dati richiesta.
Se per esempio avessimo voluto connetterci ad un database SQLite, che
non richiede l'autenticazione e l'utilizzo di un server, avremmo
utilizzato il codice seguente:
Connessione ad un db tipo SQLite
<?php
require_once 'Zend/Db.php';
$options = array ('dbname' => 'test');
$db = Zend_Db::factory('pdoSqlite', $options);
?>
Il risultato di queste operazioni è
un'implementazione
specifica del driver utilizzato dell'interfaccia
Zend_Db_Adapter_Abstract
. Tramite questa
interfaccia è possibile
accedere alle funzionalità aggiuntive fornite dal framework
e ad
alcune delle funzionalità esposte da PDO.
Interrogare il database
Una volta ottenuta l'interfaccia richiesta possiamo iniziare
a lavorare
per effettuare le interrogazioni sulla nostra base di dati. Ogni query
SQL fornita direttamente al modulo verrà passata al layer
PDO
sottostante che si occuperà di eseguirla e restituire
l'eventuale risultato sotto forma di PDOStatement. Vediamo come
eseguire una semplice query di selezione sul nostro database di prova:
Esecuzione di una query
<?php
// ... ometto la connessione al
server
/* Query eseguita quotando
manualmente i valori */
$sql = $db->quoteInto(
'SELECT * FROM articles WHERE
creation_date > ?',
'2006-06-05'
);
$result = $db->query($sql);
$rows = $result->fetchAll();
var_dump($rows);
/* Stessa query eseguita
utilizzando i placeholder */
$result = $db->query(
'SELECT * FROM articles WHERE
creation_date >
:creation_date',
array('creation_date' =>
'2006-06-05')
);
$rows = $result->fetchAll();
var_dump($rows);
?>
Come potete notare l'esecuzione di una query
è un
procedimento molto semplice. Il risultato restituito è
un'istanza di PDOStatement che utilizziamo per recuperare e gestire i
risultati. Un argomento molto importate su cui soffermarsi un attimo
è il quoting dei parametri passati alle query. Nell'esempio
fornito non è molto utile, ma è fondamentale
quando si
opera su dati potenzialmente pericolosi perché provenienti
da
fonti non sicure. Il quoting di una stringa viene effettuato da Zend_Db
utilizzando i metodi quote
o quoteInto
forniti dall'adapter, oppure,
nel caso si utilizzino i placeholder, in modo automatico sui parametri:
Esempio della funzione quote()
<?php
// ...
//
Quoto un valore scalare (stringa, numero, ecc ...)
echo $db->quote('Come "va" ?');
/*
* Il valore restituito sarà '"Come "va"?"',
* circondato da apici doppi
*/
// Quoto un array
echo $db->quote(range(3));
/*
* Il risultato è una serie di stringhe
separate da virgola:
* '"1", "2", "3"'
*/
// Quoto un valore e lo inserisco
in una stringa
echo $db->quoteInto('id = ?', 1);
/* id = "1" */
?>
Come potete notare la funzione quote
si occupa di trasformare
il
valore passato come argomento in una stringa che non potrà
nuocere in alcun modo quando utilizzato all'interno di una query SQL.
Utilizzando quoteInto
invece
si effettua il quoting di tutti i
parametri della funzione dopo il primo, e si sostituiscono (nell'ordine
di passaggio) i risultati ai punti di domanda presenti nella stringa
usata come primo argomento. Nel caso si utilizzino dei placeholder
durante la chiamata della funzione query su un adapter, i valori dei
placeholder specificati nell'array vengono tutti quotati prima
dell'assegnazione.
Prepared statement
Il modulo Zend_Db offre anche il supporto ai prepared
statement.
Questa teconologia permette (se fornita dal sistema al quale ci si
connette) di precompilare delle query
specificate parzialmente
lasciando dei parametri variabili che potranno essere specificati in
seguito. Grazie alla precompilazione della query tutte le successive
chiamate verranno eseguite in modo più efficiente. Vediamo
un
esempio:
Precompilazione della query
<?php
// ...
// Precompilo la query
$stmt = $db->prepare('SELECT * FROM articles WHERE creation_date
> :creation_date');
// Assegno il parametro alla query
precompilata ...
$stmt->bindValue('creation_date', '2006-06-05');
// ... ed eseguo lo statement
$stmt->execute();
$rows = $stmt->fetchAll();
var_dump($rows);
// Eseguo nuovamente la query con
diversi parametri
$stmt->bindValue('creation_date', '2006-03-01');
$stmt->execute();
$rows = $stmt->fetchAll();
var_dump($rows);
?>
Il metodo prepare
si occupa di
precompilare la query e
restituire un
oggetto PDOStatement
con il quale eseguire le
operazioni necessarie.
Utilizzando bindValue
è possibile
assegnare un determinato
valore ad un placeholder, mentre con execute
viene eseguita la query
usando i parametri specificati in precedenza. L'eventuale risultato
è salvato all'interno dell'oggetto, e può essere
recuperato con le normali funzionalità di PDO.
Inserire, modificare e cancellare i record
Il modulo Zend_Db
espone una serie
di funzionalità
per
velocizzare le operazioni di modifica delle tabelle del database. Per
quanto riguarda il processo di inserimento di un nuovo record possiamo
appoggiarci al metodo insert
:
Inserimento di un nuovo record: insert
<?php
// ...
$row = array (
'title'
=>
'Accesso ai database
con lo Zend Framework',
'author'
=> 'Farina
Gabriele',
'creation_date' =>
'2006-06-05',
'content'
=> ''
);
$rows_affected = $db->insert('articles', $row);
$last_insert_id = $db->lastInsertId();
?>
Il metodo insert
accetta come
argomenti la tabella su cui
operare ed
un array contenente i valori da assegnare alle colonne della tabella
per questo inserimento. Il risultato dell'operazione di inserimento,
che si occupa automaticamente di effettuare il quoting dei dati,
è il numero di righe modificate dall'operazione. L'id
dell'inserimento può essere recuperato usando il metodo
lastInsertId
dell'adapter.
Effettuare l'operazione di aggiornamento dei dati è
altrettanto semplice:
Aggiornamento di un record: update
<?php
// ...
$data = array (
'content' => '... some content
...',
);
/*
$_POST viene usato in questo modo a titolo di
esempio.
*
Evitiamo sempre di usarlo direttamente in questo
modo */
$where = $db->quoteInto('creation_date = ?',
$_POST['creation_date']);
$rows_affected = $db->update('articles', $data, $where);
?>
Usando il metodo update
possiamo
passare, oltre all'array di
valodi
da modificare, anche una clausola condizionale opzionale per limitare
le operazioni di aggiornamento. Questa clausola non verrà
quotata automaticamente quindi dovremo occuparci di fallo manualmente
come nell'esempio proposto.
Infine l'operazione di eliminazione:
Eliminazione di un record
<?php
// ...
/*
*
$_POST viene
usato in questo modo a titolo di
esempio.
*
Evitiamo sempre di usarlo direttamente in questo
modo ...
*/
$where = $db->quoteInto('creation_date = ?',
$_POST['creation_date']);
$rows_affected = $db->update('articles', $where);
?>
Conclusioni
Eccoci un'altra volta alla fine. Ho introdotto brevemente il
modulo
Zend_Db
focalizzandomi sulle
funzionalità esposte da PDO ed
alcune utilità generiche da utilizzare nei propri script.
Nei
prossimi articoli vedremo nel dettaglio l'interfaccia ad oggetti
proposta dal team Zend per la creazione automatica di query SQL.