Советы/ресурсы/шаблоны для обучения внедрению базовой ORM

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

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

С моей PHP 5.2.x(это важно) MVC framework У меня есть базовый уровень абстракции базы данных, который имеет:

  • Очень простые методы, такие как connect($host, $user, $pass, $base), query($sql, $binds) и т.д.
  • Подклассы для каждой СУБД, которые она поддерживает
  • Класс (и соответствующие подклассы) для представления наборов результатов SQL

Но не имеет:

  • Функция Active Record, которую я предполагаю, является ORM (поправьте меня, если я ошибаюсь)

EDIT:, чтобы уточнить, у меня есть только уровень абстракции базы данных. У меня пока нет моделей, но когда я их реализую, я хочу, чтобы они были родными ORM-моделями (так сказать), следовательно, этот вопрос.

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

Тем не менее, я хотел бы получить несколько простых советов от кого-либо, кто больше или меньше занимался рамками ORM. Есть ли что-то еще, что мне нужно принять к сведению, простые, академические образцы для меня, или ресурсы, которые я могу прочитать?

Ответ 1

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

Как я сказал, два года назад я реализовал пользовательскую ORM и даже использовал ее с небольшим успехом в проектах малого и среднего размера. Я включил его в довольно популярную CMS, которая в то время (и даже сейчас) не обладает такой функциональностью ORM. Кроме того, в то время популярные структуры, такие как Doctrine, не меня действительно убедили. С тех пор многое изменилось, и Doctrine 2 развился в прочной структуре, поэтому, если бы у меня теперь был выбор между реализацией моего собственного ORM или использованием одного из популярных рамок, таких как Doctrine 2 для использования в производстве, это было бы совсем не вопросом - используйте существующие, стабильные решения. НО: внедрение такой структуры (простым способом) было очень ценным учебным упражнением, и это помогло мне много работать с большими ORM с открытым исходным кодом, так как вы лучше понимаете подводные камни и трудности, связанные с реляционным сопоставлением объектов.

Не сложно реализовать базовую функциональность ORM, но как только вступает в игру отображение отношений между объектами, становится намного сложнее и интереснее.


Как я начал?

Что меня зацепило, так это книга Мартина Фаулерса Шаблоны архитектуры корпоративных приложений. Если вы хотите запрограммировать свой собственный ORM или даже если вы просто работаете с какой-либо структурой ORM, купите эту книгу. Это один из самых ценных ресурсов, который охватывает многие базовые и передовые методы в области реляционного сопоставления объектов. Прочитайте это, вы получите много замечательных идей о шаблонах, стоящих за ORM.

Основная архитектура

Я решил, что хотел бы использовать скорее Active Record или какой-то Data Mapper. Это решение влияет на то, как данные из базы данных сопоставляются с сущностью. Я решил реализовать простой Data Mapper, тот же подход, что и Doctrine 2 или Hibernate в Java. Active Record - это подход функциональности ORM (если вы можете это назвать) в Zend Framework. Активная запись намного проще, чем Data Mapper, но также гораздо более ограничена. Прочитайте эти шаблоны и проверьте упомянутые структуры, вы получите разницу довольно быстро. Если вы решите пойти с Data Mapper, вы также должны прочитать API отражения PHP.

Запросы

У меня была амбициозная цель создать свой собственный язык запросов, как DQL в Доктрине или HQL в Hibernate. Вскоре я отказался от этого, так как написание пользовательского анализатора SQL/lexer казалось сложным (и это действительно так!). То, что я сделал, было реализовать Объект запроса, чтобы инкапсулировать информацию, в которую вовлечена эта таблица (что важно, поскольку вам нужно сопоставьте данные из базы данных с соответствующими классами для каждой таблицы).

Запрос для объекта в моем ORM выглядел следующим образом:

public function findCountryByUid($countryUid) {
    $queryObject = new QueryObject();
    $queryObject->addSelectFields(new SelectFields('countries', '*'))
            ->addTable(new Table('countries'))
            ->addWhere('countries.uid = "' . intval($countryUid) . '"');

    $res = $this->findByQuery($queryObject);
    return $res->getSingleResult();
}

Конфигурация

Как правило, вам также нужно иметь какой-то конфигурационный формат, Hibernate использует XML (среди прочих), Doctrine 2 использует аннотации PHP, EZComponents использует массивы PHP в своем Постоянный объект в качестве формата конфигурации. То, что я использовал, тоже казалось естественным выбором, и CMS, с которым я работал, также использовал формат конфигурации PHP.

С помощью этой конфигурации вы определяете

  • какая таблица сопоставляется с классом
  • какие поля должны быть сопоставлены с экземпляром класса
  • какой тип полей имеет таблица (int, string и т.д.)
  • отношения между объектами (например, класс User имеет ссылку на класс UserGroup)
  • и др.

И это информация, которую вы используете в своем Data Mapper для сопоставления результата DB с объектами.

Реализация

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


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

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

Тем не менее, я надеюсь, что могу дать вам несколько отправных точек в области ORM, если вы их не знали.; -)

Ответ 2

Простая ORM может быть построена с использованием __get() и __set() и нескольких настраиваемых методов (возможно, с помощью __call()), вот простой псевдокод:

class ORM
{
  private $table = null;
  private $fields = array();

  function __construct($table)
  {
    $this->table = $table;
  }

  function __get($key)
  {
    return isset($this->fields[$key]) ? $this->fields[$key] : false;
  }

  function __set($key, $value)
  {
    $this->fields[$key] = $value;
  }

  function load($id, $field = 'id')
  {
    // populate $this->fields with SELECT * FROM $this->table WHERE $field = $id;
  }

  function save()
  {
    if (isset($this->fields['id']))
    {
      // UPDATE $this->table SET $this->fields;
    }

    else
    {
      // INSERT INTO $this->table $this->fields;
    }
  }
}

$user = new ORM('user');

$user->name = 'name';
$user->pass = '1337';

$user->save();

Это просто базовый пример, чтобы вы начали. Вы можете добавить дополнительную логику с помощью магического метода __call() для получения результатов другими полями, чем id, например.

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