• Gli Active Record di Yii: un design pattern per rappresentare le tabelle del database in un modo orientato agli oggetti.

    Date: 2012.02.01 | Category: PHP | Tags:

    Gli Active Record sono un design pattern per interagire con il database in un modo semplice: l’obiettivo è di evitare di scrivere delle noiosissime query SQL per le comuni operazioni CRUD (create, read, update and delete).

    Gli Active Record (AR), in altri termini, sono l’implementazione delle tecnica di programmazione basata sull’Object-Relational Maping (ORM)(http://it.wikipedia.org/wiki/Object-relational_mapping).

    database-orm

    database-orm

    Gli Active Record trasformano:

    • le tabelle in classi
    • le righe in oggetti ed i campi in attributi

    Secondo quest’ottica, le comuni operazioni CRUD sono implementate tramite dei metodi degli Active Record.

    Inserimento di un nuovo record

    Ad esempio per inserire un nuovo post nella tabella tbl_post:

    $post=new Post;
    $post->title='sample post';
    $post->content='post body content';
    $post->save();

    Come potete notare non è necessario scrivere la query “INSERT INTO tbl_post(……) VALUES(…..)“, ma semplicemente si crea un oggetto $post che è un’istanza della classe Post, si impostano i valori dei campi della tabella come se fossero delle proprietà di una classe ($post->title per impostare il titolo) ed infine si inserisce il record invocando il metodo save() ( $post->save() ).

    Come creare gli Active Record collegati ad una tabella del database

    Ora supponiamo di avere una tabella con la seguente struttura:

    CREATE TABLE tbl_post (
        id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        title VARCHAR(128) NOT NULL,
        content TEXT NOT NULL,
        create_time INTEGER NOT NULL
    );

    Per accedere ad una tabella del database secondo la logica ORM, bisogna prima definire una classe AR che estende la classe CActiveRecord.
    Ricordo che ciascuna classe AR rappresenta una tabella mentre un istanza della classe AR rappresenta una riga della tabella.

    Ecco il codice minimale per implementare la classe AR per la tabella tbl_post:

    class Post extends CActiveRecord
    {
        public static function model($className=__CLASS__)
        {
            return parent::model($className);
        }
     
        public function tableName()
        {
            return 'tbl_post';
        }
    }

    Attenzione che per default, il nome della classe AR è la stessa del nome della tabella.
    Se invece sono diverse è necessario fare un ovverride del metodo tableName().

    I valori delle colonne di una tabella possono essere accedute come proprietà della corrispondente istanza AR.

    $post=new Post;
    $post->title='a sample post';

    E’ importante notare che anche se la proprietà “title” non è stata esplicitamente dichiarata all’interno della classe Post, in realtà possiamo ugualmente accederci perché “title” è una colonna della tabella tbl_post e CActiveRecord permette che title sia accessibile come proprietà con l’aiutato del metodo magico __get() del PHP. In generale, nel caso in cui si tenti di accedere ad una colonna inesistente, il sistema solleva un’eccezione.

    Aggiungere un nuovo record nella tabella tbl_post

    Questi sono i semplici passi da seguire nel caso si voglia aggiungere un nuovo record in un tabella del database:

    1. Creare una nuova istanza della classe AR corrispondente alla tabella
    2. Impostare le proprietà associate alle colonne della tabella
    3. Richiamare il metodo save() per completare l’inserimento
    $post=new Post;
    $post->title='sample post';
    $post->content='content for the sample post';
    $post->create_time=time();
    $post->save();

    Si può assegnare ad un attributo un valore di tipo CDbExpression prima che il record sia salvato nel database.

    Ad esempio per salvare “un timestamp” ritornato dalla funzione MySQL NOW() basta scrivere il seguente codice:

    $post=new Post;
    $post->create_time=new CDbExpression('NOW()');
    // $post->create_time='NOW()'; will not work because
    // 'NOW()' will be treated as a string
    $post->save();

    Leggere i record

    Per leggere i dati di una tabella possiamo utilizzare i metodi find:

    // trova la prima riga che soddisfa la condizione specificata
    $post=Post::model()->find($condition,$params);
    // trova la riga con la chiave primaria specificata
    $post=Post::model()->findByPk($postID,$condition,$params);
    // trova la riga con gli attributi specificati
    $post=Post::model()->findByAttributes($attributes,$condition,$params);
    // trova la prima riga utilizzando lo statement sql specificato
    $post=Post::model()->findBySql($sql,$params);

    Il metodo statico model() ritorna un’istanza della classe AR ed è obbligatorio per ciascuna classe AR.

    Possiamo poi leggere i valori come se si accedesse ad un normale oggetto, ad esempio echo $post->title;

    Quando richiamiamo il metodo find, utilizziamo $condition e $params per specificare le condizioni della query.

    La condizione può essere una stringa che rappresenta la clausola WHERE di uno statement SQL mentre $params è un array di parametri i cui valori possono essere abbinati ai segnaposti impostati nella $condition:

    Per esempio:

    // cerca la riga con postID=10
    $post=Post::model()->find('postID=:postID', array(':postID'=>10));

    E’ possibile specificare delle query più complesse utilizzando in realtà come $condition un’istanza della classe CDbCriteria.

    $criteria=new CDbCriteria;
    $criteria->select='title';  // only select the 'title' column
    $criteria->condition='postID=:postID';
    $criteria->params=array(':postID'=>10);
    $post=Post::model()->find($criteria); // $params is not needed

    Un metodo alternativo a CDbCriteria è di passare un array al metodo find.
    Come mostrato qui di seguito:

    $post=Post::model()->find(array(
        'select'=>'title',
        'condition'=>'postID=:postID',
        'params'=>array(':postID'=>10),
    ));

    Quando una query restituisce più di un record, possiamo usare il metodo findAll:

    // find all rows satisfying the specified condition
    $posts=Post::model()->findAll($condition,$params);
    // find all rows with the specified primary keys
    $posts=Post::model()->findAllByPk($postIDs,$condition,$params);
    // find all rows with the specified attribute values
    $posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
    // find all rows using the specified SQL statement
    $posts=Post::model()->findAllBySql($sql,$params);

    Se la findAll non trova nessun record che soddisfa le condizioni specificate restituisce un array vuoto.

    Inoltre sono forniti questi metodi:

    // get the number of rows satisfying the specified condition
    $n=Post::model()->count($condition,$params);
    // get the number of rows using the specified SQL statement
    $n=Post::model()->countBySql($sql,$params);
    // check if there is at least a row satisfying the specified condition
    $exists=Post::model()->exists($condition,$params);

    Aggiornamento di Record

    $post=Post::model()->findByPk(10);
    $post->title='new post title';
    $post->save(); // save the change to database
    // update the rows matching the specified condition
    Post::model()->updateAll($attributes,$condition,$params);
    // update the rows matching the specified condition and primary key(s)
    Post::model()->updateByPk($pk,$attributes,$condition,$params);
    // update counter columns in the rows satisfying the specified conditions
    Post::model()->updateCounters($counters,$condition,$params);

    Cancellare un Record

    $post=Post::model()->findByPk(10); // assuming there is a post whose ID is 10
    $post->delete(); // delete the row from the database table
    // delete the rows matching the specified condition
    Post::model()->deleteAll($condition,$params);
    // delete the rows matching the specified condition and primary key(s)
    Post::model()->deleteByPk($pk,$condition,$params);

    Validazione dei dati

    Spesso e volentieri capita che nel momento in cui desideriamo inserire o aggiornare un record del database, abbiamo bisogno di controllare se il valore della colonna è conforme a delle regole specifiche.

    AR assolve alla validazione dei dati automaticamente quando il metodo save() è invocato.
    La validazione è basata su regole specificate nel metodo rules() della classe AR.

    if($post->save())
    {
        // data is valid and is successfully inserted/updated
    }
    else
    {
        // data is invalid. call getErrors() to retrieve error messages
    }

    Quando i dati da inserire o aggiornare sono inoltrati dal metodo post di un form, possiamo assegnarli alle rispettive proprietà della classe AR in questo modo:

    $post->title=$_POST['title'];
    $post->content=$_POST['content'];
    $post->save();

    Infine se desideriamo aggiornare più proprietà contemporaneamente in un colpo solo:

    // assume $_POST['Post'] is an array of column values indexed by column names
    $post->attributes=$_POST['Post'];
    $post->save();

    Risorse

    La fonte di questo articolo è http://www.yiiframework.com/doc/guide/1.1/it/database.arr