Reposirory в Yii2?
Reposirory в Yii2?
Знаю что в Yii2 AR - это repositroy+entity.
Я хочу разделить и вообще не использовать AR. Repository должен уметь возвращать заполненную entity и сохранять её, т.е. нужны какие-то mapper.
Выхода вижу два :
1) Писать велосипеды со своими Mapper's
2) Взять Doctrine
Есть ряд вопросов:
1) Есть ли хорошее решение для Yii2? (Сохранение агрегатов и моделей из нескольких таблиц в БД, получение из БД заполненной entity)
2) На сколько удобно использовать Doctrine в связки с Yii2, есть ли у кого-нибудь практический опыт использования такой связки? Какие неудобства могут возникнуть? (В проекте нет Foreign key)
P.S. На одном из последних докладов - "Yii2: Структура большого проекта" уважаемый Александр Макаров, сказал, что можно строить на Yii2 такие связки, но не рассказал как, сказав только, что он как прилежный разработчик возвращает AR репозиторием, но нигде не использует его функции для работы с persistence. Но, к сожалению, не все такие прилежные
Я хочу разделить и вообще не использовать AR. Repository должен уметь возвращать заполненную entity и сохранять её, т.е. нужны какие-то mapper.
Выхода вижу два :
1) Писать велосипеды со своими Mapper's
2) Взять Doctrine
Есть ряд вопросов:
1) Есть ли хорошее решение для Yii2? (Сохранение агрегатов и моделей из нескольких таблиц в БД, получение из БД заполненной entity)
2) На сколько удобно использовать Doctrine в связки с Yii2, есть ли у кого-нибудь практический опыт использования такой связки? Какие неудобства могут возникнуть? (В проекте нет Foreign key)
P.S. На одном из последних докладов - "Yii2: Структура большого проекта" уважаемый Александр Макаров, сказал, что можно строить на Yii2 такие связки, но не рассказал как, сказав только, что он как прилежный разработчик возвращает AR репозиторием, но нигде не использует его функции для работы с persistence. Но, к сожалению, не все такие прилежные
Re: Reposirory в Yii2?
дело вкуса
что тут конкретно yii2-ответственность? для чего искать что-то к yii2, если мы наоборот хотим создать фреймворко-независимое решение?
точно так же, как и в любом другом движке - устанавливаем, инициализируем, используем как любую стороннюю библиотеку.
надо либо использовать AR без репозиториев, либо не использовать AR вообще.paurlift писал(а): ↑2017.01.29, 20:26P.S. На одном из последних докладов - "Yii2: Структура большого проекта" уважаемый Александр Макаров, сказал, что можно строить на Yii2 такие связки, но не рассказал как, сказав только, что он как прилежный разработчик возвращает AR репозиторием, но нигде не использует его функции для работы с persistence. Но, к сожалению, не все такие прилежные
Re: Reposirory в Yii2?
1 вопрос тогда перефразирую, какие расширения можно использовать, чтобы написать реализацию repository, которые смогут сохранять(получать) aggregate и entity любой сложности.
Писать свой mapper на каждую сущность очень не рационально (тем более есть минимум одно готовое решение - Doctrine)
Александр Макаров сказал, что есть какое-то решение...
Писать свой mapper на каждую сущность очень не рационально (тем более есть минимум одно готовое решение - Doctrine)
Александр Макаров сказал, что есть какое-то решение...
Re: Reposirory в Yii2?
никаких расширений нет. используйте любую ORM.
частично так, частично нет
в котором вы будете писать не мапперы, но маппинги.
он не имел в виду что-то готовое.
Re: Reposirory в Yii2?
Маппинги будут нужны как минимум. Yii AR полагается на динамизм php и драйверы. Поэтому можно увидеть строку в числовом атрибуте.
Re: Reposirory в Yii2?
В простейшем случае ручная вставка:
Код: Выделить всё
$this->db->createCommand()->insert('posts', [
'id' => $post->getId()->getId(),
'created_at' => $post->getDate()->getTimestamp(),
'title' => $post->getTitle(),
])->execute();
Код: Выделить всё
$row = $this->db->createCommand('SELECT * FROM table WHERE id = :id')->...;
return $this->hydrator->hydrate(Post::class, [
'id' => new PostId($row['id']),
'date' => new DateTimeImmutable($row['created_at']),
'title' => $row['title'],
]);
Дополнить своим array_diff для отслеживания изменений связей. И при желании добавить Identity Map.
Нюанс - необходимость использования ArrayCollection вместо массивов или своих коллекций для связей.
Последний раз редактировалось ElisDN 2017.03.24, 12:44, всего редактировалось 2 раза.
Re: Reposirory в Yii2?
Да, но часть сущностей мапятся 1 к 1 на БД и в Doctrine есть консоль, которая позволяет сгенерировать маппинги.
Для тех сущностей, которые собираются из нескольких таблиц, большая часть все равно лежит в какой-то таблице, с помощью консоли уменьшаем ручной труд.
И все-таки связность БД, entity, repositroy вынесены в один .yml файл, что очень удобно. Для такого рода маппинга есть уже форма описания в .yml файлах, не нужно изобретать велосипед.
Наверно, но про какое-то решение было вскользь сказано и я как понял это не Doctrine
Re: Reposirory в Yii2?
написание маппера - это один класс на сущность из 20-30 строк. По времени это столько же сколько вы будете тратить на маппинги доктрины. Только маппер у вас будет лакончиным, понятным, а доктрина будет черным ящиком с магией внутри с десятиктратным повышением потребления памяти.paurlift писал(а): ↑2017.01.30, 09:36Да, но часть сущностей мапятся 1 к 1 на БД и в Doctrine есть консоль, которая позволяет сгенерировать маппинги.
Для тех сущностей, которые собираются из нескольких таблиц, большая часть все равно лежит в какой-то таблице, с помощью консоли уменьшаем ручной труд.
И все-таки связность БД, entity, repositroy вынесены в один .yml файл, что очень удобно. Для такого рода маппинга есть уже форма описания в .yml файлах, не нужно изобретать велосипед.
он не имел в виду что-то готовое для yii2. А брать вы можете любую ORM.
Re: Reposirory в Yii2?
Простейшие случаи понятны, спасибо за ссылку на гидратор.ElisDN писал(а): ↑2017.01.30, 06:47В простейшем случае ручная вставка:
Код: Выделить всё
$this->db->createCommand()->insert('posts', [ 'id' => $post->getId()->getId(), 'created_at' => $post->getDate()->getTimestamp(), 'title' => $post->getTitle(), ])->execute();
своим гидратором с кодом наподобии этого.Код: Выделить всё
и ручное чтение с заполнением через рефлексию: [code]$row = $this->db->createCommand('SELECT * FROM table WHERE id = :id')->...; return $this->hydrator->hydrate(Post::class, [ 'id' => new PostId($row['id']), 'date' => new DateTimeImmutable($row['created_at']), 'title' => $row['id'], ]);
Заполнение entity я делаю так, получаю репозиторием array, и его, вместе с сущностью отдаю в ArrayMapper, который через рефлексию заполняет и возвращает entity.
1) К примеру, нужно получить aggregate у которого одно из свойств коллекция связанных entity. И еще хуже, у этих entity могут быть еще свойства, которые в свою очередь могут быть тоже каким-то объектами. А хочется в итоге из БД вытянуть полностью заполненный aggregate.
2) Ну и сохранение такого aggregate сущий АД.
Кажется, что нужно писать свой разбор для любой сущности у которой свойства - объект или коллекция объектов и даже все становится плохо, если сущность лежит в 2-3 таблицах.
Re: Reposirory в Yii2?
Да, Doctrine ест больше памяти - это минус. А то что она является черным ящиков, нельзя отнести к минусам, вещь в себе, которая хорошо решает свои задачи.zelenin писал(а): ↑2017.01.30, 09:47 написание маппера - это один класс на сущность из 20-30 строк. По времени это столько же сколько вы будете тратить на маппинги доктрины. Только маппер у вас будет лакончиным, понятным, а доктрина будет черным ящиком с магией внутри с десятиктратным повышением потребления памяти.
Это не 20-30 строк кода, если сохраняем aggregate. При сохранение нужно писать разбор. При вытаскивание по id aggregate тоже нужно писать свое заполнение.
Можете прокомментировать мой пост выше? Спасибо
Re: Reposirory в Yii2?
могу: вы программирование и написание кода считаете "сущим адом" плюс, как кажется, не понимаете какую цель репозиторий преследует и, главное, зачем оно вам нужно. Попробуйте то, о чем спрашиваете, начать реализовывать. Потом обсудим процесс.
Re: Reposirory в Yii2?
Спасибо. Очень помогли
А если серьезно, я попробовал и понимаю зачем мне это нужно и меня не покидает ощущение написание своего велосипеда.
Привел конкретный пример, когда велосипед становится прям 3-х колесным.
Как мне сохранять и получать aggregate?
P.S. Обожаю программирование
Re: Reposirory в Yii2?
1) Проектируйте агрегаты адекватно. Не делайте ненужных связей. Можно для экономии хранить ID связанной сущности вместо её самой, если её поля агрегату не нужны. Либо, в крайнем случае, реализуйте ленивую загрузку.
2) При загрузке сохраняйте копию извлечённого массива $row. При сохранении сравнивайте со старым массивом, чтобы понять, какие связанные вещи изменились. Либо не сравнивайте и загоняйте всё пачкой в поле JSON.
2) При загрузке сохраняйте копию извлечённого массива $row. При сохранении сравнивайте со старым массивом, чтобы понять, какие связанные вещи изменились. Либо не сравнивайте и загоняйте всё пачкой в поле JSON.
Re: Reposirory в Yii2?
Спасибо.ElisDN писал(а): ↑2017.01.30, 10:34 1) Проектируйте агрегаты адекватно. Не делайте ненужных связей. Можно для экономии хранить ID связанной сущности вместо её самой, если её поля агрегату не нужны. Либо, в крайнем случае, реализуйте ленивую загрузку.
2) При загрузке сохраняйте копию извлечённого массива $row. При сохранении сравнивайте со старым массивом, чтобы понять, какие связанные вещи изменились. Либо не сравнивайте и загоняйте всё пачкой в поле JSON.
Если я правильно Вас понимаю, не избежать написание mapper на каждую сущность?
Re: Reposirory в Yii2?
Получать:
Код: Выделить всё
if (!$post = $this->db->createCommand('SELECT * FROM posts WHERE id = :id')->queryOne([':id' => $id])) {
throw new NotFoundException('Post not found.');
}
$photos = $this->db->createCommand('SELECT * FROM photos WHERE post_id = :id')->queryAll([':id' => $post['id']);
return $this->hydrator->hydrate(Post::class, [
'id' => new PostId($post['id']),
'date' => new DateTimeImmutable($post['created_at']),
'title' => $post['title'],
'photos' => array_map(function ($photo) {
return $this->hydrator->hydrate(Photo::class, [
'id' => new PhotoId($photo['id']),
'file' => $photo['file'],
]);
}, $photos);
]);
Тогда сделайте свой супер-маппер (который будет всё загонять один-в-один) и по умолчанию используйте его.
Последний раз редактировалось ElisDN 2017.01.30, 12:10, всего редактировалось 3 раза.
Re: Reposirory в Yii2?
Круто. Спасибо за дельные советы. Еще бы код с сохранением того же самого обратно в базу. Что-то подобное будет?ElisDN писал(а): ↑2017.01.30, 11:00Получать:
При сохранении сравнивать старый и новый массивы photos и делать batchInnsert добавленных и deleteAll удалённых.Код: Выделить всё
if (!$post = $this->db->createCommand('SELECT * FROM post WHERE id = :id')->queryOne([':id' => $id])) { throw new NotFoundException('Post not found.'); } $photos = $this->db->createCommand('SELECT * FROM photos WHERE post_id = :id')->queryAll([':id' => $post['id']); return $this->hydrator->hydrate(Post::class, [ 'id' => new PostId($row['id']), 'date' => new DateTimeImmutable($row['created_at']), 'title' => $row['title'], 'photos' => array_map(function ($photo) { return $this->hydrator->hydrate(Photo::class, [ 'id' => new PhotoId($photo['id']), 'file' => $photo['file'], ]); }, $photos); ]);
Тогда сделайте свой супер-маппер (который будет всё загонять один-в-один) и по умолчанию используйте его.
Код: Выделить всё
$this->db->createCommand()->update('posts', [
'id' => $post->getId()->getId(),
'created_at' => $post->getDate()->getTimestamp(),
'title' => $post->getTitle(),
])->execute();
foreach ($post->getPhotos() as $photo) {
$this->db->createCommand()->update('photos', [
['file' => $photo->getFile()],
['id' => $photo->getId()]
])->execute();
}
Все обернуть в транзакцию конечно же.
Re: Reposirory в Yii2?
На фотографии никто внешними ключами ссылаться не будет, поэтому здесь удобнее не заморачиваться с diff-ами, а просто удалить все старые photos и вставить заново:
Код: Выделить всё
$this->db->transaction(function (Connection $db) use ($post) {
$db->createCommand()->update('posts', [
'created_at' => $post->getDate()->getTimestamp(),
'title' => $post->getTitle(),
], ['id' => $post->getId()->getId()])->execute();
$db->createCommand()->delete('photos', ['post_id' => $post->getId()->getId()])->execute();
$db->createCommand()->batchInsert('photos', ['id', 'post_id', 'file'], array_map(function (Photo $photo) use ($post) {
return [
'id' => $photo->getId()->getId(),
'post_id' => $post->getId()->getId(),
'file' => $photo->getFile(),
];
}, $post->getPhotos()))->execute();
});
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: Reposirory в Yii2?
Смотря как с ними работают. Если по одной через API, то внешними ключами будет логичнее.
Нравится Yii? Давайте сделаем его лучше!.
Re: Reposirory в Yii2?
Александр, подскажите, пожалуйста, примеры кода выше - это правильный способ в Yii2 делать репозитории (на каждую сущность свой mapper) ?
Может есть какое-то более удачное решение?