Создавать классы модели базы данных динамически - PullRequest
3 голосов
/ 03 августа 2011

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

Информация, чтобы понять вопрос:

Вот класс, который я написал (где connect.php загружает учетные данные базы данных; оболочка для PHP PDO, хранящаяся в $db; и Base.php):

<?php
    require_once('connect.php');

    class Advertiser extends Base
    {
        public static function getByID($id)
        {
            global $db;
            $sql = "SELECT * FROM advertiser WHERE advertiserid=?";
            $values = array($id);
            $res = $db->qwv($sql, $values);

            return Advertiser::wrap($res);
        }

        public static function add($name)
        {
            $adv = new Advertiser(null, $name);
            $res = $adv->save();

            return $res;
        }

        public static function wrap($advs)
        {
            $advList = array();
            foreach( $advs as $adv )
            {
                array_push($advList, new Advertiser($adv['advertiserid'], $adv['name']));
            }

            return Advertiser::sendback($advList);
        }

        private $advertiserid;
        private $name;

        public function __construct($advertiserid, $name)
        {
            $this->advertiserid = $advertiserid;
            $this->name = $name;
        }

        public function __get($var)
        {
            return $this->$var;         
        }

        public function save()
        {
            global $db;
            if( !isset($this->advertiserid) )
            {
                $sql = "INSERT INTO advertisers (name) VALUES(?)";
                $values = array($this->name);
                $db->qwv($sql, $values);

                if( $db->stat() )
                {
                    $this->advertiserid = $db->last();
                    return $this;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                $sql = "UPDATE advertisers SET name=? WHERE advertiserid=?";
                $values = array ($this->name, $this->advertiserid);
                $db->qwv($sql, $values);

                return $db->stat();
            }
        }
    }
?>

Как вы можете видеть, он имеет довольно стандартные функции CRUD ( Edit : Хорошо, так что только CRU, в этой реализации). Иногда я расширяю такой класс, добавляя больше функций, для которых и предназначены эти классы. Например, я мог бы добавить следующую функцию в этот класс (при условии, что я добавлю столбец isBanned в базу данных):

public static function getBanned()
{
    global $db;
    $sql = "SELECT * FROM advertiser WHERE isBanned=1";
    $res = $db->q($sql);

    return Advertiser::wrap($res);
}

Вопрос:

Как я могу создать универсальный класс, который также будет загружать пользовательские классы моделей, когда они присутствуют и необходимы?

Например, если я напишу следующий код:

$model = new Catchall();
$banned = $model->Advertiser::getByID(4);

Я бы ожидал, что мой класс catchall изменит свои запросы так, чтобы все ссылки на таблицы / столбцы были такими, какие я выбрал (* в нашем случае Advertiser), но в нижнем регистре.

Кроме того, если бы я хотел создать пользовательскую функцию, подобную той, которую я написал выше, я бы ожидал, что мой класс catchall определит, что файл существует в его пути (ранее определенном, конечно) с именем, которое я имею указан (Advertisers.php, в данном случае) и загрузите его.

Advertisers.php будет extends Catchall и будет содержать только мои пользовательские функции.

Таким образом, я мог бы иметь один класс Catchall, который работал бы для всех функций CRUD, и иметь возможность легко расширять произвольные классы по мере необходимости.

  1. Какие идеи / концепции мне нужно понять, чтобы сделать это?
  2. Где я могу найти примеры этого уже в дикой природе, , не копаясь в большом количестве исходного кода CodeIgniter или Zend ?
  3. Что я пытаюсь сделать называется ?

Ответы [ 3 ]

3 голосов
/ 03 августа 2011

General Stuff : я бы посмотрел в Doctrine2 примеры того, как они делают ORM в PHP.Они используют отображение на языке разметки, чтобы сказать: в этой таблице есть эти столбцы этого типа.Также, хотя и не на PHP, Django ORM очень прост в использовании и понимании, и проработка этого урока в течение примерно 20 минут действительно откроет вам глаза на некоторые изящные возможности.(это помогло мне)

Быстрый поиск "php active record lightweight" вернул несколько интересных примеров, которые могут помочь вам выбрать правильный путь.

PHP Ideas :Я хотел бы взглянуть на магический метод получения и установки в php, __GET и __SET, который позволит вам устанавливать значения для ваших объектов без необходимости создания метода получения / установки для каждого поля каждой таблицы.Вы можете создать один __SET, чтобы убедиться, что поле set является полем в этой таблице, и добавить его в список «полей для обновления» при следующем сохранении объекта.НО, это не очень хорошая идея в долгосрочной перспективе, так как она быстро выходит из-под контроля и хрупка.

Совет : Наконец, я работала в компании, которая использовала систему, котораявыглядит примерно так, и я могу однозначно сказать, что вы не хотите пытаться масштабировать этот долгосрочный период.Подобная система (шаблон активной записи) может сэкономить огромное количество времени без необходимости писать запросы и т. Д., Но может стоить немало времени, если вы когда-нибудь захотите запустить бизнес-логику модульного тестирования в классы объектов.

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

Хотя они могут работать немного медленнее, чем ваш код выше, если вы планируете использовать это в течение значительного времени, попробуйте изучить инструменты ORM.

Редактировать Это называется активной записью.

1 голос
/ 03 августа 2011

Есть пара различных шаблонов дизайна для того, что вы пытаетесь сделать. Посмотрите на Data Mapper и Active Record .

0 голосов
/ 03 августа 2011

Используя "магический метод" PHP __get, вы можете создать эту функцию, когда получите к ней доступ через:

$model = new Catchall();
$banned = $model->Advertiser->getByID(4);

... она а) проверит наличие класса Advertiserуже определен, б) проверить файл с именем Advertiser.php и включить его, или в) вернуть новый экземпляр универсального класса.

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

См. Документы: http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods

Быстрый и грязный пример:

    public function __get($name) {

            $instance = false;
            // see if there is already a class with the requested name
            if (class_exists($name)) {
                $instance = new $name();
            }

            // check to see if there is an object def for the requested object
            if ($instance === false && file_exists(PATH_TO_OBJECTS.$name.'.php')) {
                require_once(PATH_TO_OBJECTS.$name.'.php');
                $instance = new $name();
            }

            // if instace is still not found, load up a generic
            if ($instance === false)
                $instance = new Catchall($name);

        return $instance;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...