#symfony #action #symfony4
#symfony #Экшен #symfony4
Вопрос:
Я сторонник подхода с использованием класса Action вместо контроллера. Объяснение очень простое: очень часто Контроллер включает в себя множество действий, когда, следуя принципу внедрения зависимостей, мы должны передать все требуемые зависимости конструктору, и это создает ситуацию, когда у контроллера огромное количество зависимостей, но в определенный момент времени (например, запрос) мы используем только некоторые зависимости. Сложно поддерживать и тестировать этот спагетти-код.
Чтобы пояснить, я уже работал с этим подходом в Zend Framework 2, но там он называется Middleware. Я нашел нечто подобное в API-Platform, где они также используют Action class вместо Controller, но проблема в том, что я не знаю, как это приготовить.
UPD: Как я могу получить следующий класс Action и заменить стандартный контроллер и какую конфигурацию я должен добавить в обычный проект Symfony?
<?php
declare(strict_types=1);
namespace AppActionProduct;
use AppEntityProduct;
use DoctrineORMEntityManager;
use SensioBundleFrameworkExtraBundleConfigurationMethod;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAnnotationRoute;
class SoftDeleteAction
{
/**
* @var EntityManager
*/
private $entityManager;
/**
* @param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* @Route(
* name="app_product_delete",
* path="products/{id}/delete"
* )
*
* @Method("DELETE")
*
* @param Product $product
*
* @return Response
*/
public function __invoke(Request $request, $id): Response
{
$product = $this->entityManager->find(Product::class, $id);
$product->delete();
$this->entityManager->flush();
return new Response('', 204);
}
}
Комментарии:
1. Такого рода вопросы, вероятно, лучше подходят для форума Reddit Symfony. Сказав это, действие — это просто контроллер с единственным методом действия. На самом деле ничего не нужно реализовывать. Вы могли бы рассмотреть возможность обновления вашего вопроса с более подробной информацией, прежде чем он будет закрыт.
2. Я взглянул на ваш связанный класс SoftDeleteAction. Я должен отметить, что вопросы только по ссылкам, как правило, вызывают такое же неодобрение, как и ответы только по ссылкам. В любом случае код должен работать «из коробки», за исключением внедрения объекта Product. Самое простое решение — ввести идентификатор, а затем использовать entity manager для его извлечения.
3. @Cerad Я отредактировал свой вопрос и добавил пример кода. Спасибо за ваши разъяснения.
4. Я подозреваю, что ваш маршрут не отслеживается? Из коробки обработчик аннотаций маршрута просматривает только каталог контроллера (config/routes/annotation.yaml). Самое простое решение — просто определить свой маршрут в config / routes.yaml. Поскольку вы используете метод __invoke, то вам просто нужен класс action для _controller. Метод=DELETE также может вызвать некоторые проблемы. На данный момент просто используйте POST, а затем проверьте документы о том, как подделать использование DELETE.
5. @Cerad Можете ли вы более подробно объяснить, как настраивать маршруты? Теперь у меня есть следующая конфигурация в моем
app/config/routing.yml
(см. Экран i.imgur.com/aRptcwd.png ) app_product: ресурс: «@AppProduct/Controller/» тип: префикс аннотации: /
Ответ №1:
Подход, который я пытался реализовать, называется ADR pattern (Action-Domain-Responder), и Symfony уже поддерживает это, начиная с версии 3.3. Вы можете ссылаться на него как на вызываемые контроллеры.
Из официальных документов:
Контроллеры также могут определять отдельное действие с помощью метода __invoke(), что является обычной практикой при следовании шаблону ADR (Действие-Домен-ответчик):
// src/Controller/Hello.php
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAnnotationRoute;
/**
* @Route("/hello/{name}", name="hello")
*/
class Hello
{
public function __invoke($name = 'World')
{
return new Response(sprintf('Hello %s!', $name));
}
}
Ответ №2:
Вопрос немного расплывчатый для stackoverflow, хотя он также немного интересен. Итак, вот некоторые детали настройки.
Начните с готового проекта-скелета S4:
symfony new --version=lts s4api
cd s4api
bin/console --version # 4.4.11
composer require orm-pack
Добавьте SoftDeleteAction
namespace AppActionProduct;
class SoftDeleteAction
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function __invoke(Request $request, int $id) : Response
{
return new Response('Product ' . $id);
}
}
И определите маршрут:
# config/routes.yaml
app_product_delete:
path: /products/{id}/delete
controller: AppActionProductSoftDeleteAction
На этом этапе подключение почти завершено. Если вы перейдете по URL-адресу, вы получите:
The controller for URI "/products/42/delete" is not callable:
Причина в том, что службы по умолчанию являются частными. Обычно вы расширяетесь от AbstractController, который заботится о том, чтобы сделать службу общедоступной, но в этом случае самый быстрый подход — просто пометить действие как контроллер:
# config/services.yaml
AppActionProductSoftDeleteAction:
tags: ['controller.service_arguments']
На этом этапе у вас должно быть рабочее подключенное действие.
Конечно, есть много вариантов и еще несколько деталей. Вы захотите ограничить маршрут публикацией или поддельным удалением.
Вы также можете рассмотреть возможность добавления пустого интерфейса ControllerServiceArgumentsInterface, а затем с помощью функциональности services instanceof применить тег controller, чтобы вам больше не нужно было вручную определять свои службы контроллера.
Но этого должно быть достаточно, чтобы вы начали.