2010-07-08 20 views
0

Vous cherchez à savoir s'il existe un moyen d'implémenter le type de fonctionnalité de la méthode magique __get(), mais en essayant d'instancier une nouvelle classe.Existe-t-il une méthode magique __get équivalente pour instancier des classes?

Mon but est d'avoir

$foo = new Cat();

ont finalement comme conséquence la création d'un objet générique, à savoir

new Mammal('Cat');

tel que $foo serait une instance de la classe Mammal, avec le nom de la classe appelée ('Cat') passé en argument au constructeur Mammal.


Note: Le jeu final je pense, pour ceux qui sont familiers avec le lithium ou CakePHP, est d'éviter d'avoir à définir un tas de classes pour chaque table de base de données. Si je le faisais, la plupart seraient simplement vides, les opérations de base de CRUD étant tout ce qui est nécessaire. De plus, toutes ces définitions d'inclusion et de classe ne peuvent pas être géniales pour les frais généraux. Mon idée serait d'utiliser une seule classe "Model" pour gérer la plupart des fonctions génériques, et je pourrais toujours créer des sous-classes si j'avais besoin de fonctionnalités plus avancées pour une table de base de données spécifique.

Répondre

2

Je ne pense pas qu'il existe un moyen simple et non-hacky pour réaliser ce que vous voulez avec le mécanisme d'instanciation de classe.

Je vous conseille d'utiliser une usine:

$tableFactory->createTable('Cat'); 

qui décideraient ce qui doit être fait dans les coulisses. Cela a également l'avantage de si vous décidez que Cat a besoin de sa propre classe, vous pouvez simplement modifier l'usine et vous n'avez pas besoin de modifier le code qui l'utilise.

+0

Cela fonctionnerait certainement, et pourrait être ma seule solution. Je voulais juste pouvoir écrire "new Cat()". pour la lisibilité, mais ce n'est pas essentiel. Ce serait bien cependant ... –

+0

Devrait probablement être statique - par ex. 'TableFactory :: createTable ('Cat');'. Sinon bonne réponse, le modèle d'usine est celui à utiliser ici. – Pete

0

Eh bien, il est un peu hacky, mais vous pouvez abuser du class autoloader ...

Ainsi, pirater le chargeur pour vérifier une classe « Cat », si elle n'existe pas, eval alors dans existance ...

if (!$foundClass) { 

    $code = 'Class '.$class.' extends Mammal { 
     public function __construct() { parent::__construct("'.$class.'");} 
    }'; 
    eval($code); 
} 

je l'ai dit qu'il était aki ... Mais vous pouvez toujours liste blanche les classes que vous voulez faire pour ... de plus, il a l'avantage que si vous vouliez modifier la classe , créez juste une instance ferme de celui-ci.

Mais là encore, je trouverais personnellement une autre solution. Pour moi, new Cat() n'est pas lisible du tout. C'est pour deux raisons, d'abord il n'y a pas d'indices contextuels quant à ce que c'est et ensuite je ne trouve pas la classe Cat ... Ce que je ferais est similaire à ce que Jani a suggéré: $table = DatabaseTable::getInstance('cat'); ... Je trouve que c'est beaucoup plus lisible (même si c'est plus de caractères) ...

0

La façon dont je me souviens de Cake est que vous définissez le modèle au-dessus de la classe de contrôleur, puis utilisez simplement $ this-> Modelname. Cela devrait être aussi simple à mettre en œuvre:

public function __get($prop) 
{ 
    if(in_array($prop, $this->uses)) { 
     return new $prop; 
    } 
} 

Chaque fois que vous appelez une propriété non-existante, votre classe vérifierait si le nom de la propriété existe dans un tableau des utilisations de $ et si oui, assume $ prop est classname et l'instaure.Vous voudrez stocker l'instance quelque part pour éviter de la réinstancier chaque fois que vous la récupérez.

Ceci est légèrement plus propre que d'écrire new Klass partout, parce que cela rend difficile d'échanger Klass pour autre chose. Vous le fixez dans le contrôleur alors. C'est une dépendance que vous voulez éviter. Cela dit, vous voudrez peut-être jeter un oeil à la Symfony Dependency Injection framework.

0

Je sais que c'est une vieille question maintenant mais toujours pertinente aujourd'hui pour beaucoup de gens.

Ma façon de surmonter ce scénario exact est d'avoir un modeleur de base qui contient toutes les interactions de table de base:

<?php 
    class base { 

     /** 
     * @name  $table_values 
     * @description This holds all data about the table including the field names and data for the record loaded. 
     * @example  { 
     *     "primary_keys" : [], 
     *     "table_data" : { 
     *           "name" : "value", 
     *           "name" : "value" 
     *          } 
     *    } 
     */ 
     private $table_values = array(); 

     /** 
     * @name  __get 
     * @param  $name The name of the property to return. 
     * @description The PHP magic getter. 
     */ 
     public function __get($name) { 
      $keys = array_keys($this->table_values['table_data']); 
      if (in_array($name, $keys)) { 
       return $this->table_values['table_data'][$name]; 
      } 
     } 

     /** 
     * @name  __set 
     * @param  $name The name of the property to set. 
     * @param  $value The value to assign to the property. 
     * @description The PHP magic setter. 
     */ 
     public function __set($name, $value) { 
      $this->table_values['table_data'][$name] = $value 
     } 

     /** 
     * @name  table_name 
     * @description Must override in child class. Should return the name of the table to use. 
     */ 
     public function table_name() {} 

     /** 
     * @name  load 
     * @param  $ids int|array Can be a single primary key or an assoc array of primary keys depending on the table with the keys for the array being the field names. 
     * @description This method handles loading a single record from the table and storing it as a PHP object. 
     */ 
     public function load($ids) { 
      // Use the primary key(s) to find and load the record into $table_values from the database. 
     } 

     /** 
     * @name  save 
     * @description Saves the record in the database 
     public function save() { 
      // Use the primary key(s) to find and either insert or update the database table record. 
     } 
    } 
?> 

Utilisez la classe de base comme:

<?php 
    class person extends base { 
     public function table_name() { 
      return "person"; 
     } 
    } 

    class car extends base { 
     public function table_name() { 
      return "car"; 
     } 
    } 
?> 

etc.

Utilisez et autoloader pour charger automatiquement les classes et gérer lorsqu'une classe n'existe pas pour une table:

<?php 
    spl_autoload_register(function($class_name) { 
     $filename = "/path/to/class/{$class_name}.php"; 
     if (class_exists($class_name)) { 
      // Do nothing. 
     } elesif (file_exists($filename)) { 
      require_once($filename); 
     } else { 
      // Check if the class name exists in the database as a table. 
      if ($database->table_exists($class_name)) { 
       $class_def = " 
class {$class_name} extends base { 
    public function table_name() { 
     return \"{$class_name}\"; 
    } 
}"; 
       eval($class_def); 
      } 
     } 
    }); 
?> 

La classe de base peut avoir beaucoup plus de fonctionnalités. J'ai aussi des fonctions à remplacer dans les enfants pour faire des choses avant et après charger et enregistrer, et pour vérifier les enregistrements en double avec les mêmes données par exemple.