2008-11-12 8 views
67

Ich baue eine ORM-Bibliothek mit Wiederverwendung und Einfachheit; Alles geht gut, außer dass ich an einer dummen Vererbungsbeschränkung festhalte. Bitte beachten Sie den Code unten:Abrufen des Namens einer untergeordneten Klasse in der übergeordneten Klasse (statischer Kontext)

class BaseModel { 
    /* 
    * Return an instance of a Model from the database. 
    */ 
    static public function get (/* varargs */) { 
     // 1. Notice we want an instance of User 
     $class = get_class(parent); // value: bool(false) 
     $class = get_class(self); // value: bool(false) 
     $class = get_class();  // value: string(9) "BaseModel" 
     $class = __CLASS__;  // value: string(9) "BaseModel" 

     // 2. Query the database with id 
     $row = get_row_from_db_as_array(func_get_args()); 

     // 3. Return the filled instance 
     $obj = new $class(); 
     $obj->data = $row; 
     return $obj; 
    } 
} 

class User extends BaseModel { 
    protected $table = 'users'; 
    protected $fields = array('id', 'name'); 
    protected $primary_keys = array('id'); 
} 
class Section extends BaseModel { 
    // [...] 
} 

$my_user = User::get(3); 
$my_user->name = 'Jean'; 

$other_user = User::get(24); 
$other_user->name = 'Paul'; 

$my_user->save(); 
$other_user->save(); 

$my_section = Section::get('apropos'); 
$my_section->delete(); 

Offensichtlich ist dies nicht das Verhalten, das ich erwartet hatte (obwohl das tatsächliche Verhalten ist auch sinnvoll) .. Also meine Frage ist, ob ihr von einem mittleren kennen zu lernen, in der Elternklasse, der Name der Kindklasse.

Antwort

72

kurz. das ist nicht möglich. In PHP4 könnte man einen schrecklichen Hack implementieren (siehe debug_backtrace()), aber diese Methode funktioniert nicht in PHP5. Referenzen:

bearbeiten: ein Beispiel für späte statische Bindung in PHP 5.3 (in den Kommentaren erwähnt). Beachten Sie, dass es in der aktuellen Implementierung potentielle Probleme gibt (src).

class Base { 
    public static function whoAmI() { 
     return get_called_class(); 
    } 
} 

class User extends Base {} 

print Base::whoAmI(); // prints "Base" 
print User::whoAmI(); // prints "User" 
+0

Ja, ich 'debug_backtrace nur lesen()' .. Eine mögliche Lösung * später statische Bindung * von PHP 5.3, aber das ist nicht eine Möglichkeit, in meinem Fall wäre die Verwendung. Vielen Dank. – saalaa

2

Es scheint, dass Sie versuchen, ein Singleton-Muster als Fabrikmuster zu verwenden. Ich würde empfehlen, Ihre Designentscheidungen zu bewerten. Wenn ein Singleton wirklich geeignet ist, würde ich auch empfehlen, nur statische Methoden zu verwenden, bei denen die Vererbung nicht gewünscht ist.

class BaseModel 
{ 

    public function get() { 
     echo get_class($this); 

    } 

    public static function instance() { 
     static $Instance; 
     if ($Instance === null) { 
      $Instance = new self; 

     } 
     return $Instance; 
    } 
} 

class User 
extends BaseModel 
{ 
    public static function instance() { 
     static $Instance; 
     if ($Instance === null) { 
      $Instance = new self; 

     } 
     return $Instance; 
    } 
} 

class SpecialUser 
extends User 
{ 
    public static function instance() { 
     static $Instance; 
     if ($Instance === null) { 
      $Instance = new self; 

     } 
     return $Instance; 
    } 
} 


BaseModel::instance()->get(); // value: BaseModel 
User::instance()->get();  // value: User 
SpecialUser::instance()->get(); // value: SpecialUser 
+0

.. nein. Du hast nicht verstanden, was ich versucht habe, aber das ist, weil ich es nicht gut erklärt habe :). Eigentlich versuche ich nur, eine statische Methode (implementiert in BaseModel) zu liefern, um() eine Instanz eines gegebenen Modells zu erhalten (sei es Benutzer, Rolle oder was auch immer). Ich werde die Frage aktualisieren .. – saalaa

+0

Ok, aktualisiert. Natürlich hat die BaseModel-Klasse viel mehr Methoden, einschließlich einiger, um zu verfolgen, was im Objekt geändert wurde und UPDATE nur, was sich geändert hat, usw. Aber danke trotzdem :). – saalaa

2

Vielleicht ist das nicht wirklich die Frage zu beantworten, aber man könnte ein Parameter() specifing die Art zu erhalten hinzuzufügen. dann können Sie anrufen

BaseModel::get('User', 1); 

anstelle von Aufruf von User :: get(). Sie könnten Logik in BaseModel :: get() hinzufügen, um zu überprüfen, ob eine get-Methode in der Unterklasse vorhanden ist, und sie dann aufrufen, wenn Sie zulassen möchten, dass die Unterklasse diese überschreibt.

Ansonsten ist der einzige Weg, ich offensichtlich denken kann, ist von Sachen zu jeder Unterklasse hinzufügen, die dumm ist:

class BaseModel { 
    public static function get() { 
     $args = func_get_args(); 
     $className = array_shift($args); 

     //do stuff 
     echo $className; 
     print_r($args); 
    } 
} 

class User extends BaseModel { 
    public static function get() { 
     $params = func_get_args(); 
     array_unshift($params, __CLASS__); 
     return call_user_func_array(array(get_parent_class(__CLASS__), 'get'), $params); 
    } 
} 


User::get(1); 

Dies würde wahrscheinlich brechen, wenn Sie dann auf Benutzer subclassed, aber ich nehme an, Sie get_parent_class(__CLASS__) ersetzen könnten mit 'BaseModel' in diesem Fall

+0

Eigentlich ja. Diese PHP-Beschränkung hatte den großen Vorteil, mich dazu zu zwingen, mein Design zu überprüfen. Es wird viel mehr wie $ connection-> get ('User', 24) aussehen; da es mehrere Verbindungen gleichzeitig erlaubt und auch semantisch korrekter ist. Aber du hast einen Punkt :). – saalaa

+0

array_shift scheint langsam, weil es jeden Schlüssel des Arrays ändern muss ... Nur $ args [0] stattdessen; – Xesau

0

Das Problem ist keine Sprachbeschränkung, es ist Ihr Design. Egal, dass Sie Klassen haben; Die statischen Methoden sind eher prozedural als objektorientiert. Sie verwenden auch den globalen Status in irgendeiner Form. (Wie kann get_row_from_db_as_array() wissen, wo die Datenbank zu finden ist?) Und schließlich sieht es sehr schwierig zu Unit-Test.

Versuchen Sie etwas in diese Richtung.

145

Sie müssen nicht auf PHP 5.3 warten, wenn Sie sich einen Weg vorstellen können, dies außerhalb eines statischen Kontexts zu tun. In PHP 5.2.9, in einem nicht-statische Methode der übergeordneten Klasse, können Sie tun:

get_class($this); 

und es wird den Namen des Kindes Klasse als String zurück.

heißt

class Parent() { 
    function __construct() { 
     echo 'Parent class: ' . get_class() . "\n" . 'Child class: ' . get_class($this); 
    } 
} 

class Child() { 
    function __construct() { 
     parent::construct(); 
    } 
} 

$x = new Child(); 

dieser Wille Ausgang:

Parent class: Parent 
Child class: Child 

süß oder?

+10

Wenn Sie abstrakte Klassen verwenden, ist dies ein Muss. –

+0

schön! cooler Trick! –

+0

Das verdient mehr Upvotes! – Mattisdada

5

Falls Sie nicht möchten, dass get_called_class() verwenden Sie andere Tricks in der letzten Zeit statisch verwenden können Bindung (PHP 5.3+). Aber der Nachteil in diesem Fall müssen Sie getClass() -Methode in jedem Modell haben. Was ist keine große Sache IMO.

<?php 

class Base 
{ 
    public static function find($id) 
    { 
     $table = static::$_table; 
     $class = static::getClass(); 
     // $data = find_row_data_somehow($table, $id); 
     $data = array('table' => $table, 'id' => $id); 
     return new $class($data); 
    } 

    public function __construct($data) 
    { 
     echo get_class($this) . ': ' . print_r($data, true) . PHP_EOL; 
    } 
} 

class User extends Base 
{ 
    protected static $_table = 'users'; 

    public static function getClass() 
    { 
     return __CLASS__; 
    } 
} 

class Image extends Base 
{ 
    protected static $_table = 'images'; 

    public static function getClass() 
    { 
     return __CLASS__; 
    } 
} 

$user = User::find(1); // User: Array ([table] => users [id] => 1) 
$image = Image::find(5); // Image: Array ([table] => images [id] => 5) 
0

Zwei Variationen über Preston Antwort:

1)

class Base 
{ 
    public static function find($id) 
    { 
     $table = static::$_table; 
     $class = static::$_class; 
     $data = array('table' => $table, 'id' => $id); 
     return new $class($data); 
    } 
} 

class User extends Base 
{ 
    public static $_class = 'User'; 
} 

2)

class Base 
{ 
    public static function _find($class, $id) 
    { 
     $table = static::$_table; 
     $data = array('table' => $table, 'id' => $id); 
     return new $class($data); 
    } 
} 

class User extends Base 
{ 
    public static function find($id) 
    { 
     return self::_find(get_class($this), $id); 
    } 
} 

Hinweis: einen Eigenschaftsnamen, beginnend mit _ ist eine Konvention, die im Grunde bedeutet, " Ich weiß, dass ich dies öffentlich gemacht habe, aber es hätte wirklich geschützt werden sollen, aber ich konnte das nicht tun und mein Ziel erreichen "