SlideShare ist ein Scribd-Unternehmen logo
1 von 39
Толстая модель
       История разработки ORM

Шамин Михаил
Geometria.ru
Ведущий разработчик
Geometria.ru

• Главный фотохроникер страны
• 8 лет на рынке
• Представительство в 150 городах России, СНГ и
  Прибалтики
• Ежедневно 80 000 пользователей / 600 000 просмотров
• В понедельник 110 000 / 1 000 000
• Более 500 000 репортажей
• 15 000 000 фотографий
• 800 000 зарегистрированных пользователей
Почему понадобился свой ORM

Было

•   Наследство в виде залежей кода-лапши
•   Практически вся бизнес-логика в контроллерах
•   Вплоть до формирования select ов!
•   Некоторые экшены размером в 200 строк!
•   В роли модели - Zend_Db_Table
Почему понадобился свой ORM

Стало

• Стали использовать NoSQL решения, такие как Redis и
  Mongo
• Понадобилось решение, готовое работать с любым
  хранилищем, а не только SQL
• Есть ли что-то на рынке?
• Doctrine2 в alphа, еще сырая - страшно.
• Что делать?
• Пишем свой велосипед!
Выбор дизайна



                 Открываем книгу

              Мартина Фаулера
                   (Martin Fowler)

"Шаблоны корпоративных приложений"
  ("Patterns of Enterprise Application Architecture")
Выбор дизайна

         И находим то что нужно.

           Domain Model
                  или
     Модель предметной области
Поля модели. Как задавать ?

• В Zend_Db_Table_Row поля не прописаны явно, а
  берутся из схемы таблицы БД

• В Doctrine2 через задание private/protected свойств и
  генерацию getter/setter методов.
Поля модели. Решение:

Использовать DocBlock
Профиты:

•   Готовый шаблон для типов данных
•   Сразу в аннотации класса видны все поля модели
•   Автокомплит в IDE (Zend Studio, PhpStorm, NetBeans)
•   Быстрое создание классов
Zend_Reflection для генерации полей

/**
  * @property integer $id
  * @property string $title
  * @property string $body
  * @property boolean $hidden
  * @property integer $date
  **/
class Model_Post extends Geometria_Model
{
}
Zend_Reflection для генерации полей
После $post = new Model_Post();

свойство $_data будет выглядеть следующим образом:

class Model_Post extends Geometria_Model
{
    protected $_data = array(
        'id' => null,
        'title' => null,
        'body' => null,
        'hidden' => null,
        'date' => null,
    );
}
Доступ к полям

• Внешний доступ к полям обеспечивается через
  магические методы __get() и __set()

• Можно реализовать методы get<поле> и set<поле>,
  чтобы изменить логику установки/получения значения
  поля.
/**
  * ...
  * @property integer $date Unix timestamp
  */
class Model_Post extends Geometria_Model
...
public function setDate($value)
{
     if ($value instanceof Zend_Date) {
         $value = $value->getTimestamp();
     }
     $this->_data['date'] = $value;
}
Установка значения по умолчанию

class Model_Post extends Geometria_Model
...
public function getDate($value)
{
    if (null === $this->_data['date']) {
        $this->_data['date'] = time();
    }
    return $this->_data['date'];
}
Как хранить модель?

Используем DataMapper

• Маппер знает все о модели и о том, как и где еѐ
  хранить.
• Модель ничего не знает о хранилище.
• Логика домена отделена от persist логики
• Можно менять структуру бд или даже сменить
  хранилище, не меняя логику модели, всего лишь
  изменив маппер.
• Маппер выполняет CRUD операции
• Можно использовать любое хранилище: MySQL, Mongo,
  Redis, Config file, RESTApi и др.
Интерфейс маппера

interface Geometria_Model_Mapper_Interface
{
    public function create(Geometria_Model $model);

    public function update(Geometria_Model $model);

    public function delete(Geometria_Model $model);

    public function fetchOne($cond, $sort);

    public function fetchAll($cond, $sort, $limit, $skip);

    public function getCount($cond);
}
Работа с моделью

$post = new Model_Post();
$post->title = 'hello world!';
$post->body = 'foo bar';

$postMapper = new Model_Post_Mapper();
$postMapper->create($post);

echo $post->id; // 1 маппер сам проставил в модели
id
Выборки
• Условие $cond - простой массив
  имя поля => значение
• Сортировка $sort - тоже просто массив
  имя поля => (bool) направление сортировки
• Для более сложных выборок пишем отдельный метод

Выбрать 10 скрытых постов, начиная с самых новых

$mapper->fetchAll(
    array('hidden' => true),
    array('date' => false),
    10
);
Делаем ActiveRecord

Рассказываем модели, что у нее есть маппер.

• Делаем статический метод getMapper() который из
  специального контейнера
  Geometria_Model_Mapper_Manager достает нужный ей
  маппер
• Делаем у модели методы create(), update(), delete()


public function create()
{
    return self::getMapper()->create($this);
}
Теперь создание модели выглядит так:
$post = new Model_Post();
$post->title = 'hello world!';
$post->body = 'foo bar';
$post->create();

echo $post->id; // 1

А пост можно получить в одну строчку:

$post = Model_Post::getMapper()->fetchOne(
    array('id' => 1)
);
или так
$post = Model_Post::getMapper()->fetch(1);
Что вернет fetchAll()? Коллекцию!

• аналог Zend_Db_Table_Rowset
• Паттерн Record Set
• Позволяет выполнять массовые действия с набором
  моделей

interface Geometria_Model_Collection_Interface
extends Iterator, Countable
{
  public function append(Geometria_Model $model);
  public function prepend(Geometria_Model $model);
  public function populate(array $data);
  public function clear();
  public function toArray();
}
Нужен Paginator?
class Geometria_Paginator_Adapter_Mapper
  implements Zend_Paginator_Adapter_Interface
{
  public function __construct(
    Geometria_Model_Mapper_Interface $mapper,
    array $cond = null,
    array $sort = null
  )
  {
    $this->_mapper = $mapper;
    $this->_cond   = $cond;
    $this->_sort   = $sort;
  }
}
Нужен Paginator?
class Geometria_Paginator_Adapter_Mapper
  ...
  public function getItems($offset, $limit)
  {
    return $this->_mapper->fetchAll(
       $this->_cond,
       $this->_sort,
       $limit,
       $offset
    );
  }

 public function count()
 {
   return $this->_mapper->getCount(
      $this->_cond,
      $this->_sort
   );
 }
Хотим кешировать, логировать и тд.
• Используем декоратор для маппера
• Декортатор - это матрешка: в конструктор первого
  декоратора передаем маппер, в конструктор второго
  передаем первый декоратор и так далее
• Декоратор перехватывает "интересующие" его методы,
  и изменяет результат на ему угодный, остальные
  методы просто пропускает дальше.
• При инициализации маппера маппер-менеджер
  спрашивает у маппера, хочет ли он
  задекорироваться и оборачивает
  во все указанные декоратры
Примеры декораторов
Cache
• fetchOne(), fetchAll() - на основании переданного условия
  берет данные из кеша, или же просит маппер выполнить
  запрос и кеширует его результат.
• create(), update(), delete() - сбрасывает соответсвующий
  кеш.

Profiler
• Декоратор пропускает все запросы через себя,
   записывая в лог время выполнения запроса.

Identity Map
 • Кеширует результаты в памяти, чтобы маппер не
   выполнял одинаковые запросы дважды
Отношения

Раз уж строим ORM, то должны быть отношения между
сущностями.

• Отношения так же, как и поля, задаются в DocBlock
• Параметры описываются в спец формате
• При создании модели, создаются объекты-менеджеры
  отношений
• При обращении к полю, ссылающемуся на внешнюю
  сущность, объект-менеджер отношения делает запрос к
  внешнему мапперу и возвращает полученый объект.
Пример работы с отношениями
/**
  * ...
  * @property integer $authorId
  * @property Model_Author $author
[relation=belongsTo;localKey=authorId]
  */
class Model_Post extends Geometria_Model
{...}

$post = Model_Post::getMapper()->fetchOne(array('authorId'
=> 5));
$author = $post->author; // Model_Author

Менеджер отношения в данном случае выполнит запрос

Model_Author::getMapper()->fetchOne(array('id' => 5));
Виды отношений

• hasOne - one-to-one отношение
• belongsTo - тоже что и hasOne, но требует
  обязательного наличия объекта
• hasMany - one-to-many отношение
Полиморфические связи

Обеспечивают связь с несколькими видами сущностей, то
на какой тип сущности стоит ссылка определяет параметр
ownerType, в то время как параметр ownerTypeId
определяет id сущности.

/**
  * @property string $ownerType
  * @property integer $ownerId
  * @property Model_User|Model_Post $owner
[relation=polymorhic; localKey=ownerId;
localTypeKey=ownerType]
  */
class Model_Comment extends Geometria_Model
{..}
Тонкости отношений

$posts = Model_Post::getMapper()->fetchAll();

foreach ($posts as $post) {
    echo $post->title . ' by ' . $post->author;
}

Автор запрашивается при каждой итерации.

Если у нас 10 постов, значит мы сделаем 1 запрос на
получение постов и 10 запросов на получение авторов.
Итого 11 запросов - плохо!
Тонкости отношений

Решение:

$posts->fetchRelations('author');

Просим relation-manager получить всех авторов одним
запросом и проставить во всех постах коллекции.

Итого: 2 запроса, независимо от количества постов.
Тонкости отношений

А если у автора есть связь с картинкой-аватаркой?

$posts->fetchRelations('author', 'picture');

Что означает, что перед тем, как "распихать" всех авторов
по постам, у полученной коллекции авторов будет
вызван метод:

$authors->fetchRelations('picture');
Каскадные операции

У отношений можно прописать действие, которое будет
выполняться при удалении модели onDelete:
 • CASCADE - удалить все связанные зависимые модели
 • SET NULL - очистить значения внешних ключей

Это позволяет сохранять целостность связей внутри
нашей системы.
Жизнь без Join'ов

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

Использовать sphinx.

•   Создаем индекс в сфинксе для такого рода выборки.
•   Индексируем данные.
•   Создаем sphinx декоратор
•   Декоратор ищет id документов, удовлетворяющих
    поисковому запросу. И по этом списку id маппер
    возвращает коллекцию с результатом.
Что дало внедрение ORM

• Существенное ускорение разработки
• Время вхождения в чужой код значительно
  уменьшилось
• Использование Domain Driven Design позволяет
  говорить на языке предметной области, что повышает
  читаемость кода
• Логика приложения вынесена в отдельный слой
  сервисов. Что позволяет использовать ее не только в
  MVC, но и в CLI, например.
• Размер экшенов в контроллерах сократился до 10 строк.
Будет ли open source?

Будет, но позже )
Спасибо

Twitter: @munk13

МойКруг: http://munkie.moikrug.ru

Weitere ähnliche Inhalte

Was ist angesagt?

PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
pgdayrussia
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел Тайкало
Stanfy
 
YiiConf 2012 - Alexander Makarov - Yii2, что нового
YiiConf 2012 - Alexander Makarov - Yii2, что новогоYiiConf 2012 - Alexander Makarov - Yii2, что нового
YiiConf 2012 - Alexander Makarov - Yii2, что нового
Alexander Makarov
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Yandex
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)
Ontico
 

Was ist angesagt? (20)

Javascript 1
Javascript 1Javascript 1
Javascript 1
 
Лекция 11. Тестирование.
Лекция 11. Тестирование.Лекция 11. Тестирование.
Лекция 11. Тестирование.
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка Django
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному коду
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
 
Объектное и прототипное программирование в Javascript
Объектное и прототипное программирование в JavascriptОбъектное и прототипное программирование в Javascript
Объектное и прототипное программирование в Javascript
 
2014-08-02 01 Егор Непомнящих. jWidget - очередной MV*-фреймворк
2014-08-02 01 Егор Непомнящих. jWidget - очередной MV*-фреймворк2014-08-02 01 Егор Непомнящих. jWidget - очередной MV*-фреймворк
2014-08-02 01 Егор Непомнящих. jWidget - очередной MV*-фреймворк
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Фундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел ТайкалоФундаментальные основы разработки под iOS. Павел Тайкало
Фундаментальные основы разработки под iOS. Павел Тайкало
 
YiiConf 2012 - Alexander Makarov - Yii2, что нового
YiiConf 2012 - Alexander Makarov - Yii2, что новогоYiiConf 2012 - Alexander Makarov - Yii2, что нового
YiiConf 2012 - Alexander Makarov - Yii2, что нового
 
Лекция 6. Классы 1.
Лекция 6. Классы 1.Лекция 6. Классы 1.
Лекция 6. Классы 1.
 
Yii 2. Что нового?
Yii 2. Что нового?Yii 2. Что нового?
Yii 2. Что нового?
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
 
Магия метаклассов
Магия метаклассовМагия метаклассов
Магия метаклассов
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap Database
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)
 

Andere mochten auch

Werkvelddag C-master RUG
Werkvelddag C-master RUGWerkvelddag C-master RUG
Werkvelddag C-master RUG
Roy Meijer
 
政策コーカス谷尾チームプレゼン資料
政策コーカス谷尾チームプレゼン資料政策コーカス谷尾チームプレゼン資料
政策コーカス谷尾チームプレゼン資料
hiden
 
Información sobre la violencia domestica para padres, representantes y estud...
Información sobre  la violencia domestica para padres, representantes y estud...Información sobre  la violencia domestica para padres, representantes y estud...
Información sobre la violencia domestica para padres, representantes y estud...
Richard Ortega
 
σχεδιο εξοικονομησησ ενεργειασ
σχεδιο εξοικονομησησ ενεργειασσχεδιο εξοικονομησησ ενεργειασ
σχεδιο εξοικονομησησ ενεργειασ
kate68
 
Sztuka rzymu okres średniego cesarstwa
Sztuka rzymu okres średniego cesarstwaSztuka rzymu okres średniego cesarstwa
Sztuka rzymu okres średniego cesarstwa
malkosia
 
เทคโนโลยีสารสนเทศหลิว
เทคโนโลยีสารสนเทศหลิวเทคโนโลยีสารสนเทศหลิว
เทคโนโลยีสารสนเทศหลิว
Nart-Anong Srinak
 

Andere mochten auch (20)

Identiteit in de ict
Identiteit in de ictIdentiteit in de ict
Identiteit in de ict
 
Lesson1
Lesson1Lesson1
Lesson1
 
BlueEyes russian
BlueEyes russianBlueEyes russian
BlueEyes russian
 
Werkvelddag C-master RUG
Werkvelddag C-master RUGWerkvelddag C-master RUG
Werkvelddag C-master RUG
 
政策コーカス谷尾チームプレゼン資料
政策コーカス谷尾チームプレゼン資料政策コーカス谷尾チームプレゼン資料
政策コーカス谷尾チームプレゼン資料
 
Ukr el11 5
Ukr el11 5Ukr el11 5
Ukr el11 5
 
Still life
Still lifeStill life
Still life
 
Información sobre la violencia domestica para padres, representantes y estud...
Información sobre  la violencia domestica para padres, representantes y estud...Información sobre  la violencia domestica para padres, representantes y estud...
Información sobre la violencia domestica para padres, representantes y estud...
 
第七章
第七章第七章
第七章
 
σχεδιο εξοικονομησησ ενεργειασ
σχεδιο εξοικονομησησ ενεργειασσχεδιο εξοικονομησησ ενεργειασ
σχεδιο εξοικονομησησ ενεργειασ
 
Sztuka rzymu okres średniego cesarstwa
Sztuka rzymu okres średniego cesarstwaSztuka rzymu okres średniego cesarstwa
Sztuka rzymu okres średniego cesarstwa
 
Ppt图
Ppt图Ppt图
Ppt图
 
Mz
MzMz
Mz
 
Presentatie Hans Vaneerdewegh (ABVV-Metaal)
Presentatie Hans Vaneerdewegh (ABVV-Metaal)Presentatie Hans Vaneerdewegh (ABVV-Metaal)
Presentatie Hans Vaneerdewegh (ABVV-Metaal)
 
Tania
TaniaTania
Tania
 
Extensão Av. G. Vargas
Extensão Av. G. VargasExtensão Av. G. Vargas
Extensão Av. G. Vargas
 
เทคโนโลยีสารสนเทศหลิว
เทคโนโลยีสารสนเทศหลิวเทคโนโลยีสารสนเทศหลิว
เทคโนโลยีสารสนเทศหลิว
 
Aspetti di sicurezza del crittosistema Pretty Good Privacy (PGP)
Aspetti di sicurezza del crittosistema Pretty Good Privacy (PGP)Aspetti di sicurezza del crittosistema Pretty Good Privacy (PGP)
Aspetti di sicurezza del crittosistema Pretty Good Privacy (PGP)
 
Sistema GeoLoc
Sistema GeoLocSistema GeoLoc
Sistema GeoLoc
 
Dragon Project
Dragon ProjectDragon Project
Dragon Project
 

Ähnlich wie Толстая модель. История разработки ORM

Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.
DrupalCampDN
 
Ember.js ответ на почти все вопросы - java script frameworks day 2014
Ember.js   ответ на почти все вопросы - java script frameworks day 2014Ember.js   ответ на почти все вопросы - java script frameworks day 2014
Ember.js ответ на почти все вопросы - java script frameworks day 2014
Andrey Listochkin
 
ZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and DoctrineZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and Doctrine
ZFConf Conference
 

Ähnlich wie Толстая модель. История разработки ORM (20)

Ice Php Framework Preview Release
Ice Php Framework Preview ReleaseIce Php Framework Preview Release
Ice Php Framework Preview Release
 
Survive with OOP
Survive with OOPSurvive with OOP
Survive with OOP
 
Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.Импорт данных с фреймворком Migrate. Владислав Богатырев.
Импорт данных с фреймворком Migrate. Владислав Богатырев.
 
Выжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим ГопейВыжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим Гопей
 
Zend Framework и Doctrine
Zend Framework и DoctrineZend Framework и Doctrine
Zend Framework и Doctrine
 
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
 
бегун
бегунбегун
бегун
 
Расширенное кеширование в Doctrine2
Расширенное кеширование в Doctrine2Расширенное кеширование в Doctrine2
Расширенное кеширование в Doctrine2
 
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
Расширенное кеширование Doctrine2 (Ильяс Салихов, Intaro)
 
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCАрхитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
 
Корпоративное приложение на Rails
Корпоративное приложение на RailsКорпоративное приложение на Rails
Корпоративное приложение на Rails
 
UWDC 2013, Yii2
UWDC 2013, Yii2UWDC 2013, Yii2
UWDC 2013, Yii2
 
Метапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScriptМетапрограммирование с примерами на JavaScript
Метапрограммирование с примерами на JavaScript
 
Yserver
YserverYserver
Yserver
 
Ember.js ответ на почти все вопросы - java script frameworks day 2014
Ember.js   ответ на почти все вопросы - java script frameworks day 2014Ember.js   ответ на почти все вопросы - java script frameworks day 2014
Ember.js ответ на почти все вопросы - java script frameworks day 2014
 
бегун
бегунбегун
бегун
 
Ruby on Rails. Работа с моделями — продолжение
Ruby on Rails. Работа с моделями — продолжениеRuby on Rails. Работа с моделями — продолжение
Ruby on Rails. Работа с моделями — продолжение
 
ZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and DoctrineZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and Doctrine
 
Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"Михаил Давыдов "Масштабируемые JavaScript-приложения"
Михаил Давыдов "Масштабируемые JavaScript-приложения"
 
"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)
 

Kürzlich hochgeladen

Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Ирония безопасности
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
Хроники кибер-безопасника
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
Хроники кибер-безопасника
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
Хроники кибер-безопасника
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
Ирония безопасности
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
Хроники кибер-безопасника
 

Kürzlich hochgeladen (9)

Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 
MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 

Толстая модель. История разработки ORM

  • 1. Толстая модель История разработки ORM Шамин Михаил Geometria.ru Ведущий разработчик
  • 2.
  • 3. Geometria.ru • Главный фотохроникер страны • 8 лет на рынке • Представительство в 150 городах России, СНГ и Прибалтики • Ежедневно 80 000 пользователей / 600 000 просмотров • В понедельник 110 000 / 1 000 000 • Более 500 000 репортажей • 15 000 000 фотографий • 800 000 зарегистрированных пользователей
  • 4.
  • 5. Почему понадобился свой ORM Было • Наследство в виде залежей кода-лапши • Практически вся бизнес-логика в контроллерах • Вплоть до формирования select ов! • Некоторые экшены размером в 200 строк! • В роли модели - Zend_Db_Table
  • 6. Почему понадобился свой ORM Стало • Стали использовать NoSQL решения, такие как Redis и Mongo • Понадобилось решение, готовое работать с любым хранилищем, а не только SQL • Есть ли что-то на рынке? • Doctrine2 в alphа, еще сырая - страшно. • Что делать? • Пишем свой велосипед!
  • 7. Выбор дизайна Открываем книгу Мартина Фаулера (Martin Fowler) "Шаблоны корпоративных приложений" ("Patterns of Enterprise Application Architecture")
  • 8.
  • 9. Выбор дизайна И находим то что нужно. Domain Model или Модель предметной области
  • 10. Поля модели. Как задавать ? • В Zend_Db_Table_Row поля не прописаны явно, а берутся из схемы таблицы БД • В Doctrine2 через задание private/protected свойств и генерацию getter/setter методов.
  • 11. Поля модели. Решение: Использовать DocBlock Профиты: • Готовый шаблон для типов данных • Сразу в аннотации класса видны все поля модели • Автокомплит в IDE (Zend Studio, PhpStorm, NetBeans) • Быстрое создание классов
  • 12. Zend_Reflection для генерации полей /** * @property integer $id * @property string $title * @property string $body * @property boolean $hidden * @property integer $date **/ class Model_Post extends Geometria_Model { }
  • 13. Zend_Reflection для генерации полей После $post = new Model_Post(); свойство $_data будет выглядеть следующим образом: class Model_Post extends Geometria_Model { protected $_data = array( 'id' => null, 'title' => null, 'body' => null, 'hidden' => null, 'date' => null, ); }
  • 14. Доступ к полям • Внешний доступ к полям обеспечивается через магические методы __get() и __set() • Можно реализовать методы get<поле> и set<поле>, чтобы изменить логику установки/получения значения поля.
  • 15. /** * ... * @property integer $date Unix timestamp */ class Model_Post extends Geometria_Model ... public function setDate($value) { if ($value instanceof Zend_Date) { $value = $value->getTimestamp(); } $this->_data['date'] = $value; }
  • 16. Установка значения по умолчанию class Model_Post extends Geometria_Model ... public function getDate($value) { if (null === $this->_data['date']) { $this->_data['date'] = time(); } return $this->_data['date']; }
  • 17. Как хранить модель? Используем DataMapper • Маппер знает все о модели и о том, как и где еѐ хранить. • Модель ничего не знает о хранилище. • Логика домена отделена от persist логики • Можно менять структуру бд или даже сменить хранилище, не меняя логику модели, всего лишь изменив маппер. • Маппер выполняет CRUD операции • Можно использовать любое хранилище: MySQL, Mongo, Redis, Config file, RESTApi и др.
  • 18. Интерфейс маппера interface Geometria_Model_Mapper_Interface { public function create(Geometria_Model $model); public function update(Geometria_Model $model); public function delete(Geometria_Model $model); public function fetchOne($cond, $sort); public function fetchAll($cond, $sort, $limit, $skip); public function getCount($cond); }
  • 19. Работа с моделью $post = new Model_Post(); $post->title = 'hello world!'; $post->body = 'foo bar'; $postMapper = new Model_Post_Mapper(); $postMapper->create($post); echo $post->id; // 1 маппер сам проставил в модели id
  • 20. Выборки • Условие $cond - простой массив имя поля => значение • Сортировка $sort - тоже просто массив имя поля => (bool) направление сортировки • Для более сложных выборок пишем отдельный метод Выбрать 10 скрытых постов, начиная с самых новых $mapper->fetchAll( array('hidden' => true), array('date' => false), 10 );
  • 21. Делаем ActiveRecord Рассказываем модели, что у нее есть маппер. • Делаем статический метод getMapper() который из специального контейнера Geometria_Model_Mapper_Manager достает нужный ей маппер • Делаем у модели методы create(), update(), delete() public function create() { return self::getMapper()->create($this); }
  • 22. Теперь создание модели выглядит так: $post = new Model_Post(); $post->title = 'hello world!'; $post->body = 'foo bar'; $post->create(); echo $post->id; // 1 А пост можно получить в одну строчку: $post = Model_Post::getMapper()->fetchOne( array('id' => 1) ); или так $post = Model_Post::getMapper()->fetch(1);
  • 23. Что вернет fetchAll()? Коллекцию! • аналог Zend_Db_Table_Rowset • Паттерн Record Set • Позволяет выполнять массовые действия с набором моделей interface Geometria_Model_Collection_Interface extends Iterator, Countable { public function append(Geometria_Model $model); public function prepend(Geometria_Model $model); public function populate(array $data); public function clear(); public function toArray(); }
  • 24. Нужен Paginator? class Geometria_Paginator_Adapter_Mapper implements Zend_Paginator_Adapter_Interface { public function __construct( Geometria_Model_Mapper_Interface $mapper, array $cond = null, array $sort = null ) { $this->_mapper = $mapper; $this->_cond = $cond; $this->_sort = $sort; } }
  • 25. Нужен Paginator? class Geometria_Paginator_Adapter_Mapper ... public function getItems($offset, $limit) { return $this->_mapper->fetchAll( $this->_cond, $this->_sort, $limit, $offset ); } public function count() { return $this->_mapper->getCount( $this->_cond, $this->_sort ); }
  • 26. Хотим кешировать, логировать и тд. • Используем декоратор для маппера • Декортатор - это матрешка: в конструктор первого декоратора передаем маппер, в конструктор второго передаем первый декоратор и так далее • Декоратор перехватывает "интересующие" его методы, и изменяет результат на ему угодный, остальные методы просто пропускает дальше. • При инициализации маппера маппер-менеджер спрашивает у маппера, хочет ли он задекорироваться и оборачивает во все указанные декоратры
  • 27. Примеры декораторов Cache • fetchOne(), fetchAll() - на основании переданного условия берет данные из кеша, или же просит маппер выполнить запрос и кеширует его результат. • create(), update(), delete() - сбрасывает соответсвующий кеш. Profiler • Декоратор пропускает все запросы через себя, записывая в лог время выполнения запроса. Identity Map • Кеширует результаты в памяти, чтобы маппер не выполнял одинаковые запросы дважды
  • 28. Отношения Раз уж строим ORM, то должны быть отношения между сущностями. • Отношения так же, как и поля, задаются в DocBlock • Параметры описываются в спец формате • При создании модели, создаются объекты-менеджеры отношений • При обращении к полю, ссылающемуся на внешнюю сущность, объект-менеджер отношения делает запрос к внешнему мапперу и возвращает полученый объект.
  • 29. Пример работы с отношениями /** * ... * @property integer $authorId * @property Model_Author $author [relation=belongsTo;localKey=authorId] */ class Model_Post extends Geometria_Model {...} $post = Model_Post::getMapper()->fetchOne(array('authorId' => 5)); $author = $post->author; // Model_Author Менеджер отношения в данном случае выполнит запрос Model_Author::getMapper()->fetchOne(array('id' => 5));
  • 30. Виды отношений • hasOne - one-to-one отношение • belongsTo - тоже что и hasOne, но требует обязательного наличия объекта • hasMany - one-to-many отношение
  • 31. Полиморфические связи Обеспечивают связь с несколькими видами сущностей, то на какой тип сущности стоит ссылка определяет параметр ownerType, в то время как параметр ownerTypeId определяет id сущности. /** * @property string $ownerType * @property integer $ownerId * @property Model_User|Model_Post $owner [relation=polymorhic; localKey=ownerId; localTypeKey=ownerType] */ class Model_Comment extends Geometria_Model {..}
  • 32. Тонкости отношений $posts = Model_Post::getMapper()->fetchAll(); foreach ($posts as $post) { echo $post->title . ' by ' . $post->author; } Автор запрашивается при каждой итерации. Если у нас 10 постов, значит мы сделаем 1 запрос на получение постов и 10 запросов на получение авторов. Итого 11 запросов - плохо!
  • 33. Тонкости отношений Решение: $posts->fetchRelations('author'); Просим relation-manager получить всех авторов одним запросом и проставить во всех постах коллекции. Итого: 2 запроса, независимо от количества постов.
  • 34. Тонкости отношений А если у автора есть связь с картинкой-аватаркой? $posts->fetchRelations('author', 'picture'); Что означает, что перед тем, как "распихать" всех авторов по постам, у полученной коллекции авторов будет вызван метод: $authors->fetchRelations('picture');
  • 35. Каскадные операции У отношений можно прописать действие, которое будет выполняться при удалении модели onDelete: • CASCADE - удалить все связанные зависимые модели • SET NULL - очистить значения внешних ключей Это позволяет сохранять целостность связей внутри нашей системы.
  • 36. Жизнь без Join'ов Как сделать выборку постов, написанных женщинами, если посты используют одно хранилище, а авторы другое, и нет возможности сделать join? Использовать sphinx. • Создаем индекс в сфинксе для такого рода выборки. • Индексируем данные. • Создаем sphinx декоратор • Декоратор ищет id документов, удовлетворяющих поисковому запросу. И по этом списку id маппер возвращает коллекцию с результатом.
  • 37. Что дало внедрение ORM • Существенное ускорение разработки • Время вхождения в чужой код значительно уменьшилось • Использование Domain Driven Design позволяет говорить на языке предметной области, что повышает читаемость кода • Логика приложения вынесена в отдельный слой сервисов. Что позволяет использовать ее не только в MVC, но и в CLI, например. • Размер экшенов в контроллерах сократился до 10 строк.
  • 38. Будет ли open source? Будет, но позже )