Лучшая практика контроллеров Symfony

Я ищу лучшие практики в логике контроллера Symfony. Мой текущий код есть один контроллер:

<?php

namespace App\Controller;

use App\Entity\Categories;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class Controller extends AbstractController
{
    /**
     * @Route("/", name = "main_index")
     */
    public function index()
    {

        $categories = $this->getDoctrine()
            ->getRepository(Categories::class)
            ->findAll();

        return $this->render('index.html.twig', [
            'categories' => $categories,
        ]);
    }
}

категории Сущность:

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Categories
 *
 * @ORM\Table(name = "categories", indexes = {@ORM\Index(name = "title", columns = {"title"}), @ORM\Index(name = "url", columns = {"url"})})
 * @ORM\Entity
 */
class Categories
{
    /**
     * @var bool
     *
     * @ORM\Column(name = "id", type = "integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "IDENTITY")
     */
    private $id;

    /**
     * @var string|null
     *
     * @ORM\Column(name = "title", type = "string", length=255, nullable=true, options = {"default" = "NULL"})
     */
    private $title = 'NULL';

    /**
     * @var string|null
     *
     * @ORM\Column(name = "url", type = "string", length=255, nullable=true, options = {"default" = "NULL"})
     */
    private $url = 'NULL';

    /**
     * @var string|null
     *
     * @ORM\Column(name = "header_text", type = "text", length=65535, nullable=true, options = {"default" = "NULL"})
     */
    private $headerText = 'NULL';

    /**
     * @var string|null
     *
     * @ORM\Column(name = "body_text", type = "text", length=65535, nullable=true, options = {"default" = "NULL"})
     */
    private $bodyText = 'NULL';

    /**
     * @var string|null
     *
     * @ORM\Column(name = "footer_text", type = "text", length=65535, nullable=true, options = {"default" = "NULL"})
     */
    private $footerText = 'NULL';

    /**
     * @var \DateTime|null
     *
     * @ORM\Column(name = "created_at", type = "datetime", nullable=true, options = {"default" = "NULL"})
     */
    private $createdAt = 'NULL';

    /**
     * @var \DateTime|null
     *
     * @ORM\Column(name = "updated_at", type = "datetime", nullable=true, options = {"default" = "NULL"})
     */
    private $updatedAt = 'NULL';

    public function getId(): ?bool
    {
        return $this->id;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(?string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getUrl(): ?string
    {
        return $this->url;
    }

    public function setUrl(?string $url): self
    {
        $this->url = $url;

        return $this;
    }

    public function getHeaderText(): ?string
    {
        return $this->headerText;
    }

    public function setHeaderText(?string $headerText): self
    {
        $this->headerText = $headerText;

        return $this;
    }

    public function getBodyText(): ?string
    {
        return $this->bodyText;
    }

    public function setBodyText(?string $bodyText): self
    {
        $this->bodyText = $bodyText;

        return $this;
    }

    public function getFooterText(): ?string
    {
        return $this->footerText;
    }

    public function setFooterText(?string $footerText): self
    {
        $this->footerText = $footerText;

        return $this;
    }

    public function getCreatedAt(): ?\DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(?\DateTimeInterface $createdAt): self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getUpdatedAt(): ?\DateTimeInterface
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }


}

Я хочу повторить переменную "$ Categories" на всех остальных моих страницах. Значит - лишний запрос на каждой странице, но я не хочу повторять код:

$categories = $this->getDoctrine()
        ->getRepository(Categories::class)
        ->findAll();

везде, потому что все страницы должны постоянно отображать категории. Также по логике Symfony у всех @route должна быть собственная функция. Итак, как я предполагаю сделать логику маршрута и не повторять код запроса категорий? создав еще один внешний класс с этим кодом и просто повторно используя его во всех других методах маршрута?

Обновлено: Мое решение:

Файл шаблоны / index.html.twig (в одном месте):

 {{ render(controller('App\\Repository\\CategoriesListRepository::getCategories')) }}

шаблоны / категории.html.twig (один файл):

{% for category in categories %}
    <li>
        {{ category.getName() }}
    </li>
{% endfor %}

Репозиторий / CategoriesListRepository.php:

    <?php declare(strict_types=1);

namespace App\Repository;

use App\Entity\Categories;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class CategoriesListRepository
 * @package App\Repository
 */
final class CategoriesListRepository extends AbstractController
{


    /**
     * @var \Doctrine\Common\Persistence\ObjectRepository
     */
    private $repository;

    /**
     * CategoriesListRepository constructor.
     * @param EntityManagerInterface $entityManager
     */
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(Categories::class);
    }

    public function getCategories(): Response
    {
        return $this->render('categories.html.twig', ['categories' => $this->repository->findAll()]);
    }
}

было бы неплохо услышать некоторые комментарии / рекомендации

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
0
938
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Есть несколько возможных решений. Самым очевидным из них было бы создать подкласс AbstractController, добавить к нему метод protectedgetCategories() и вызвать этот метод из всех методов контроллера, где необходимы категории.

Но все же: это очень повторяющееся. Поэтому я, вероятно, добавлю специальную функцию Twig, чтобы в любом шаблоне, где она нужна, вы просто написали что-то вроде {{ displayCategories() }} или {% for category in getCategories() %} ... {% endfor %}, а расширение Twig сделало бы все это за вас. (Дополнительную информацию см. В документации Twig. Это несложно. Вам просто нужно внедрить Doctrine в качестве зависимости от конструктора расширения и перезаписать метод getFunctions() из Twig_Extension).

Ответ принят как подходящий

Вы можете вызвать контроллер из шаблона.

Создайте контроллер, который отображает только список категорий:

public function listAllAction()
{
    $categories = $this->getDoctrine()
        ->getRepository(Categories::class)
        ->findAll();

    return $this->render('categories.html.twig', [
        'categories' => $categories,
    ]);
}

а затем вызовите его с помощью функции render в файле ветки:

<div id = "list-of-categories">
    {{ render(controller(
        'App:Category:listAll',
    )) }}
</div>

Взгляните на Как вставлять переменные во все шаблоны (т.е. глобальные переменные).

Вы должны создать следующую конфигурацию:

# config/packages/twig.yaml
twig:
    # ...
    globals:
        # the value is the service's id
        category_service: '@App\Service\CategoryService'

А в вашем CategoryService вы получите все категории в рамках метода getCategories(). Позже вы можете вызвать свой шаблон ветки category_service.getCategories().

Вы можете использовать $ Categories в качестве переменной контейнера, используя метод $ container-> set (), а затем получить переменную с помощью $ container-> get ('Categories').

Если вы хотите только избежать повторения кода (иметь копию кода в разных местах), предыдущие ответы могут подойти. Но если вы не хотите снова и снова выполнять вызовы репозитория с целью оптимизации производительности базы данных, на мой взгляд, не рекомендуется перемещать код в места, за которые не несут ответственности (например, глобальные переменные twig).

Даже если результат может быть всегда одинаковым на разных страницах, возможно, что возвращаются разные результаты. Но об этом не стоит беспокоиться.

Вместо этого я бы использовал кеш результатов доктрины для своих запросов. Кеш должен решить, совпадают ли запрошенные данные с предыдущим запросом или нет, и предоставить мне эти данные.

Пока вы используете доктрину и не используете DBAL, вас это устроит.

Другие вопросы по теме