mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 17:10:53 +00:00
Refactor ResourceDeleteSubscriber
This commit is contained in:
parent
3d39f05fdf
commit
5691cea019
7 changed files with 277 additions and 43 deletions
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\AdminBundle\EventListener;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Provider\FlashBagProvider;
|
||||
use Sylius\Component\Core\Exception\ResourceDeleteException;
|
||||
use Sylius\Resource\ResourceActions;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
final readonly class ResourceDeleteExceptionListener
|
||||
{
|
||||
public function __construct(
|
||||
private UrlGeneratorInterface $router,
|
||||
private RequestStack $requestStack,
|
||||
) {
|
||||
}
|
||||
|
||||
public function onResourceDeleteException(ExceptionEvent $event): void
|
||||
{
|
||||
$exception = $event->getThrowable();
|
||||
|
||||
if (!$exception instanceof ResourceDeleteException) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlashBagProvider::getFlashBag($this->requestStack)->add('error', [
|
||||
'message' => 'sylius.resource.delete_error',
|
||||
'parameters' => ['%resource%' => $exception->getResourceName()],
|
||||
]);
|
||||
|
||||
$eventRequest = $event->getRequest();
|
||||
$requestAttributes = $eventRequest->attributes;
|
||||
$originalRoute = $requestAttributes->get('_route', '');
|
||||
|
||||
$referrer = $eventRequest->headers->get('referer');
|
||||
if (null !== $referrer) {
|
||||
$event->setResponse(new RedirectResponse($referrer));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event->setResponse($this->createRedirectResponse($originalRoute, ResourceActions::INDEX));
|
||||
}
|
||||
|
||||
private function createRedirectResponse(string $originalRoute, string $targetAction): RedirectResponse
|
||||
{
|
||||
$redirectRoute = str_replace(ResourceActions::DELETE, $targetAction, $originalRoute);
|
||||
|
||||
return new RedirectResponse($this->router->generate($redirectRoute));
|
||||
}
|
||||
}
|
||||
|
|
@ -14,31 +14,12 @@ declare(strict_types=1);
|
|||
namespace Sylius\Bundle\AdminBundle\EventListener;
|
||||
|
||||
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
|
||||
use Sylius\Bundle\CoreBundle\Provider\FlashBagProvider;
|
||||
use Sylius\Resource\ResourceActions;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Sylius\Component\Core\Exception\ResourceDeleteException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
final readonly class ResourceDeleteSubscriber implements EventSubscriberInterface
|
||||
final readonly class ResourceDeleteListener
|
||||
{
|
||||
public function __construct(
|
||||
private UrlGeneratorInterface $router,
|
||||
private RequestStack $requestStack,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::EXCEPTION => 'onResourceDelete',
|
||||
];
|
||||
}
|
||||
|
||||
public function onResourceDelete(ExceptionEvent $event): void
|
||||
{
|
||||
$exception = $event->getThrowable();
|
||||
|
|
@ -67,19 +48,7 @@ final readonly class ResourceDeleteSubscriber implements EventSubscriberInterfac
|
|||
return;
|
||||
}
|
||||
|
||||
FlashBagProvider::getFlashBag($this->requestStack)->add('error', [
|
||||
'message' => 'sylius.resource.delete_error',
|
||||
'parameters' => ['%resource%' => $resourceName],
|
||||
]);
|
||||
|
||||
$referrer = $eventRequest->headers->get('referer');
|
||||
if (null !== $referrer) {
|
||||
$event->setResponse(new RedirectResponse($referrer));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$event->setResponse($this->createRedirectResponse($originalRoute, ResourceActions::INDEX));
|
||||
throw new ResourceDeleteException($resourceName);
|
||||
}
|
||||
|
||||
private function getResourceNameFromRoute(string $route): string
|
||||
|
|
@ -92,13 +61,6 @@ final readonly class ResourceDeleteSubscriber implements EventSubscriberInterfac
|
|||
return trim(implode(' ', $routeArrayWithoutPrefixes));
|
||||
}
|
||||
|
||||
private function createRedirectResponse(string $originalRoute, string $targetAction): RedirectResponse
|
||||
{
|
||||
$redirectRoute = str_replace(ResourceActions::DELETE, $targetAction, $originalRoute);
|
||||
|
||||
return new RedirectResponse($this->router->generate($redirectRoute));
|
||||
}
|
||||
|
||||
private function isMethodDelete(Request $request): bool
|
||||
{
|
||||
return Request::METHOD_DELETE === $request->getMethod();
|
||||
|
|
@ -109,6 +71,7 @@ final readonly class ResourceDeleteSubscriber implements EventSubscriberInterfac
|
|||
return str_starts_with($route, 'sylius');
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $syliusParameters */
|
||||
private function isAdminSection(array $syliusParameters): bool
|
||||
{
|
||||
return array_key_exists('section', $syliusParameters) && 'admin' === $syliusParameters['section'];
|
||||
|
|
@ -26,10 +26,14 @@
|
|||
<tag name="kernel.event_listener" event="sylius.locale.pre_delete" method="preDelete" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.event_subscriber.resource_delete" class="Sylius\Bundle\AdminBundle\EventListener\ResourceDeleteSubscriber">
|
||||
<service id="sylius.event_listener.resource_delete_exception" class="Sylius\Bundle\AdminBundle\EventListener\ResourceDeleteExceptionListener">
|
||||
<argument type="service" id="router" />
|
||||
<argument type="service" id="request_stack" />
|
||||
<tag name="kernel.event_subscriber" event="kernel.exception" />
|
||||
<tag name="kernel.event_listener" event="kernel.exception" method="onResourceDeleteException" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.event_listener.resource_delete" class="Sylius\Bundle\AdminBundle\EventListener\ResourceDeleteListener">
|
||||
<tag name="kernel.event_listener" event="kernel.exception" method="onResourceDelete" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.event_subscriber.admin_cache_control_subscriber" class="Sylius\Bundle\AdminBundle\EventListener\AdminSectionCacheControlSubscriber">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\AdminBundle\EventListener;
|
||||
|
||||
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Prophecy\Argument;
|
||||
use Sylius\Component\Core\Exception\ResourceDeleteException;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
final class ResourceDeleteExceptionListenerSpec extends ObjectBehavior
|
||||
{
|
||||
function let(UrlGeneratorInterface $router, RequestStack $requestStack): void
|
||||
{
|
||||
$this->beConstructedWith($router, $requestStack);
|
||||
}
|
||||
|
||||
function it_does_nothing_if_exception_is_not_resource_delete_exception(
|
||||
KernelInterface $kernel,
|
||||
ForeignKeyConstraintViolationException $exception,
|
||||
): void {
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), new Request(), HttpKernelInterface::MAIN_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDeleteException($event)->shouldReturn(null);
|
||||
}
|
||||
|
||||
function it_redirects_to_referer_if_present_and_adds_flash_message(
|
||||
KernelInterface $kernel,
|
||||
Request $request,
|
||||
RequestStack $requestStack,
|
||||
FlashBagInterface $flashBag,
|
||||
UrlGeneratorInterface $router,
|
||||
SessionInterface $session,
|
||||
): void {
|
||||
$request->attributes = new ParameterBag(['_route' => 'sylius_admin_product_delete']);
|
||||
$request->headers = new HeaderBag(['referer' => '/admin/product/index']);
|
||||
$exception = new ResourceDeleteException('Product');
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), $request->getWrappedObject(), HttpKernelInterface::MAIN_REQUEST, $exception);
|
||||
|
||||
$requestStack->getSession()->willReturn($session);
|
||||
$session->getBag('flashes')->willReturn($flashBag);
|
||||
$flashBag->add('error', ['message' => 'sylius.resource.delete_error', 'parameters' => ['%resource%' => 'Product']])->shouldBeCalled();
|
||||
|
||||
$router->generate(Argument::cetera())->shouldNotBeCalled();
|
||||
|
||||
$this->onResourceDeleteException($event);
|
||||
}
|
||||
|
||||
function it_redirects_to_index_when_no_referer_present_and_adds_flash_message(
|
||||
KernelInterface $kernel,
|
||||
Request $request,
|
||||
RequestStack $requestStack,
|
||||
FlashBagInterface $flashBag,
|
||||
UrlGeneratorInterface $router,
|
||||
SessionInterface $session,
|
||||
): void {
|
||||
$request->attributes = new ParameterBag(['_route' => 'sylius_admin_product_delete']);
|
||||
$request->headers = new HeaderBag();
|
||||
$exception = new ResourceDeleteException('Product');
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), $request->getWrappedObject(), HttpKernelInterface::MAIN_REQUEST, $exception);
|
||||
|
||||
$requestStack->getSession()->willReturn($session);
|
||||
$session->getBag('flashes')->willReturn($flashBag);
|
||||
$flashBag->add('error', ['message' => 'sylius.resource.delete_error', 'parameters' => ['%resource%' => 'Product']])->shouldBeCalled();
|
||||
|
||||
$router->generate('sylius_admin_product_index')->willReturn('/admin/product/index');
|
||||
|
||||
$this->onResourceDeleteException($event);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace spec\Sylius\Bundle\AdminBundle\EventListener;
|
||||
|
||||
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Component\Core\Exception\ResourceDeleteException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
final class ResourceDeleteListenerSpec extends ObjectBehavior
|
||||
{
|
||||
function it_does_nothing_if_exception_is_not_foreign_key_constraint(KernelInterface $kernel): void
|
||||
{
|
||||
$this->onResourceDelete(new ExceptionEvent($kernel->getWrappedObject(), new Request(), HttpKernelInterface::MAIN_REQUEST, new \Exception()))->shouldReturn(null);
|
||||
}
|
||||
|
||||
function it_does_nothing_if_request_is_not_main(KernelInterface $kernel, ForeignKeyConstraintViolationException $exception): void
|
||||
{
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), new Request(), HttpKernelInterface::SUB_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDelete($event)->shouldReturn(null);
|
||||
}
|
||||
|
||||
function it_should_throw_resource_delete_exception_if_all_conditions_are_met(KernelInterface $kernel, ForeignKeyConstraintViolationException $exception): void
|
||||
{
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), new Request(), HttpKernelInterface::MAIN_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDelete($event)->shouldThrow(ResourceDeleteException::class);
|
||||
}
|
||||
|
||||
function it_should_do_nothing_if_method_is_not_delete(KernelInterface $kernel, ForeignKeyConstraintViolationException $exception): void
|
||||
{
|
||||
$request = new Request([], [], ['_route' => 'sylius_admin_product_delete', '_sylius' => ['section' => 'admin']]);
|
||||
$request->setMethod('GET');
|
||||
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), $request, HttpKernelInterface::MAIN_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDelete($event)->shouldReturn(null);
|
||||
}
|
||||
|
||||
function it_should_do_nothing_if_route_is_not_sylius(KernelInterface $kernel, ForeignKeyConstraintViolationException $exception): void
|
||||
{
|
||||
$request = new Request([], [], ['_route' => 'non_sylius_route', '_sylius' => ['section' => 'admin']]);
|
||||
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), $request, HttpKernelInterface::MAIN_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDelete($event)->shouldReturn(null);
|
||||
}
|
||||
|
||||
function it_should_do_nothing_if_section_is_not_admin(KernelInterface $kernel, ForeignKeyConstraintViolationException $exception): void
|
||||
{
|
||||
$request = new Request([], [], ['_route' => 'sylius_admin_product_delete', '_sylius' => ['section' => 'shop']]);
|
||||
|
||||
$event = new ExceptionEvent($kernel->getWrappedObject(), $request, HttpKernelInterface::MAIN_REQUEST, $exception->getWrappedObject());
|
||||
|
||||
$this->onResourceDelete($event)->shouldReturn(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -119,6 +119,11 @@
|
|||
<tag name="kernel.event_listener" event="sylius.product.pre_delete" method="protectFromRemovingProductInUseByPromotionRule" />
|
||||
</service>
|
||||
|
||||
<service id="sylius.listener.product_option_value" class="Sylius\Bundle\CoreBundle\EventListener\ProductOptionValueDeletionListener">
|
||||
<argument type="service" id="sylius.repository.product_variant" />
|
||||
<tag name="doctrine.orm.entity_listener" event="preRemove" entity="%sylius.model.product_option_value.class%" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\CoreBundle\EventListener\PostgreSQLDefaultSchemaListener">
|
||||
<tag name="doctrine.event_listener" event="postGenerateSchema" method="postGenerateSchema" lazy="true" />
|
||||
</service>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Component\Core\Exception;
|
||||
|
||||
class ResourceDeleteException extends \RuntimeException
|
||||
{
|
||||
public function __construct(
|
||||
private string $resourceName,
|
||||
string $message = '',
|
||||
int $code = 0,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
if (empty($message)) {
|
||||
$message = sprintf('Cannot delete, the %s is in use.', $resourceName);
|
||||
}
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return $this->resourceName;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue