Qualche settimana fa abbiamo iniziato a parlare degli strumenti forniti dallo Zend Framework per accedere in modo univoco alla maggior parte dei database supportati da PHP. Lo strumento in questione era la libreria Zend_Db che forniva un layer che, sfruttando PDO e qualche aggiunta da parte degli sviluppatori della libreria, permetteva di gestire agilmente la maggior parte delle esigenze relative allo sviluppo utilizzando i database.
In questo articolo continuerò il discorso iniziato introducendo una serie di classi ausiliarie che permettono di effettuare operazioni su database utilizzando una sintassi ad oggetti ed astraendo gran parte dell'SQL dal motore utilizzato. Le classi in questione fanno parte del modulo Zend_Db e permettono di effettuare operazioni di selezione nonchè inserimenti e modifiche alle singole righe.
La classe Zend_Db_Table
La prima classe che prendiamo in considerazione è Zend_Db_Table; la classe è sviluppata appoggiandosi a Zend_Adapter, che utilizza per connettersi ad una tabella specifica, recuperare informazioni sulla sua struttura ed esporre alcuni metodi per effettuare operazioni sulle righe e le colonne.
<?php require_once 'Zend/Db.php'; require_once 'Zend/Db/Table.php'; $params = array ( 'host' => 'localhost', 'username' => 'gabriele', 'password' => 'farina', 'dbname' => 'html_it' ); $db = Zend_Db::factory('pdoMysql', $params); /* Assegnamo l'adapter di default che verrà utilizzato * da ogni istanza di Zend_Db_table per accedere alla tabella * specifica */ Zend_Db_Table::setDefaultAdapter($db); // Creiamo una classe che si riferisce ad una tabella sul nostro DB class Articles extends Zend_Db_Table { } $articles = new Articles(); ?>
Per poter usufruire della classe Zend_Db_Table è necessario eseguire due operazioni fondamentali: la prima è quella di assegnare uno Zend_Adapter da utilizzare ogni volta che un'istanza di Zend_Db_Table avrà necessità di connettersi alla tabella di riferimento oppure effettuare query su di questa; la seconda è quella di definire una o più classi che estendono Zend_Db_Table e rappresentano le tabelle nel nostro database. Queste classi, quando inizializzate, recupereranno informazioni sulla struttura e sul contenuto dalle corrispettive tabelle presenti nel database e ci forniranno alcuni metodi per accedervi comodamente.
Il comportamento standard delle classi che estendono Zend_Db_Table è quello di recuperare il nome della tabella a cui devono fare riferimento dal nome della classe, trasformandolo in minuscolo ed anteponendo un underscore a tutte le lettere precedentemente maiuscole a parte la prima, e di utilizzare id come nome della colonna che rappresenta la chiave primaria. Ovviamente possiamo modificare questo comportamento:
<?php
// ... setup ...
class HtmlItArticles extends Zend_Db_Table
{
protected $_name = 'articles';
protected $_primary = 'article_id';
}
?>
In questo modo la classe HtmlItArticles farà riferimento alla tabella articles ed utilizzerà la colonna article_id come chiave primaria. In alternativa è possibile sovraccaricare il metodo _setup() e specificare questi valori come proprietà dell'istanza:
<?php
// ... setup ...
class HtmlItArticles extends Zend_Db_Table
{
protected function _setup()
{
$this->_name = 'articles';
$this->_primary = 'article_id';
// Operazione fondamentale
parent::_setup();
}
}
?>
Operazioni sulle tabelle
Una volta create correttamente le nostre classi possiamo istanziarle ed iniziare a lavorare su di queste. Le operazioni per le modifiche delle righe sono le stesse esposte dagli Zend_Adapter, anche se alcuni metodi hanno un comportamento leggermente diverso:
<?php // ... setup ... $articles = new HtmlItArticles(); $adapter = $articles->getAdapter(); $data = array( 'title' => "Tool dello Zend Framework per l'accesso ai db", 'author' => 'Gabriele Faarina' ); $insert_id = $articles->insert($data); // INSERT INTO articles (title, author) VALUES ('Tool dello Zend Framework per l'accesso ai db', 'Gabriele Faarina') $set = array( 'author' => 'Gabriele Farina', ); $where = $adapter->quoteInto('article_id = ?', $insert_id); $articles->update($set, $where); // UPDATE articles SET author = 'Gabriele Farina' WHERE article_id = '1' $where = $adapter->quoteInto('title = ?', "Tool dello Zend Framework per l'accesso ai db"); $articles->delete($where); // DELETE FROM articles WHERE title = 'Tool dello Zend Framework per l'accesso ai db' ?>
Il metodo insert accetta un array avente come chiavi le colonne da inserire e come valori i rispettivi valori, che verranno automaticamente quotati utilizzando lo Zend_Adapter associato alla tabella. Il valore restituito è l'ID dell'ultimo inserimento, differentemente dall'omonimo metodo Zend_Adapter::insert che restituisce il numero di righe interessate dall'operazione.
Il metodo update funziona allo stesso modo di Zend_Adapter::update ed accetta un array di colonne da modificare ed una clausola where opzionale; la clausola where dovrà essere quotata manualmente. Il valore restituito è il numero di righe interessate all'operazione di aggiornamento.
Infine il metodo delete accetta una clausola where precedentemente quotata e restituisce il numero di colonne eliminate.
L'utilizzo di metodi per effettuare le operazioni sulla tabella permette di aggiungere alle nostre classi dei comportamenti specifici alla nostra applicazione da eseguire durante queste operazioni:
<?php
// ... setup ...
class Articles extends Zend_Db_Table
{
public function insert($data)
{
if(!isset($data['creation_date']))
{
$data['creation_date'] = date('d/m/Y h:i:s');
}
parent::insert($data);
}
}
?>
Ricercare i record
Infine le classi che estendono Zend_Db_Table permettono di ricercare le righe utilizzando alcuni metodi interessanti:
- find(mixed): accetta come parametro un intero o un array di interi che rappresentano gli indici delle righe da recuperare. Restituisce un'istanza di Zend_Db_Table_Row in caso di richiesta singola oppure Zend_Db_Table_Rowset;
- fetchRow(string, string): accetta come parametri una clausola where ed, opzionalmente, il nome della colonna da utilizzare per l'ordinamento dei risultati; restituisce un'istanza di Zend_Db_Table_Row che rappresenta la prima riga trovata;
- fetchAll(string, string, int, int): accetta una clausola where, la colonna per la quale ordinare, il numero di righe da recuperare e la posizione dalla quale iniziare la restituzione delle righe; restituisce un'istanza di Zend_Db_Table_Rowset che rappresenta le righe che rispondono ai requisiti specificati.
Vediamo un esempio completo:
<?php
// ... setup ...
class Authors extends Zend_Db_Table
{
}
class Articles extends Zend_Db_Table
{
public function insert($data)
{
if(!isset($data['creation_date']))
{
$data['creation_date'] = date('d/m/Y h:i:s');
}
parent::insert($data);
}
}
$articles = new Articles();
$authors = new Authors();
$adapter = $articles->getAdapter();
$gabriele_id = $authors->insert(array(
'name' => 'Gabriele',
'surname' => 'Farina'
));
for($i = 0; $i < 100; ++$i)
{
$articles->insert(array(
'title' => 'Titolo dell'articolo '.$i,
'content' => 'Contenuto dell'articolo'.$i,
'author_id' => $gabriele_id
));
}
$even = $articles->findAll('id % 2 == 0');
for($even as $article)
{
$article->title .= '(even)';
$article->save();
}
$tenth = $articles->find(10);
$author = $authors->find($tenth->author_id);
printf("
<h3>%s</h3>
<p>%s</p>
<p style="text-align: right">%s</p>",
$tenth->title,
$tenth->content,
$author->name." ".$author->surname
);
?>
La classe Zend_Db_Table_Row contiene una lista di proprietà che rappresentano le colonne recuperate utilizzando la query di selezione. Queste colonne possono essere successivamente modificate ed i cambiamenti possono essere salvati utilizzando il metodo save. Nel caso in cui le modifiche ad una proprietà non possano essere effettuate a causa di alcuni vincoli interni della colonna, viene generata un'eccezione. La classe Zend_Db_Table_Rowset non è altro che un oggetto che si comporta come un array di Zend_Db_Table_Row:
<?php
// ... setup ...
$articles = new Articles();
$to_edit = $articles->findAll();
foreach($to_edit as $article)
{
echo $article->title, "...";
$article->revision_date = date('d/m/Y h:i:s');
$article->save();
echo "ok<br />";
}
?>
Conclusioni
Ed anche questo articolo è terminato. Sinceramente ritengo le funzionalità esposte dal modulo Zend_Db molto interessanti, soprattutto nell'ottica di fornire un approccio ad oggetti semplice da gestire ed adattare alle proprie esigenze. Forse sarebbe interessante inserire qualche soluzione per gestire le relazioni in modo da avere già a disposizione gli oggetti a cui si riferisce in una relazione piuttosto che doverli recuperare a mano. Però nel complesso la libreria mi pare interessante e con ottimi margini di crescita. D'altronde siamo solo alla versione 0.1.3 e di novità che bollono in pentola ce ne sono molte.