2008-11-12 2 views
67

Я создаю библиотеку ORM с повторным использованием и простотой; все идет хорошо, за исключением того, что я застрял от глупого ограничения наследования. Пожалуйста, обратите внимание код ниже:Получение имени дочернего класса в родительском классе (статический контекст)

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(); 

Очевидно, что это не поведение я ожидал (хотя реальное поведение также имеет смысл) .. Так что мой вопрос, если вы, ребята, знаете о среднем получить, в родительский класс, имя дочернего класса.

ответ

72

в общем. это невозможно. в php4 вы можете реализовать ужасный взлом (проверьте debug_backtrace()), но этот метод не работает в PHP5. Ссылки:

редактировать: пример позднего статического связывания в PHP 5.3 (упоминается в комментариях). заметьте, что в его текущей реализации есть потенциальные проблемы (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

Да, я просто прочитал про `debug_backtrace()` .. Возможным решением было бы использовать * позднюю статическую привязку * из PHP 5.3, но это не возможно в моем случае. Спасибо. – saalaa 2008-11-12 06:13:56

2

Возможно, вы пытаетесь использовать одноэлементный шаблон в качестве заводского шаблона. Я бы рекомендовал оценить ваши дизайнерские решения. Если одноэлемент действительно подходит, я бы также рекомендовал использовать статические методы, где наследование равно , а не.

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

.. нет. Вы не понимали, что я пытаюсь болеть, но это потому, что я не объяснил это хорошо :). На самом деле, я просто пытаюсь предоставить статический метод (реализованный в BaseModel), чтобы получить() экземпляр данной модели (может быть, это пользователь, роль или что-то еще). Я обновлю вопрос. – saalaa 2008-11-12 06:19:18

+0

Хорошо, обновлено. Конечно, класс BaseModel имеет гораздо больше методов, в том числе некоторые, чтобы отслеживать, что изменилось в объекте, и UPDATE только то, что было сделано, и т. Д. Но спасибо вам все равно :). – saalaa 2008-11-12 06:28:53

2

Может быть, это на самом деле не отвечая на вопрос, но вы можете добавить параметр, чтобы получить() задав тип. тогда вы можете позвонить по телефону

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

вместо вызова пользователя :: get(). Вы можете добавить логику в BaseModel :: get(), чтобы проверить, существует ли метод get в подклассе, а затем вызывать это, если вы хотите разрешить подкласс переопределять его.

В противном случае единственный способ, которым я могу думать, очевидно, добавляя материал к каждому подклассу, который глупо:

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); 

Это, вероятно, сломаться, если вы тогда подклассы пользователя, но я полагаю, вы могли бы заменить get_parent_class(__CLASS__) с 'BaseModel' в этом случае

+0

Собственно, да. Это ограничение PHP имело большое преимущество, чтобы заставить меня рассмотреть мой дизайн. Это будет гораздо больше похоже на $ connection-> get ('User', 24); поскольку он позволяет одновременно использовать несколько соединений и также семантически более корректен. Но у вас есть точка :). – saalaa 2008-11-13 03:19:35

0

проблема не ограничение языка, это ваш дизайн. Неважно, что у вас есть занятия; статические методы опровергают процедурный, а не объектно-ориентированный дизайн. Вы также используете глобальное состояние в той или иной форме. (Как get_row_from_db_as_array() знает, где найти базу данных?) И, наконец, очень сложно провести единичный тест.

Попробуйте что-нибудь в этом направлении.

$db = new DatabaseConnection('dsn to database...'); 
$userTable = new UserTable($db); 
$user = $userTable->get(24); 
145

Вам не нужно ждать PHP 5.3, если вы в состоянии представить себе способ сделать это за пределами статического контекста. В php 5.2.9, в не статический метод родительского класса, вы можете сделать:

get_class($this); 

и возвращает имя класса ребенка в виде строки.

т.е.

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

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

$x = new Child(); 

это Выведет:

Parent class: Parent 
Child class: Child 

сладкий да?

5

Если вы не хотите использовать get_called_class(), вы можете использовать другие трюки поздней статической привязки (PHP 5.3+). Но недостатком в этом случае вам должен быть метод getClass() в каждой модели. Что немаловажно, ИМО.

<?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

Две вариации ответа Престон:

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); 
    } 
} 

Примечание: начиная имя свойства с _ это соглашение, что в основном означает " я знаю, что сделал это публично, но это действительно должно было быть защищено, но я не мог этого сделать и достичь своей цели »

Смежные вопросы