Reposirory в Yii2?

Обсуждаем, как правильно строить приложения
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Reposirory в Yii2?

Сообщение paurlift »

Знаю что в 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. Но, к сожалению, не все такие прилежные :)
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Reposirory в Yii2?

Сообщение zelenin »

paurlift писал(а): 2017.01.29, 20:26Выхода вижу два :
1) Писать велосипеды со своими Mapper's
2) Взять Doctrine
дело вкуса
paurlift писал(а): 2017.01.29, 20:261) Есть ли хорошее решение для Yii2? (Сохранение агрегатов и моделей из нескольких таблиц в БД, получение из БД заполненной entity)
что тут конкретно yii2-ответственность? для чего искать что-то к yii2, если мы наоборот хотим создать фреймворко-независимое решение?
paurlift писал(а): 2017.01.29, 20:262) На сколько удобно использовать Doctrine в связки с Yii2, есть ли у кого-нибудь практический опыт использования такой связки?
точно так же, как и в любом другом движке - устанавливаем, инициализируем, используем как любую стороннюю библиотеку.
paurlift писал(а): 2017.01.29, 20:26P.S. На одном из последних докладов - "Yii2: Структура большого проекта" уважаемый Александр Макаров, сказал, что можно строить на Yii2 такие связки, но не рассказал как, сказав только, что он как прилежный разработчик возвращает AR репозиторием, но нигде не использует его функции для работы с persistence. Но, к сожалению, не все такие прилежные :)
надо либо использовать AR без репозиториев, либо не использовать AR вообще.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

1 вопрос тогда перефразирую, какие расширения можно использовать, чтобы написать реализацию repository, которые смогут сохранять(получать) aggregate и entity любой сложности.

Писать свой mapper на каждую сущность очень не рационально (тем более есть минимум одно готовое решение - Doctrine)

Александр Макаров сказал, что есть какое-то решение...
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Reposirory в Yii2?

Сообщение zelenin »

paurlift писал(а): 2017.01.29, 22:24 1 вопрос тогда перефразирую, какие расширения можно использовать, чтобы написать реализацию repository, которые смогут сохранять(получать) aggregate и entity любой сложности.
никаких расширений нет. используйте любую ORM.
paurlift писал(а): 2017.01.29, 22:24Писать свой mapper на каждую сущность очень не рационально
частично так, частично нет
paurlift писал(а): 2017.01.29, 22:24(тем более есть минимум одно готовое решение - Doctrine)
в котором вы будете писать не мапперы, но маппинги.
paurlift писал(а): 2017.01.29, 22:24Александр Макаров сказал, что есть какое-то решение...
он не имел в виду что-то готовое.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: Reposirory в Yii2?

Сообщение anton_z »

Маппинги будут нужны как минимум. Yii AR полагается на динамизм php и драйверы. Поэтому можно увидеть строку в числовом атрибуте.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Reposirory в Yii2?

Сообщение ElisDN »

paurlift писал(а): 2017.01.29, 20:26 1) Есть ли хорошее решение для Yii2? (Сохранение агрегатов и моделей из нескольких таблиц в БД, получение из БД заполненной entity)
В простейшем случае ручная вставка:

Код: Выделить всё

$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.
paurlift писал(а): 2017.01.29, 20:26 2) На сколько удобно использовать Doctrine в связки с Yii2, есть ли у кого-нибудь практический опыт использования такой связки? Какие неудобства могут возникнуть? (В проекте нет Foreign key)
Нюанс - необходимость использования ArrayCollection вместо массивов или своих коллекций для связей.
Последний раз редактировалось ElisDN 2017.03.24, 12:44, всего редактировалось 2 раза.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

zelenin писал(а): 2017.01.29, 23:09будете писать не мапперы, но маппинги.
Да, но часть сущностей мапятся 1 к 1 на БД и в Doctrine есть консоль, которая позволяет сгенерировать маппинги.
Для тех сущностей, которые собираются из нескольких таблиц, большая часть все равно лежит в какой-то таблице, с помощью консоли уменьшаем ручной труд.

И все-таки связность БД, entity, repositroy вынесены в один .yml файл, что очень удобно. Для такого рода маппинга есть уже форма описания в .yml файлах, не нужно изобретать велосипед.
zelenin писал(а): 2017.01.29, 23:09он не имел в виду что-то готовое.
Наверно, но про какое-то решение было вскользь сказано и я как понял это не Doctrine
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Reposirory в Yii2?

Сообщение zelenin »

paurlift писал(а): 2017.01.30, 09:36
zelenin писал(а): 2017.01.29, 23:09будете писать не мапперы, но маппинги.
Да, но часть сущностей мапятся 1 к 1 на БД и в Doctrine есть консоль, которая позволяет сгенерировать маппинги.
Для тех сущностей, которые собираются из нескольких таблиц, большая часть все равно лежит в какой-то таблице, с помощью консоли уменьшаем ручной труд.

И все-таки связность БД, entity, repositroy вынесены в один .yml файл, что очень удобно. Для такого рода маппинга есть уже форма описания в .yml файлах, не нужно изобретать велосипед.
написание маппера - это один класс на сущность из 20-30 строк. По времени это столько же сколько вы будете тратить на маппинги доктрины. Только маппер у вас будет лакончиным, понятным, а доктрина будет черным ящиком с магией внутри с десятиктратным повышением потребления памяти.
paurlift писал(а): 2017.01.30, 09:36
zelenin писал(а): 2017.01.29, 23:09он не имел в виду что-то готовое.
Наверно, но про какое-то решение было вскользь сказано и я как понял это не Doctrine
он не имел в виду что-то готовое для yii2. А брать вы можете любую ORM.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

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 таблицах.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

zelenin писал(а): 2017.01.30, 09:47 написание маппера - это один класс на сущность из 20-30 строк. По времени это столько же сколько вы будете тратить на маппинги доктрины. Только маппер у вас будет лакончиным, понятным, а доктрина будет черным ящиком с магией внутри с десятиктратным повышением потребления памяти.
Да, Doctrine ест больше памяти - это минус. А то что она является черным ящиков, нельзя отнести к минусам, вещь в себе, которая хорошо решает свои задачи.

Это не 20-30 строк кода, если сохраняем aggregate. При сохранение нужно писать разбор. При вытаскивание по id aggregate тоже нужно писать свое заполнение.

Можете прокомментировать мой пост выше? Спасибо :)
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Reposirory в Yii2?

Сообщение zelenin »

могу: вы программирование и написание кода считаете "сущим адом" плюс, как кажется, не понимаете какую цель репозиторий преследует и, главное, зачем оно вам нужно. Попробуйте то, о чем спрашиваете, начать реализовывать. Потом обсудим процесс.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

zelenin писал(а): 2017.01.30, 10:24 могу: вы программирование и написание кода считаете "сущим адом" плюс, как кажется, не понимаете какую цель репозиторий преследует и, главное, зачем оно вам нужно. Попробуйте то, о чем спрашиваете, начать реализовывать. Потом обсудим процесс.
Спасибо. Очень помогли :)
А если серьезно, я попробовал и понимаю зачем мне это нужно и меня не покидает ощущение написание своего велосипеда.
Привел конкретный пример, когда велосипед становится прям 3-х колесным.

Как мне сохранять и получать aggregate?


P.S. Обожаю программирование :)
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Reposirory в Yii2?

Сообщение ElisDN »

1) Проектируйте агрегаты адекватно. Не делайте ненужных связей. Можно для экономии хранить ID связанной сущности вместо её самой, если её поля агрегату не нужны. Либо, в крайнем случае, реализуйте ленивую загрузку.

2) При загрузке сохраняйте копию извлечённого массива $row. При сохранении сравнивайте со старым массивом, чтобы понять, какие связанные вещи изменились. Либо не сравнивайте и загоняйте всё пачкой в поле JSON.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

ElisDN писал(а): 2017.01.30, 10:34 1) Проектируйте агрегаты адекватно. Не делайте ненужных связей. Можно для экономии хранить ID связанной сущности вместо её самой, если её поля агрегату не нужны. Либо, в крайнем случае, реализуйте ленивую загрузку.

2) При загрузке сохраняйте копию извлечённого массива $row. При сохранении сравнивайте со старым массивом, чтобы понять, какие связанные вещи изменились. Либо не сравнивайте и загоняйте всё пачкой в поле JSON.
Спасибо.

Если я правильно Вас понимаю, не избежать написание mapper на каждую сущность?
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Reposirory в Yii2?

Сообщение ElisDN »

paurlift писал(а): 2017.01.30, 10:34 Как мне сохранять и получать aggregate?
Получать:

Код: Выделить всё

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);
]);
При сохранении сравнивать старый и новый массивы photos и делать batchInnsert добавленных и deleteAll удалённых.
paurlift писал(а): 2017.01.30, 10:34 Да, но часть сущностей мапятся 1 к 1 на БД... Если я правильно Вас понимаю, не избежать написание mapper на каждую сущность?
Тогда сделайте свой супер-маппер (который будет всё загонять один-в-один) и по умолчанию используйте его.
Последний раз редактировалось ElisDN 2017.01.30, 12:10, всего редактировалось 3 раза.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

ElisDN писал(а): 2017.01.30, 11:00
paurlift писал(а): 2017.01.30, 10:34 Как мне сохранять и получать aggregate?
Получать:

Код: Выделить всё

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);
]);
При сохранении сравнивать старый и новый массивы photos и делать batchInnsert добавленных и deleteAll удалённых.
paurlift писал(а): 2017.01.30, 10:34 Да, но часть сущностей мапятся 1 к 1 на БД... Если я правильно Вас понимаю, не избежать написание mapper на каждую сущность?
Тогда сделайте свой супер-маппер (который будет всё загонять один-в-один) и по умолчанию используйте его.
Круто. Спасибо за дельные советы. Еще бы код с сохранением того же самого обратно в базу. Что-то подобное будет?

Код: Выделить всё


$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();
} 

Все обернуть в транзакцию конечно же.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Reposirory в Yii2?

Сообщение ElisDN »

paurlift писал(а): 2017.01.30, 11:17 Еще бы код с сохранением того же самого обратно в базу. Что-то подобное будет?
На фотографии никто внешними ключами ссылаться не будет, поэтому здесь удобнее не заморачиваться с 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?

Сообщение samdark »

Смотря как с ними работают. Если по одной через API, то внешними ключами будет логичнее.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Reposirory в Yii2?

Сообщение ElisDN »

samdark писал(а): 2017.01.30, 13:15 Смотря как с ними работают. Если по одной через API, то внешними ключами будет логичнее.
Без разницы. Даже по API с ними логичнее работать через $post.
paurlift
Сообщения: 26
Зарегистрирован: 2017.01.29, 20:16

Re: Reposirory в Yii2?

Сообщение paurlift »

samdark писал(а): 2017.01.30, 13:15 Смотря как с ними работают. Если по одной через API, то внешними ключами будет логичнее.
Александр, подскажите, пожалуйста, примеры кода выше - это правильный способ в Yii2 делать репозитории (на каждую сущность свой mapper) ?
Может есть какое-то более удачное решение?
Ответить