AmberLEX
@AmberLEX
php/web-developer

Репозиторий, как правильно организовать однотипные запросы?

Учусь.
Стандартный блог (категории, посты, теги).
Подскажите как правильно организовать код. А то какой-то говнокод получается.

В админке есть Posts, Categories, Tags
При нажатии в меню на Posts - вижу все посты
При нажатии в меню на Categories - вижу категории и количество постов к каждой
При нажатии в меню на Tags - вижу теги и количество постов к каждому
При нажатии на количество постов из меню Categories или Tags - попадаю в Posts и вижу соответстующие посты (типа такой фильтр выходит)

Вот по топорному сделал class PostController (такое в контроллере нельзя)
#[Route('', name: 'index', methods: ['GET'])]
public function index(
    PostRepository $postRepository,
    CategoryRepository $categoryRepository,
    TagRepository $tagRepository,
    Request $request,
    PaginatorInterface $paginator
): Response {
    if ($category = $request->query->getInt('category')) {
        $category = $categoryRepository->find($category);
        $posts = $category
            ? $category->getPosts()
            : $postRepository->findAllOrderedByNewest();
    } elseif ($tag = $request->query->getInt('tag')) {
        $tag = $tagRepository->find($tag);
        $posts = $tag
            ? $tag->getPosts()
            : $postRepository->findAllOrderedByNewest();
    } else {
        $posts = $postRepository->findAllOrderedByNewest();
    }

    $posts = $paginator->paginate($posts, $request->query->getInt('page', 1));

    return $this->render('admin/blog/post/index.html.twig', compact('posts'));
}

Если id категории или тега не переданы, использую findAllOrderedByNewest()
Вместо CategoryRepository и TagRepository - можно написать методы получения соответствующих постов в PostRepository (ща их нет) и убрать CategoryRepository и TagRepository из параметров function index() (и вообще нуужно ли?)

Для постов в PostRepository есть методы.
// PostRepository

// Для получения списка в админке
public function findAllOrderedByNewest()
{
    return $this->createQueryBuilder('p')
        ->leftJoin('p.category', 'c')
        ->addSelect('c')
        ->leftJoin('p.tags', 't')
        ->addSelect('t')
        ->orderBy('p.createdAt', 'DESC')
        ->getQuery()
        ->getResult();
}
// Для получения списка активных на фронте
public function findAllActiveOrderedByNewest()
{
    return $this->createQueryBuilder('p')
        ->leftJoin('p.category', 'c')
        ->addSelect('c')
        ->leftJoin('p.tags', 't')
        ->addSelect('t')
        ->andWhere('p.isActive = true')
        ->andWhere('c.isActive = true')
        ->orderBy('p.createdAt', 'DESC')
        ->getQuery()
        ->getResult();
}
// Для получения одного поста на фронте
public function findOneActiveBySlug(string $slug): ?Post
{
    return $this->createQueryBuilder('p')
        ->select('p', 'c')
        ->where('p.slug = :slug')
        ->setParameter('slug', $slug)
        ->leftJoin('p.category', 'c')
        ->andwhere('p.isActive = true')
        ->andWhere('c.isActive = true')
        ->setMaxResults(1)
        ->getQuery()
        ->getOneOrNullResult();
}
// Для получения списка постов категории на фронте
public function findAllActiveInCategory(Category $category)
{
    return $this->createQueryBuilder('p')
        ->select('p', 't')
        ->leftJoin('p.tags', 't')
        ->where('p.category = :category')
        ->setParameter('category', $category)
        ->andWhere('p.isActive = true')
        ->orderBy('p.createdAt', 'DESC')
        ->getQuery()
        ->getResult();
}

Чтобы не использовать CategoryRepository и TagRepository (как говорил выше) можно сюда еще дописать нужные методы.
Это я точно что-то не так делаю. Это ж сколько кода будет (ну можно конечно в методы параметры передавать и по кусочкам внутри собрать запрос, только попробуй потом разберись в этом методе через полгода).

До этого работал на Ларавел, для подобного, кода выходит в разы меньше (есть билдер как тут доктриновский, есть ORM Eloquent где вообще все тремя словами записывается). Здесь не пойму как вообще правильно делается.

Например список постов категории на фронте можно получить через $category->getPosts(), а не через findAllActiveInCategory(), хотя тогда все равно придется писать условия выборки как-то, как?)

Получается в репозиториях вообще все через билдер делается - т.е. обычный sql только запись через билдер?
Как это вообще все делают или как хотя бы это нормально сделать?
  • Вопрос задан
  • 93 просмотра
Пригласить эксперта
Ответы на вопрос 1
@heartdevil
плыву как воздушный шарик
Я не знаю ни laravel ни symfony, но может идейно помогу.
Кода, скорее всего, будет больше, просто потому что вы все пишете сами. Меньше может стать только если вы что-то там доустановите в симфони и будете пользоваться этой надстройкой. Но идейно, у вас есть уже разделение на репозитории и вам нужен еще один слой бизнес логики. Вы можете и бизнес логику хранить в репозиториях, но тогда вопрос, а зачем вообще нужен этот слой репозиториев, если все можно хранить в контроллерах :). Вообще, репозитории должны быть как можно более общими. Какие-то простые запросы в базу типа, вернуть все или вернуть все у чего id равен чему-то. Такие вещи как флаги или сортировка, тоже можно уже сразу встроить в запросы, но обычно флаги - это уже бизнес логика. Так же как и какая-то кастомная сортировка. Для тких "хитрых" запросов обычно выделяется еще один слой. Бизнес-логика. К примеру, называем его Services и туда пробрасываем нашим репозитории и на основе них уже делаются методы типа GetActiveSoretedFilteredAndSoOnService. Ну и далее сервисы пробрасываются в контроллеры и конроллеры знают только про них. А про контроллеры вы правильно заметили. Такую логику переключений лучше там не держать. Хотя бывает разное. Да и вопрос, а зачем так переключать запросы в контроллере? В чем выгода? Может лучше на каждый пункт меню сделать отдельный метод get в контроллере? Или это специфика symfony? Что все через index надо делать.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы