Se avete seguito i miei articoli in questi mesi, vi sarete sicuramente spesso imbattuti nel concetto di persistenza degli oggetti su database. Come accennato quando ho trattato questo argomento parlando del framework MVC Taste, la persistenza su database è un concetto che sta prendendo sempre più piede nello sviluppo odierno, sia grazie al fatto che risulta molto importante poter utilizzare una sola interfaccia di accesso ai dati (che solitamente è il linguaggio di programmazione stesso, non l'SQL) sia perché esistono librerie interessanti capaci di svolgere per noi gran parte dei compiti più ripetitivi.
La libreria di cui parleremo si chiama Propel, ed è un insieme di script per PHP 5 che si occupano di mappare la struttura relazionale delle nostre tabelle su normali oggetti PHP. Non possiamo quindi parlare di vera e propria persistenza degli oggetti dato che sono questi ultimi ad essere generati in base alla struttura del nostro database, ma il risultato è molto simile e ci permette di non prendere lavorare con gli oggetti senza la necessità di accedere a linguaggi di interrogazione alternativi.
Propel è un progetto ispirato ad Apache Torque ed è strutturato in due grossi componenti separati: un generatore di codice e una libreria di runtime. Il generatore di codice è uno script che, come vedremo in seguito, accetta un documento XML in input e genera in output le classi necessarie per accedere ed interrogare il database relazionale utilizzando solamente costrutti PHP; la libreria di runtime invece definisce una serie di funzionalità che vengono utilizzate dal codice generato per comunicare correttamente con il database. Per la generazione di codice Propel si basa su un altro interessante progetto open source, Phing, che è un framework per la compilazione ed il build delle applicazioni basato sull'ormai utilizzatissimo Apache Ant. Infine, per rendere uniforme l'accesso ad un grande numero di database relazionali, Propel utilizza una libreria chiamata Creole, che è essa stessa un side project degli stessi sviluppatori di Propel.
In questo articolo ci occuperemo del semplice processo di installazione e della configurazione del sistema, mentre nei prossimi andremo in dettaglio analizzando le funzionalità offerte da Propel.
Installazione e configurazione
Prima di iniziare ad utilizzare Propel è ovviamente necessario installarlo e fornirgli tutte le dipendenze necessaria affinché possa funzionare correttamente. Oltre ad avere una versione di PHP 5 funzionante installata sul proprio Web server con abilitato il supporto ad XML ed SPL (Standard PHP Library), è anche necessario uno dei database relazionali supportati attualmente da Propel: MySQL, MS SQL Server, SQLite, Oracle o PostgreSQL. Assicurato il fatto di avere questi strumenti installati correttamente, possiamo procedere con l'installazione di Phing e Creole, gli altri due requisiti. Per l'installazione di questi due moduli e di Propel stesso mi affiderò a PEAR (che da PHP 4.3.0 viene installato automaticamente a meno che non lo si disabiliti manualmente).
Procediamo quindi con i comandi per installare i tre progetti:
pear channel-discovery pear.phing.info pear install phing/phing pear channel-discovery pear.phpdb.org pear install phpdb/creole pear install phpdb/jargon pear install phpdb/propel_generator pear install phpdb/propel_runtime
Con questi semplici comandi ci assicuriamo l'installazione funzionante di Propel e di tutte le sue dipendenze. In caso la directory di installazione dei package PEAR non fosse inclusa nella direttiva include_path
del file di configurazione di PHP, è importante ricordarsi di aggiungerla e riavviare il server.
Dopo aver installato i package sono necessarie alcune operazioni di configurazione per assicurarsi che tutto funzioni correttamente. Per prima cosa è necessario che vengano disabilitate alcune - obsolete - direttive nel file php.ini: ze1_compatibility_mode
, magic_quotes_gpc
e magic_quotes_sybase
devono essere impostate a Off
.
L'ultimo accorgimento da prendere è quello di linkare simbolicamente lo script propel-gen in modo che risulti facilmente accessibile dalla shell:
cd /usr/local/bin ln -s /usr/local/propel/bin/propel-gen propel-gen
Lo script propel-gen è stato introdotto con al versione 1.2 della librerie e si occupa semplicemente di nascondere le chiamate sottostanti al progetto Phing. In caso utilizziate Windows, esiste un file chiamato propel-gen.bat che si occupa degli stessi compiti.
Definizione di uno schema XML
Come accennato nell'introduzione Propel utilizza lo script propel-gen per creare delle classi PHP che possono essere utilizzate per accedere al database relazionale sottostante. Questo script utilizza internamente Phing con dei task sviluppati appositamente per generare il codice, recuperando le informazioni da cui comprendere come generare il codice da dei file XML.
Questi file XML devono rispettare una stesura standard che vedremo in seguito, e rappresentano un passo fondamentale per informare Propel di come interfacciarsi con il nostro database. Un errore nella stesura dell'XML e Propel si comporterà in modo inaspettato, magari non trovando alcune tabelle o valutando dei campi come se fossero di tipo diverso da quello che sono in realtà.
Propel lavora su una directory nella quale inserisce il codice generato e dalla quale recupera il file XML contenente la descrizione del DB ed un file contenente le proprietà di compilazione; è opportuno che questa directory sia differente in base al progetto che su cui si sta lavorando, per evitare problemi in fase di compilazione o utilizzo.
Creata questa directory possiamo procedere con la definizione del file XML che mappa il nostro database (schema.xml). Lo schema XML dipende ovviamente dalla struttura del nostro database relazionale, ma come vedremo a breve è molto semplice da definire. Il database su cui andremo a lavorare si chiamerà redazione, e conterrà due tabelle: una contenete gli articoli scritti ed una gli autori di questi articoli. La struttura SQL delle tabelle sarà la seguente:
CREATE DATABASE redazione CHARSET utf8 COLLATE utf8_unicode_ci; CREATE TABLE articoli ( article_id INT UNSIGNED AUTO_INCREMENT NOT NULL, author_id INT UNSIGNED NOT NULL, title VARCHAR NOT NULL, content TEXT NOT NULL, FOREIGN KEY (author_id) REFERENCES autori (author_id) ON DELETE CASCADE ON UPDATE CASCADE, KEY(author_id), PRIMARY KEY(article_id) ) Type=InnoDB; CREATE TABLE autori ( author_id INT UNSIGNED AUTO_INCREMENT NOT NULL, name VARCHAR NOT NULL, surname VARCHAR NOT NULL, PRIMARY KEY(author_id) ) Type=InnoDB;
In base alla definizione del database precedente, l'XML contenente le informazioni da passare al generatore di Propel sarà quello visualizzato nella pagina seguente.
Il file XML da passare al generatore di Propel sarà così conformato:
<database name="redazione" defaultIdMethod="native"> <table name="articoli" description="Tabella degli Articoli"> <column name="article_id" type="integer" primaryKey="true" autoIncrement="true" required="true" description="ID dell'Articolo"/> <column name="title" type="varchar" size="255" required="true" description="Titolo dell'Articolo"/> <column name="content" type="text" required="true" description="Contenuto dell'Articolo"/> <column name="author_id" type="integer" required="true" description="Chiave esterna (Autore)"/> <foreign-key foreignTable="autori"> <reference local="author_id" foreign="author_id"/> </foreign-key> </table> <table name="autori" description="Tabella degli Autori"> <column name="author_id" type="integer" required="true" primaryKey="true" description="ID dell'Autore"/> <column name="nome" type="varchar" size="128" required="true" description="Nome"/> <column name="cognome" type="varchar" size="128" required="true" description="Cognome"/> </table> </database>
Come possiamo notare la struttura dell'XML è molto semplice e rispecchia quasi specularmente la definizione SQL utilizzata per le tabelle. Il nodo di root di ogni schema XML dovrà essere database a cui dovranno essere associati come attributi il nome del database (name
) ed il metodo da utilizzare per la generazione degli indici (defaultIdMethod
); questo valore dipende da come si desidera procedere quando viene aggiunto un nuovo record ad una tabella del database. In generale è abbastanza utilizzare native e demandare al database engine stesso il compito di generare l'id del nuovo record creato.
All'interno del nodo database sono presenti dei nodi table
, contenenti le informazioni relative alle tabelle da utilizzare; il nodo table nel nostro caso contiene due diversi nodi: il nodo column
ed il nodo foreign-key
. Come possiamo evincere dal nome il primo serve a fornire le informazioni relative alla definizione di una colonna, il secondo invece di una chiave esterna. Propel ha un sistema avanzato che permette di gestire in modo quasi automatico la maggior parte delle relazioni che possono intercorre tra due tabelle; per questo motivo è buona norma specificare sempre una chiave esterna ove necessario.
Gli attributi dei due nodi sopra descritti sono autoesplicativi: è bene comunque ricordarsi che primaryKey
può essere utilizzato una sola volta, ed autoIncrement
può essere assegnato solamente ad una colonna identificata come chiave primaria.
Il secondo passaggio consiste nel definire le impostazioni di generazione del codice. Per prima cosa creiamo un file build.properties nella directory di lavoro, che conterrà le informazioni necessarie a Phing per generare il codice. Nel mio caso utilizzerò MySQL come database, ma è possibile specificare altri driver:
propel.project = redazione propel.database = mysql propel.database.url = mysql://username:password@host/redazione
Dopo aver salvato questo file possiamo passare ad impostare, in un file XML, le informazioni di configurazione che saranno necessarie in fase di runtime, che salveremo all'interno del file runtime-conf.xml. Il file schema.xml ed il file build.properties creati in precedenza sono relativi alla fase di generazione del codice e non influenzano direttamente la fase di runtime.
<config> <propel> <datasources default="redazione"> <datasource id="redazione"> <adapter>mysql</adapter> <connection> <phptype>mysql</phptype> <database>redazione</database> <hostspec>localhost</hostspec> <username>username</username> <password>password</password> </connection> </datasource> </datasources> </propel> </config>
Per compilare il nostro progetto utilizziamo
propel-gen /path/alla/cartella/redazione
Ed otterremo una serie di file PHP ed SQL pronti ad essere utilizzati all'interno dei nostri progetti. La prossima settimana vedremo le API di accesso esposte dal runtime di Propel, così da poter completare l'introduzione a questo interessante progetto.