Я видел различные рамки MVC, а также автономные рамки ORM для PHP, а также другие вопросы ORM. тем не менее, большинство вопросов требуют начала существующих фреймворков, чего я не ищу. (Я также прочитал этот вопрос , но я не уверен, что с ним делать, поскольку ответы расплывчаты.)
Вместо этого я решил, что лучше всего научусь, когда мои руки станут грязными и на самом деле напишу свой собственный ORM, даже простой. Кроме того, я не знаю, как начать, тем более что код, который я вижу в других ORM, настолько сложный.
С моим PHP 5.2.x (это важно) MVC framework У меня есть базовый уровень абстракции базы данных, который имеет:
connect($host, $user, $pass, $base)
, query($sql, $binds)
и т. Д. Но не имеет:
EDIT: уточнить, у меня есть только уровень абстракции базы данных. У меня пока нет моделей, но когда я их реализую, я хочу, чтобы они были родными ORM-моделями (так сказать), следовательно, этот вопрос.
Я немного ознакомился с ORM, и, насколько я понимаю, они предоставляют средства для дальнейшей абстрактной модели данных из самой базы данных, представляя данные как не что иное, как классы / объекты на основе PHP; снова, исправьте меня, если я ошибаюсь или что-то пропустил.
Тем не менее, я хотел бы получить несколько простых советов от кого-либо еще, кто больше или меньше использовал рамки ORM. Есть ли что-нибудь еще, что мне нужно принять к сведению, простые, академические образцы, на которые я могу ссылаться, или ресурсы, которые я могу прочитать?
Поскольку этот вопрос довольно старый, я думаю, вы уже пытались написать 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 в Doctrine или 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 в своем компоненте Persistent Object в качестве конфигурационного формата. То, что я использовал, тоже казалось естественным выбором, и CMS, с которым я работал, также использовал формат конфигурации PHP.
С этой конфигурацией вы определяете
И это информация, которую вы используете в своем Data Mapper для сопоставления результата DB с объектами.
Реализация
Я решил пойти с сильным подходом к тестированию из-за сложного характера написания пользовательской ORM. TDD или нет, написав много, много модульных тестов – действительно хорошая идея для такого проекта. Кроме того: держите руки грязными и храните книгу Фаулеров близко. 😉
Поскольку я сказал, что это действительно стоило усилий, но я бы не хотел этого делать снова, из-за зрелых фреймворков, которые существуют в наши дни.
Я больше не использую свой ORM, это сработало, но не хватало многих функций: ленивая загрузка, сопоставление компонентов, поддержка транзакций, кеширование, пользовательские типы, подготовленные операторы / параметры и т. Д. И это было не так хорошо для использования в крупных проектах.
Тем не менее, я надеюсь, что могу дать вам несколько отправных точек в области ORM, если вы их не знали. 😉
Простую 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()
magic для получения результатов другими полями, кроме, например, id
.
Имейте в виду, что пример, который я дал, не обрабатывает отношения, поэтому различные реализации ORM действительно различаются, однако я обычно не доверяю ORM для обработки отношений для меня, поскольку они имеют тенденцию быть более медленными и не создают эффективных запросов.