Etendre un Zend_Db_Table_Row avec des propriétés customisées

La famille Zend_Db_Table offre aux développeurs d’applications un ORM simple, mais parfois certaines manipulations évidentes s’avèrent impossibles.

Mettons nous en situation

Dans mon cas, j’ai souvent l’habitude d’appeler un objet depuis un autre. Par exemple, j’appelle l’objet Magazine associé à un Article par la méthode $article->getMagazine(). C’est pratique, ça permet des enchassements de méthodes assez rigolos genre $article->getMagazine()->getCouverture()->getUrl(). Jusqu’ici, pas de problème : il suffit d’écrire la méthode getMagazine() dans ma classe Article héritant de Zend_Db_Table_Row, et les autres méthodes dans les autres classes.

Mais que se passe-t-il si j’appelle huit fois de suite la méthode getMagazine() ? Il instanciera bêtement l’objet magazine huit fois de suite, avec huit requêtes SQL successives. Pas très optimal question performances. J’aimerais donc stocker l’objet Magazine, une fois instancié, dans une variable de la classe Article, et lui ressortir l’objet stocké les sept fois suivantes. Et là, ça coince.

Note : on pourra me rétorquer qu’il suffit de stocker l’objet $magazine une bonne fois pour toutes lors de l’exécution. C’est vrai et ça règle la question pour les situations simples où l’on ne traite que d’un unique article, mais dès qu’on commence à avoir plusieurs objets possédant chacun un ou plusieurs objets associés, on est obligé de stocker les variables dans un tableau associatif, et là je me dis : pour rester dans une logique POO et faciliter le déboguage, autant stocker l’info directement dans l’objet concerné.

Les limites de Zend_Db_Table_Row

Une classe héritant de Zend_Db_Table_Row possède un nombre défini de propriétés accessibles, qui correspondent rigoureusement aux colonnes de la table SQL associée. Si vous tentez, naïvement, de définir une nouvelle variable du type $magazine->variable1 = 50, il génère une erreur car il n’est pas prévu de pouvoir lui associer d’autres variables que celles de sa table.

Comment contourner cette limite ? J’ai trouvé la réponse sur le forum du Zend Framework :

  • créer une nouvelle classe héritant de Zend_Db_Table_Row, par exemple My_DbTableRow
  • créer dans cette classe une variable $_otherData qui contiendra les variables customisées. Ce tableau s’ajoute aux tableaux associatifs $_data et $_cleanData utilisés par la classe pour stocker les colonnes de la table
  • surcharger les méthodes accesseurs (__get(), __set(), __isset() et __unset()) de l’objet Zend_Db_Table_Row pour qu’il vérifie si une variable donnée existe dans le tableau $_otherData en plus du tableau $_data

Mais l’auteur ne livrait pas le code correspondant, j’ai donc suivi ses recommandations et je vous livre ci-dessous le code de ma classe :

<?php
class My_DbTableRow extends Zend_Db_Table_Row_Abstract
{
    protected $_otherData = array();

    function __set($var, $value)
    {
        if (in_array($var,$this->getTable()->info('cols')))
        {
            parent::__set($var, $value);
        }
        else
        {
            $this->_otherData[$var] = $value;
        }
    }

    function __get($var)
    {
        if (isset($this->_data[$var]))
        {
            return parent::__get($var);;
        }
        else
        {
            return $this->_otherData[$var] ;
        }
    }

    function __isset($var)
    {
        if (isset($this->_otherData[$var]))
        {
            return true;
        }
        else
        {
            return parent::__isset($var);
        }
    }

    function __unset($var)
    {
        if (isset($this->_otherData[$var]))
        {
            unset ($this->_otherData[$var]);
        }
        else
        {
            parent::__unset($var);
        }
    }
}

Au passage, j’ajoute ma classe My_DbTable qui étend la classe Zend_Db_Table_Row en utilisant par défaut le nouvel objet My_DbTableRow « extensible ».

Usage

Avec ces classes, vous pouvez donc créer des objets Zend_Db_Table_Row « extensibles » et leur affecter à l’exécution des nouvelles variables qui n’existent pas dans la table. Attention, vous ne pourrez évidemment pas sauver ces propriétés dans la base de données.

Pour revenir à mon exemple de code initial, voici comment je pourrais écrire mon code :

// Fichier Articles.php
class Articles extends My_DbTable {
    protected $_rowClass = 'Article';
}

class Article extends My_DbTableRow {
    function getMagazine()
    {
        // On vérifie si une variable a déjà été définie avec le même nom que la fonction et on l'instancie si besoin
        if (!isset($this->getMagazine))
        {
            // On peut utiliser la méthode magique find*() si on a défini 
            // les relations entre les tables $this->findMagazines();
            $this->getMagazine = // code pour instancer le magazine
        }
        return $this->getMagazine;
    }
}

Ainsi lorsque j’appellerai la méthode $article->getMagazine(), il n’instanciera qu’une seule fois l’objet magazine, et me renverra le contenu de la variable getMagazine les fois suivantes.

N’hésitez pas à me signaler toute erreur ou suggestion d’amélioration.

Outil de référencement professionnel - essai gratuit Ce contenu a été publié dans Développement PHP, avec comme mot(s)-clé(s) , , , , . Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *