2008-12-17 15 views
2

J'ai une structure de base de données qui a une table Person qui contient des champs tels que le nom, email, company_id, personType et autres. Parce que toutes les personnes ne sont pas nécessairement des utilisateurs du système, j'ai une table distincte Utilisateur qui définit userName et mot de passe pour ces personnes qui sont des utilisateurs dans le système.Comment établir plusieurs héritages de tables dans le domaine à l'aide de Zend Framework?

Je le code suivant pour définir le tableau de données Passerelle pour la personne Tableau:

class Model_Table_Person extends Zend_Db_Table_Abstract 
{ 
    protected $_name = 'person'; 
    protected $_primary = 'person_id'; 

    protected $_referenceMap = array(
     'Company' => array(
      'columns' => 'company_id', 
      'refTableClass' => 'Company', 
      'refColumns' => 'id' 
     ), 
     'Store' => array(
      'columns' => 'store_id', 
      'refTableClass' => 'Store', 
      'refColumns' => 'id' 
     ) 
    ); 

    public function findByPersonType(string $personType) 
    { 
     $where = $this->getAdapter()->quoteInto('personType = ?', $personType); 
     return $this->fetchAll($where); 
    } 
} 

Ce code définit l'objet de domaine pour personne:

class Model_Person 
{ 
    protected static $_gateway; 

    protected $_person; 

    public static function init() 
    { 
     if(self::$_gateway == null) 
     { 
      self::$_gateway = new Model_Table_Person(); 
     } 
    } 

    public static function get(string $searchString, string $searchType = 'id') 
    { 
     self::init(); 

     switch($searchString) 
     { 
      case 'id': 
       $row = self::$_gateway->find($id)->current(); 
       break; 
     } 

     return self::factory($row); 
    } 

    public static function getCollection(string $searchString, string $searchType = null) 
    { 
     self::init(); 

     switch($searchType) 
     { 
      case 'userType': 
       $row = self::$_gateway->findByPersonType($searchString); 
       break; 
      default: 
       $personRowset = self::$_gateway->fetchAll(); 
       break; 
     } 

     $personArray = array(); 

     foreach ($personRowset as $person) 
     { 
      $personArray[] = self::factory($person); 
     } 

     return $personArray; 
    } 

    public function getCompany() 
    { 
     return $this->_person->findParentRow('Company'); 
    } 

    public function getStore() 
    { 
     return $this->_person->findParentRow('Store'); 
    } 

    protected static function factory(Zend_Db_Table_Row $row) 
    { 
     $class = 'Model_Person_' . ucfirst($row->personType); 
     return new $class($row); 
    } 

    // protected constructor can only be called from this class, e.g. factory() 
    protected function __construct(Zend_Db_Table_Row $personRow) 
    { 
     $this->_person = $personRow; 
    } 
} 

Enfin, j'ai une autre table Passerelle de données pour l'utilisateur:

class Model_Table_User extends Zend_Db_Table_Abstract 
{ 
    protected $_name = 'user'; 
    protected $_primary = 'person_id'; 

    public function findByUserName() 
    { 

    } 
} 

Et une classe de base qui étend la table Model_Person e comme ceci:

class Model_User extends Model_Person 
{  
    public function login() 
    { 

    } 

    public function setPassword() 
    { 

    } 
} 

Comment prolonger correctement la classe « Model_User » (qui sert un type de base pour tous les autres types d'utilisateurs mais) d'utiliser les fonctions de classe « Model_Person » qui carte à un tout de table également mappant les fonctions "Model_User" réelles pour utiliser une deuxième table?

Répondre

1

Ceci est une énorme faiblesse pour PHP (avant la version 5.3.0) - son manque de soutien pour late static binding.

C'est, quand une méthode statique, comme get() appelle une autre méthode statique telle que init(), il toujours utilise la méthode init() définie dans cette classe. Si une sous-classe définit une autre méthode init() et appelle get(), en attendant qu'elle appelle la version remplacée de init(), cela ne se produira pas.

class A 
{ 
    static $var = null; 
    static function init() { self::$var = 1234; } 
    static function get() { self::init(); } 
} 

class B extends A 
{ 
    static function init() { self::$var = 5678; } 
} 

B::get(); 
print B::$var . "\n"; 

Ceci imprime "1234" alors que vous pouvez vous attendre à imprimer "5678". C'est comme si A::get() ne sait pas que cela fait partie de la classe B. La solution de contournement a été que vous devez copier l'implémentation pour la méthode get() dans la sous-classe, même si elle ne fait rien différemment de la superclasse. Ceci est très insatisfaisant.

PHP 5.3.0 tente de résoudre ce problème, mais vous devez un peu différemment à coder get():

function get() { 
    static::init(); 
} 

PHP 5.3.0 est encore en version alpha actuellement.


Il y a quelques solutions de contournement possibles:

  • Ne pas sous-classe Person comme User, mais ajouter le login et le mot de passe des attributs à la classe Person. Si ces attributs sont NULL, alors il s'agit d'une personne qui n'est pas un utilisateur, et les fonctions et setPassword() devraient tenir compte de ceci et lancer une exception ou retourner false ou quelque chose.

  • Utilisez une méthode différente get() par sous-type: getUser(), getPerson(), etc. Chacun de ces initialiser serait son propre objet de table respectif.

+0

Voulez-vous concevoir différemment les relations entre les classes User et Person? J'ai pensé essayer cette méthode d'extension de la classe Model_Person, puis implémenter un second objet $ _gateway pour l'objet User, mais il ne semble pas que ça se passe bien. –

+0

Je pense avoir décidé d'utiliser les tables $ _referenceMap de User et Person pour définir l'utilisation des données dans la table User plutôt que d'essayer de créer une sorte d'héritage de passerelle multiple? Est-ce que cela semble plus ou moins correct? –

+0

Ouais, j'ai l'impression que le langage PHP ne supporte pas ce que vous essayez de faire. Vous pourriez avoir besoin d'une classe d'usine après tout. –