Существует ли магический метод __get для создания экземпляров классов? - PullRequest
0 голосов
/ 09 июля 2010

Нужно посмотреть, есть ли какой-нибудь способ реализовать функциональность магического метода __get(), но при попытке создать новый класс.

Моя цель -

$foo = new Cat();

В конечном итоге приводит к созданию универсального объекта, то есть

new Mammal('Cat');

Так, что $foo будет экземпляром класса Mammal с именем вызываемого класса ('Cat'), передаваемым в качестве аргумента конструктору Mammal.


Примечание: Конечная игра, которую я имею в виду для тех, кто знаком сLithium или CakePHP, чтобы избежать необходимости определять целую кучу классов для каждой отдельной таблицы базы данных.Если бы я это сделал, большинство из них были бы просто пустыми, а основные операции CRUD - это все, что необходимо.Кроме того, все эти включения и определения классов не могут быть хорошими для накладных расходов.Моя идея состояла бы в том, чтобы использовать один класс «Модель» для обработки большинства универсальных функций, и я всегда мог создавать подклассы, если мне требовались более расширенные функциональные возможности для конкретной таблицы базы данных.

Ответы [ 4 ]

2 голосов
/ 09 июля 2010

Я не думаю, что есть какой-то простой нехакерский способ достичь того, что вы хотите, с помощью механизма создания классов.

Я бы порекомендовал использовать фабрику:

$tableFactory->createTable('Cat');

Что бы потом решить, что нужно делать за кулисами. Это также имеет то преимущество, что если вы когда-нибудь решите, что Cat требуется собственный класс, вы можете просто изменить фабрику, и вам может не потребоваться изменять код, который ее использует.

0 голосов
/ 11 октября 2016

Я знаю, что это старый вопрос сейчас, но все еще актуален сегодня для многих людей.

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

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

Используйте базовый класс как:

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

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

и т.д.

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

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

Базовый класс может иметь гораздо больше функциональности. У меня также есть функции, которые можно переопределить в дочерних элементах, чтобы выполнять действия до и после загрузки и сохранения, а также проверять наличие дублирующихся записей, например, с такими же данными.

0 голосов
/ 09 июля 2010

Насколько я помню из Cake, вы определяете модель поверх класса контроллера, а затем просто используете $ this-> Modelname. Это должно быть так просто реализовать как:

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

Каждый раз, когда вы вызываете несуществующее свойство, ваш класс проверяет, существует ли имя свойства в массиве, который использует $, и, если это так, предполагает, что $ prop является именем класса, и создает его. Вы захотите хранить экземпляр где-нибудь, чтобы избежать повторного создания экземпляра при каждом его извлечении.

Это несколько чище, чем писать new Klass повсюду, потому что это затрудняет обмен Klass на что-то другое. Тогда вы подключаете его к контроллеру. Это зависимость, которую вы хотите избежать. С учетом вышесказанного вы, возможно, захотите взглянуть на инфраструктуру внедрения зависимостей Symfony .

0 голосов
/ 09 июля 2010

Ну, это немного хакерски, но вы можете злоупотребить автозагрузчиком класса ...

Итак, взломайте загрузчик, чтобы проверить наличие класса "Cat", если он нене существует, а затем оцените его в существовании ...

if (!$foundClass) {

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

Я сказал, что это было хакерским ... Но вы всегда можете внести в белый список те классы, для которых вы хотите сделать это ... Плюс, у него естьПреимущество в том, что если вы хотите изменить класс, просто создайте его экземпляр.

Но опять же, я бы лично нашел другое решение.Для меня new Cat() вообще не читается.Это из-за двух причин: во-первых, нет никаких контекстных подсказок относительно того, что это такое, а во-вторых, я не могу найти класс Cat ... То, что я сделал бы, похоже на то, что предложил Яни: $table = DatabaseTable::getInstance('cat'); ... Я нахожу, что НАМНОГОболее читабельно (хотя в нем больше символов) ...

...