Как правильно реализовать паттерн Repository?

capscom

Новичок
Всем привет!

При использовании паттерна у меня возникают трудности:

1. В случае связанных данных. Например, у нас есть сайт с объявлениями о продаже авто. На главной странице нужно вывести их список с пагинацией и фильтрами по свойствам:

Код:
SELECT (списокполей)
FROM Posts
LEFT JOIN Cars on Cars.id =Posts.carId
LEFT JOIN Cities on Cities.id =Posts.cityId
LEFT JOIN Regions on Regions.id =Cities.regionId
LEFT JOIN Countries on Countries.id =Regions.countryId
LEFT JOIN Models on Models.id =Cars.modelId
LEFT JOIN Brands on Brands.id =Models.brandId
WHERE Cars.condition ='new' AND Regions.id =1 AND Models.id =2
LIMIT 10,10
С точки зрения принципа "единой ответственности", в данном случае, нужно под каждую сущность/таблицу создать отдельные репозитори: BrandRepository, CountryRepository и т.д.
Но как тогда собрать такой запрос используя группу репозиториев, не нарушая принципа единой ответственности?

2. При создании объявления нужно произвести запись в 2 таблицы: Posts и Cars. Как в сервисе использовать транзакции между репозитариями. Другими словами, есть ли на php UnitOfWork ?
 

hell0w0rd

Продвинутый новичок
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/
С точки зрения принципа "единой ответственности", в данном случае, нужно под каждую сущность/таблицу создать отдельные репозитори: BrandRepository, CountryRepository и т.д.
Есть запрос, у него есть from часть. Относительно этой части и создаешь репозиторий, в данном случае PostsRepository. Все просто.
 

Вурдалак

Продвинутый новичок
Есть запрос, у него есть from часть. Относительно этой части и создаешь репозиторий, в данном случае PostsRepository. Все просто.
В общем случае это не так. Запрос может содержать различные группировки, суммы и т.д. (проекции), которые нельзя смаппить на сущности, т.е. по сути возвращается какая-то денормализованная модель «для чтения». Но в таком случае Repository не нужен.
 

capscom

Новичок
В общем случае это не так. Запрос может содержать различные группировки, суммы и т.д. (проекции), которые нельзя смаппить на сущности, т.е. по сути возвращается какая-то денормализованная модель «для чтения». Но в таком случае Repository не нужен.
Я правильно понимаю, что мой пример, как раз из случая который под Repository не подходит?
 

hell0w0rd

Продвинутый новичок
Я правильно понимаю, что мой пример, как раз из случая который под Repository не подходит?
нет.
Написали же, когда ты данные модифицируешь. Когда внутри SELECT не просто поля, а агрегирующие функции.
С другой стороны и тут можно и нужно использовать репозиторий, просто возвращать не объект, а массив, на пример. Или отдельные модели писать под это дело.
 

hell0w0rd

Продвинутый новичок
В общем случае это не так. Запрос может содержать различные группировки, суммы и т.д. (проекции), которые нельзя смаппить на сущности, т.е. по сути возвращается какая-то денормализованная модель «для чтения». Но в таком случае Repository не нужен.
Как я выше написал - ну нужна модель, а не репозиторий. Репозиторий - просто удобная форма сервиса для работы с бд.
 

Sufir

Я не волшебник, я только учусь
Я на данный момент пришел к такому варианту:

Репозиторий + сущности для записи
PHP:
$post = new Post();
$postRepository->save($post);
PHP:
$post = $postRepository->get(/*id*/);
$post->changePrice(100500);
QueryObject + DTO для чтения
PHP:
class FindPostsQuery {
    public setFilter();
    public execute();
}
PHP:
$postsList = $postsQuery->setFilter($filter)->execute(); // Возвращает коллекцию DTO или массивы, не сущности
Это позволяет сохранять сущности и репозитории от разрастания и чистыми от лишних данных и дополнительных ответственностей, и при этом удобным образом комбинировать данные нужным способом в различных местах, где происходит чтение.
 
Последнее редактирование:

capscom

Новичок
Я на данный момент пришел к такому варианту:

Репозиторий + сущности для записи
PHP:
$post = new Post();
$postRepository->save($post);
PHP:
$post = $postRepository->get(/*id*/);
$post->changePrice(100500);
QueryObject + DTO для чтения
PHP:
class FindPostsQuery {
    public setFilter();
    public execute();
}
PHP:
$postsList = $postsQuery->setFilter($filter)->execute(); // Возвращает коллекцию DTO или массивы, не сущности
Это позволяет сохранять сущности и репозитории от разрастания и чистыми от лишних данных и дополнительных ответственностей, и при этом удобным образом комбинировать данные нужным способом в различных местах, где происходит чтение.
Спасибо за совет. А как делает транзакции между репозиториями?
 

capscom

Новичок
нет.
Написали же, когда ты данные модифицируешь. Когда внутри SELECT не просто поля, а агрегирующие функции.
С другой стороны и тут можно и нужно использовать репозиторий, просто возвращать не объект, а массив, на пример. Или отдельные модели писать под это дело.
Тут такое дело спорное. Потому как после from может идти куча join-ов. А это уже нарушение принципа единой ответственности. Ведь с другими таблицами(сущностями) работают другие репозитории.
 

hell0w0rd

Продвинутый новичок
Тут такое дело спорное. Потому как после from может идти куча join-ов. А это уже нарушение принципа единой ответственности. Ведь с другими таблицами(сущностями) работают другие репозитории.
нет ничего спорного. На выходе мы получаем root-объект/их коллекцию, вот от этого и нужно плясать.
Если хочется с паттернами и чистым кодом повозиться - пиши запросы без джойнов, а джойны делай в коде отдельным сервисом, который не будет знать ничего о том, что джойнит, зато будет знать о том, как джойнить :D
 

Redjik

Джедай-мастер
Не надо мудрить, это обычная модель.
Спецификации тут не пришей кобыле хвост.
Никакого нарушения нет.
 

capscom

Новичок
нет ничего спорного. На выходе мы получаем root-объект/их коллекцию, вот от этого и нужно плясать.
Если хочется с паттернами и чистым кодом повозиться - пиши запросы без джойнов, а джойны делай в коде отдельным сервисом, который не будет знать ничего о том, что джойнит, зато будет знать о том, как джойнить :D
Ясно )


Не надо мудрить, это обычная модель.
Спецификации тут не пришей кобыле хвост.
Никакого нарушения нет.
Помудрить захотелось для красоты кода. Решил уйти на легковесное решение Entity + Repository + DataMapper + UnitOfWork. Маппера нормального не нашел, UnitOfWork в чистой реализации так же не нашел. А без этого полезность Repository крайне сомнительной становится.
 

Redjik

Джедай-мастер
Все что ты перечислил необходимо для отделения бизнес логики от реализации, пока ты мыслишь таблицами и крудами, тебе оно не нужно.
 
Сверху