- Философия ActiveRecord
- Active Record против Data Mapper-а для сохранения данных
- Пример шаблона Active Record
- Преимущества Active Record
- Недостатки Active Record
- Пример Data Mapper-а
- Преимущества Data Mapper-а
- Недостатки Data Mapper-а
- Сервис-объекты
- Варианты использования
- DAO vs Active Record vs Data Mapper паттерны при работе с базой данных
- Паттерн DAO
- Паттерн Active Record
- Паттерн Data Mapper
Философия ActiveRecord
Сегодня в нашей заметке мы рассмотрим паттерн под названием ActiveRecord, который представляет из себя средство работы с базой данных. Сразу же попрошу профессионалов сильно такого рода заметки не критиковать. Написаны они лишь для того, чтобы заитересовать, дать стимул читать такие книженции как Agile Web Development with Ruby on Rails.
ActiveRecord правильнее даже будет назвать реализацией технологии ORM:
«ORM (англ. Object-relational mapping) — технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных»
ActiveRecord в Ruby on Rails очень меня порадовал, когда впервые с ним познакомился. Его реализации можно найти в разных веб фреймворках, как RoR, CakePHP, Castle и так далее. Идея его состоит в том, что каждая таблица базы данных превращается в класс, каждая строка таблицы в объект этого класса. ActiveRecord обеспечивает методы работы с данными каждого столбца таблицы.
Скажем, пусть у нас будет таблица с данными Хабра-населения users, тогда добавить запись о новом комраде, желающем пополнить эти стройные ряды можно будет таким образом:
x = User.new
x.name = «Роман Иванов»
x.login = «ivanov»
x.email = «no@reply.com»
x.homepage = «ivanov-thebest.com — просто лучший и все здесь»
x.save
Эти магические операции создадут и отправят в базу следующий запрос:
INSERT INTO users (name, login, email, homepage) VALUES
\ (‘Роман Иванов’, ‘ivanov’, ‘no@reply.com’, ‘ivanov-thebest.com — просто лучший и все здесь’);
С не меньшим успехом можно искать и выбирать записи из базы таким образом:
#найдем всех пользователей
f = User.find(:all)
#найдем пользователя с логином ivanov
search_str = «ivanov»
f = User.find(:first, :conditions => [«login = ?», search_str])
Такой прием автоматически защитит нас от SQL-инъекции (это будет сделано Рельсам за нас в варианте приведенном выше), а также сгенерирует следующий запрос:
SELECT * FROM users WHERE login = ‘ivanov’
Благодаря Рельсам и их заботливым разработчикам приведенное выше можно сделать еще проще:
Вот, в самом кратком кратце все, снасти расставлены 🙂 А кто заинтересовался советую начать со странички РельсоВики посвященной ActiveRecord, а дальше (если сразу хочется поперед батька в пекло) открыть Active Record Reference Documentation. Ленивым предлагаю продолжать читать этот блог, еще чего-нибудь обязательно расскажем, благо про ActiveRecord рассказов непочатый край. Смелым — не ждать продолжения, а пробовать-пробовать-пробовать.
Active Record против Data Mapper-а для сохранения данных
Эти 2 шаблона проектирования описаны в книге Мартина Фаулера «Шаблоны корпоративных приложений» и представляют собой способы работы с сохранением данных в объектно-ориентированном программировании.
Пример шаблона Active Record
class Foo < protected $db; public $id; public $bar; public function __construct(PDO $db) < $this->db = $db; > public function do_something() < $this->bar .= uniqid(); > public function save() < if ($this->id) < $sql = "UPDATE foo SET bar = :bar WHERE $statement = $this->db->prepare($sql); $statement->bindParam("bar", $this->bar); $statement->bindParam("id", $this->id); $statement->execute(); > else < $sql = "INSERT INTO foo (bar) VALUES (:bar)"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $this->bar); $statement->execute(); $this->id = $this->db->lastInsertId(); > > > //Insert $foo = new Foo($db); $foo->bar = 'baz'; $foo->save();
В этом упрощенном примере, дескриптор базы данных вводится в конструкторе Foo (Использование инъекции зависимостей здесь позволяет тестировать объект без использования реальной базы данных), и Foo использует его, чтобы сохранять свои данные. Do_something — просто метод-заглушка, заменяющий бизнес логику.
Преимущества Active Record
- Писать код с Active Record получается быстро и легко, в том случае, когда свойства объекта прямо соотносятся с колонками в базе данных.
- Сохранение происходит в одном месте, что позволяет легко изучить, как это работает.
Недостатки Active Record
- Модели Active Record нарушаю принципы SOLID . В частности, принцип единой ответственности ( SRP — «S» в принципах SOLID ). Согласно принципу, доменный объект должен иметь только одну зону ответственности, то есть только свою бизнес-логику. Вызывая его для сохранения данных, вы добавляете ему дополнительную зону ответственности, увеличивая сложность объекта, что усложняет его поддержку и тестирование.
- Реализации сохранения данных тесно связана с бизнес-логикой, а это означает, что если вы позже захотите использовать другую абстракцию для сохранения данных (например для хранения данных в XML-файле, а не в базе данных), то вам придется делать рефакторинг кода.
Пример Data Mapper-а
class Foo < public $id; public $bar; public function do_something() < $this->bar .= uniqid(); > > class FooMapper < protected $db; public function __construct(PDO $db) < $this->db = $db; > public function saveFoo(Foo &$foo) < if ($foo->id) < $sql = "UPDATE foo SET bar = :bar WHERE $statement = $this->db->prepare($sql); $statement->bindParam("bar", $foo->bar); $statement->bindParam("id", $foo->id); $statement->execute(); > else < $sql = "INSERT INTO foo (bar) VALUES (:bar)"; $statement = $this->db->prepare($sql); $statement->bindParam("bar", $foo->bar); $statement->execute(); $foo->id = $this->db->lastInsertId(); > > > //Insert $foo = new Foo(); $foo->bar = 'baz'; $mapper = new FooMapper($db); $mapper->saveFoo($foo);
В данном случае, класс Foo намного проще и должен беспокоиться только о своей бизнес-логике. Он не только не должен сохранять собственные данные, он даже не знает и не заботится о том, все ли его данные были сохранены.
Преимущества Data Mapper-а
- Каждый объект имеет свою зону ответственности, тем самым следую принципам SOLID и сохраняя каждый объект простым и по существу.
- Бизнес-логика и сохранение данных связаны слабо, и если вы хотите сохранять данные в XML-файл или какой-нибудь другой формат, вы можете просто написать новый Mapper, не притрагиваясь к доменному объекту.
Недостатки Data Mapper-а
- Вам придется гораздо больше думать, перед тем как написать код.
- В итоге у вас больше объектов в управлении, что немного усложняет код и его отладку.
Сервис-объекты
При использовании шаблона проектирования Data Mapper, вызывающий код должен выбрать Mapper и бизнес-объект и связать их вместе. Если это код вызова в контроллере, то в конечном счете ваша модель «утекает» в контроллер, что может вызвать большие проблемы при поддержке и юнит-тестировании. Эта проблема может быть решена путем введения объекта-сервиса. Сервис является воротами между контроллером и моделью и связывает доменный объект с Mapper-ом по мере необходимости.
Следует помнить, что M в MVC, представляет собой слой абстракции модели, а не объект модели. Так может быть несколько типов объектов в одной модели (в приведенном выше примере, у вас может быть объект сервиса, доменный объект и объект Mapper-а, выступающие в роли единой модели). С другой стороны, если вы используете модели Active Record, ваша модель может быть представлена лишь одним объектом.
Варианты использования
Объекты Active Record исторически были очень популярны из-за того, что они проще, легче в понимании и быстрее в написании, поэтому многие фреймворки и ORM используют Active Record по умолчанию.
Если вы уверены, что вам никогда не понадобиться менять слой сохранения данных (если вы имеете дело с объектом, который представляет из себя INI-файл, например), или вы имеете дело с очень простыми объектами, в которых не так много бизнес-логики, или просто предпочитаете держать все в небольшом количестве классов, тогда шаблон Active Record это то, что вам нужно.
Использование Data Mapper-а хотя и ведет к более чистому, простому в тестировании и поддержке коду, и обеспечивает большую гибкость, — цена этому, — повышение сложности. Если вы еще не пробовали его использовать, то дайте ему шанс, — вам должно понравиться.
DAO vs Active Record vs Data Mapper паттерны при работе с базой данных
В популярных фреймворках для работы с базой данных, как правило, используется ORM, которая представляет собой вспомогательную прослойку между приложением и базой данных.
В зависимости от фреймворка, ORM может реализовывать паттерн Active Record (например, framework kohana, Yii) или Data Mapper (например, doctrine в symfony). ORM фреймворка также может реализовывать другой паттерн или гибрид.
ORM — Object Relational Mapping. Говоря самым простым языком, ряды из таблиц в базе данных, будут представлены в виде объектов, проперти которых соответствуют именам полей из таблиц, а значения пропертей объекта — значениям из базы данных. Одна строка в базе данных — один объект.
Итак, идем от более простого к более сложному: DAO -> Active Record -> Data Mapper.
Паттерн DAO
Используется в библиотеке DBSimple.
DAO (Data Access Object) — объект, который предоставляет абстрактный интерфейс к базе данных. Главной идеей DAO является сделать возможным определенные операции с данными не вдаваюсь в детали реализации базы данных.
При использовании DAO — функции для работы c конкретной таблицей хранятся в файле модели. Модель (таблица) наследует абстрактный класс, реализующий DAO.
При получение ряда в DAO — в результирующем объекте или массиве будут содержаться все поля из базы данных. Пример:
$user = TableUser::me()->fetchRowById($userId);
В переменной $user будет объект либо массив в зависимости от реализации, содержащий все поля из таблицы «user». Класс TableUser будет содержать все методы, которые работают с таблицей «user». Дополнительный класс репозитория тут не нужен. Почему? Если для получения данных создать класс репозиторий, то в модели (таблице) останется буквально единственный метод получения имени таблицы.
Для обновления данных в DAO — используются отдельные методы, реализующие прямые sql-запросы, в отличие от Active Record где меняется состояние модели и вызывается save() для персиста состояния в базу данных.
Паттерн Active Record
Используется в Kohana, Yii.
Это шаблон проектирования или один из слоев приложения, который несет ответственность за представление бизнес-логики и данных. Active Record позволяет создавать и использовать более просто те объекты, который требуют постоянного хранения в базе данных. Если говорить в отношении MVC, то Active Record реализует первую букву М — то есть модель.
Очень простой пример использования модели, реализующей паттерн Active Record в php:
$user = new User(); $user->name = 'name'; $user->email = 'email@dot.com'; $user->save();
Этот код вызовет генерацию такого sql-запроса:
INSERT INTO user (name, email) VALUES ('name', 'email@dot.com');
Как правило, внутри модели, которая реализует Active Record, прописаны property. В данном случае в модели User должны быть представлены как минимум property name, email. Содержимое класса User:
Проперти, как правило, делают публичными, как в примере выше. Но, проперти модели могут быть и приватными, тогда отдельно создают сеттеры и геттеры под каждый проперти.
Как правило, Active Record Модель — это маппинг полей модели на поля в базе данных. В Active record сама модель отвечает за сохранение данных в базу данных. А это означает, что нарушется первый принцип из SOLID — принцип единственности ответственности. Класс отвечает не только за представление данных, но и за сохранение.
Паттерн Data Mapper
Используется в Hibernate в Java и в Doctrine2 в php, так в CycleOrm.
Data Mapper — это слой доступа к данным, который предоставляет двунаправленный маппинг данных между постоянным хранилищем данных (обычно, это sql база данных) и хранением данных в памяти (например, на время выполнениния php скрипта).
В отличие от Active Record, в Data Mapper появляется еще один слой или тип сущности такой как entityManager. Именно этот слой будет отвечать за перенос состояния модели в базу данных и обратно.
Дата добавления: 5 лет назад
php
- Как получить все слова из строки на php? Решено!
- Как вернуть результат от функции printf() вместо вывода на экран? Решено!
- Как получить имена параметров из конструктора класса на php? Решено!
- Как обрезать gif изображение на php imagick? Решено!
- Как удалить NULL-байты из строки? 1 ответ
- Простая обёртка для mysqli6 комментариев
- Тестер регулярных выражений на php 0 комментариев
- Определяем язык текста с помощью cld2+python+php 0 комментариев
- PHP 8 — что нового в 8-ой версии php 0 комментариев
- PHP выполнить shell команду синхронно с timeout 0 комментариев