SlideShare a Scribd company logo
Typical pitfalls
of Sylius development
Thank you!
Why do we do this?
😂😂😂
AI is not a threat for now
Q&A
Performance
N+1
Full stack mode
Headless mode
Product list
Let’s start small
{
"@context":"/api/contexts/Product",
"@id":"/api/products",
"@type":"hydra:Collection",
"hydra:totalItems":6,
"hydra:member":[
{
"@id":"/api/products/1",
"@type":"Product",
"id":1,
"name":"T-Shirt",
"price":1000
},
{
"@id":"/api/products/2",
"@type":"Product",
"id":2,
"name":"Trousers",
"price":5000
},
{
"“…“":"“…“"
}
]
}
Amount of products: 6
No associations between objects
Sample query on the left
O(1)
Order list
How many queries are executed here?
{
"@context": "/api/contexts/Order",
"@id": "/api/orders",
"@type": "hydra:Collection",
"hydra:totalItems": 3,
"hydra:member": [
{
"@id": "/api/orders/1",
"@type": "Order",
"id": 1,
"orderItems": [
"/api/order_items/1",
"/api/order_items/2"
]
},
{
“…”: “…“
}
]
}
Amount of orders: 3
Every order associated with 2 items
Sample query on the left
No total
fi
eld
O(M)
Order list with additional
fi
eld
How many queries are executed here?
{
"@context": "/api/contexts/Order",
"@id": "/api/orders",
"@type": "hydra:Collection",
"hydra:totalItems": 3,
"hydra:member": [
{
"@id": "/api/orders/1",
"@type": "Order",
"id": 1,
"orderItems": [
"/api/order_items/1",
"/api/order_items/2"
],
"total": 7000
},
{
“…”: “…“
}
]
}
Amount of orders: 3
Every order associated with 2 items
Sample query on the left
Added “total()” as a function of product
price and quantity of item
O(M*N) or O(M^2) 🚀
How to
fi
x it?
Solution 1
#[ORMOneToMany(fetch: ‘LAZY')] => 11
#[ORMOneToMany(fetch: ‘EXTRA_LAZY')] => 11
#[ORMOneToMany(fetch: ‘EAGER')] => 5 (OrderItem and Product)
#[ORMOneToMany(fetch: ‘EAGER')] (OrderItem and Product) + Serialisation groups => 4
Solution 2
final class LoadItemsAndProductsExtension implements QueryCollectionExtensionInterface,
QueryItemExtensionInterface
{
private function apply(QueryBuilder $queryBuilder): void
{
$queryBuilder
->addSelect('oi', 'p')
->join(OrderItem::class, 'oi', Join::WITH, 'oi.originOrder = o')
->join(Product::class, 'p', Join::WITH, 'oi.product = p')
;
}
}
But….
{
"@context": "/api/contexts/Order",
"@id": "/api/orders",
"@type": "hydra:Collection",
"hydra:totalItems": 3,
"hydra:member": [
{
"@id": "/api/orders/1",
"@type": "Order",
"id": 1,
"orderItems": [
"/api/order_items/1",
"/api/order_items/2"
],
"total": 7000
},
{
"@id": "/api/order_items/1",
"@type": "OrderItem",
"id": 1,
"product": "/api/products/1",
"quantity": 2,
"originOrder": "/api/orders/1",
"price": 2000
},
{
"@id": "/api/products/1",
"@type": "Product",
"id": 1,
"name": "T-Shirt",
"price": 1000
},
{
"@id": "/api/order_items/2",
"@type": "OrderItem",
"id": 2,
"product": "/api/products/2",
"quantity": 1,
"originOrder": "/api/orders/1",
"price": 5000
},
{
“…”: “…”
}
]
}
So is single query an ultimate
solution?
Concurrency from di
ff
erent
users
Sequential order number
generator
Does it need to be
sequential?
Or defer it 😉
Locking mechanism of
product variants (inventory)
What is locked?
Concurrency from the same
user
Order Processing
How does Doctrine collection
handling works?
Tests
Contract tests
Functional tests
You don’t need to do BDD
to work with Sylius
Although we ecourage you to do so
No. of Behat scenarios in our
latest successfull Sylius project:
0
No. of Behat scenarios in our
latest successfull Sylius project:
Testing documentation
Payments
Payments
Current
fl
ow
Payment after checkout
Typical request
Payment before completion
What we want to achieve
Authorization during checkout
Flexible capture moment
Core Team
is actively working on it
Resource
Resource
Regular Symfony
controllers
<?php
/*
* This
fi
le is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
*
fi
le that was distributed with this source code.
*/
declare(strict_types=1);
namespace SyliusBundleResourceBundleController;
use DoctrinePersistenceObjectManager;
use FOSRestBundleViewView;
use SyliusBundleResourceBundleEventResourceControllerEvent;
use SyliusComponentResourceExceptionDeleteHandlingException;
use SyliusComponentResourceExceptionUpdateHandlingException;
use SyliusComponentResourceFactoryFactoryInterface;
use SyliusComponentResourceMetadataMetadataInterface;
use SyliusComponentResourceModelResourceInterface;
use SyliusComponentResourceRepositoryRepositoryInterface;
use SyliusComponentResourceResourceActions;
use SymfonyComponentDependencyInjectionContainerAwareTrait;
use SymfonyComponentDependencyInjectionContainerInterface;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpKernelExceptionBadRequestHttpException;
use SymfonyComponentHttpKernelExceptionHttpException;
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
use SymfonyComponentSecurityCoreExceptionAccessDeniedException;
class ResourceController
{
use ControllerTrait;
use ContainerAwareTrait;
protected MetadataInterface $metadata;
protected RequestCon
fi
gurationFactoryInterface $requestCon
fi
gurationFactory;
protected ?ViewHandlerInterface $viewHandler;
protected RepositoryInterface $repository;
protected FactoryInterface $factory;
protected NewResourceFactoryInterface $newResourceFactory;
protected ObjectManager $manager;
protected SingleResourceProviderInterface $singleResourceProvider;
protected ResourcesCollectionProviderInterface $resourcesCollectionProvider;
protected ResourceFormFactoryInterface $resourceFormFactory;
protected RedirectHandlerInterface $redirectHandler;
protected FlashHelperInterface $
fl
ashHelper;
protected AuthorizationCheckerInterface $authorizationChecker;
protected EventDispatcherInterface $eventDispatcher;
protected ?StateMachineInterface $stateMachine;
protected ResourceUpdateHandlerInterface $resourceUpdateHandler;
protected ResourceDeleteHandlerInterface $resourceDeleteHandler;
public function __construct(
MetadataInterface $metadata,
RequestCon
fi
gurationFactoryInterface $requestCon
fi
gurationFactory,
?ViewHandlerInterface $viewHandler,
RepositoryInterface $repository,
FactoryInterface $factory,
NewResourceFactoryInterface $newResourceFactory,
ObjectManager $manager,
SingleResourceProviderInterface $singleResourceProvider,
ResourcesCollectionProviderInterface $resourcesFinder,
ResourceFormFactoryInterface $resourceFormFactory,
RedirectHandlerInterface $redirectHandler,
FlashHelperInterface $
fl
ashHelper,
AuthorizationCheckerInterface $authorizationChecker,
EventDispatcherInterface $eventDispatcher,
?StateMachineInterface $stateMachine,
ResourceUpdateHandlerInterface $resourceUpdateHandler,
ResourceDeleteHandlerInterface $resourceDeleteHandler,
) {
$this->metadata = $metadata;
$this->requestCon
fi
gurationFactory = $requestCon
fi
gurationFactory;
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->factory = $factory;
$this->newResourceFactory = $newResourceFactory;
$this->manager = $manager;
$this->singleResourceProvider = $singleResourceProvider;
$this->resourcesCollectionProvider = $resourcesFinder;
$this->resourceFormFactory = $resourceFormFactory;
$this->redirectHandler = $redirectHandler;
$this->
fl
ashHelper = $
fl
ashHelper;
$this->authorizationChecker = $authorizationChecker;
$this->eventDispatcher = $eventDispatcher;
$this->stateMachine = $stateMachine;
$this->resourceUpdateHandler = $resourceUpdateHandler;
$this->resourceDeleteHandler = $resourceDeleteHandler;
}
public function showAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::SHOW);
$resource = $this->
fi
ndOr404($con
fi
guration);
$event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $con
fi
guration, $resource);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if ($con
fi
guration->isHtmlRequest()) {
return $this->render($con
fi
guration->getTemplate(ResourceActions::SHOW . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
]);
}
return $this->createRestView($con
fi
guration, $resource);
}
public function indexAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::INDEX);
$resources = $this->resourcesCollectionProvider->get($con
fi
guration, $this->repository);
$event = $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $con
fi
guration, $resources);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if ($con
fi
guration->isHtmlRequest()) {
return $this->render($con
fi
guration->getTemplate(ResourceActions::INDEX . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resources' => $resources,
$this->metadata->getPluralName() => $resources,
]);
}
return $this->createRestView($con
fi
guration, $resources);
}
public function createAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::CREATE);
$newResource = $this->newResourceFactory->create($con
fi
guration, $this->factory);
$form = $this->resourceFormFactory->create($con
fi
guration, $newResource);
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
$newResource = $form->getData();
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $newResource);
}
if ($con
fi
guration->hasStateMachine()) {
$stateMachine = $this->getStateMachine();
$stateMachine->apply($con
fi
guration, $newResource);
}
$this->repository->add($newResource);
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::CREATE, $newResource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $newResource, Response::HTTP_CREATED);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $newResource);
}
if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($con
fi
guration->getTemplate(ResourceActions::CREATE . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $newResource,
$this->metadata->getName() => $newResource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function updateAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::UPDATE);
$resource = $this->
fi
ndOr404($con
fi
guration);
$form = $this->resourceFormFactory->create($con
fi
guration, $resource);
$form->handleRequest($request);
if (
in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) &&
$form->isSubmitted() &&
$form->isValid()
) {
$resource = $form->getData();
/** @var ResourceControllerEvent $event */
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
try {
$this->resourceUpdateHandler->handle($resource, $con
fi
guration, $this->manager);
} catch (UpdateHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::UPDATE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
if ($con
fi
guration->getParameters()->get('return_content', false)) {
return $this->createRestView($con
fi
guration, $resource, Response::HTTP_OK);
}
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($con
fi
guration->getTemplate(ResourceActions::UPDATE . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function deleteAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::DELETE);
$resource = $this->
fi
ndOr404($con
fi
guration);
if ($con
fi
guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), (string) $request->request->get('_csrf_token'))) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
}
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
try {
$this->resourceDeleteHandler->handle($resource, $this->repository);
} catch (DeleteHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::DELETE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
public function bulkDeleteAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::BULK_DELETE);
$resources = $this->resourcesCollectionProvider->get($con
fi
guration, $this->repository);
if (
$con
fi
guration->isCsrfProtectionEnabled() &&
!$this->isCsrfTokenValid(ResourceActions::BULK_DELETE, (string) $request->request->get('_csrf_token'))
) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
}
$this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE, $con
fi
guration, $resources);
foreach ($resources as $resource) {
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
try {
$this->resourceDeleteHandler->handle($resource, $this->repository);
} catch (DeleteHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::BULK_DELETE);
if (isset($postEvent)) {
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
}
return $this->redirectHandler->redirectToIndex($con
fi
guration);
}
public function applyStateMachineTransitionAction(Request $request): Response
{
$stateMachine = $this->getStateMachine();
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::UPDATE);
$resource = $this->
fi
ndOr404($con
fi
guration);
if ($con
fi
guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->get('_csrf_token'))) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token.');
}
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
if (!$stateMachine->can($con
fi
guration, $resource)) {
throw new BadRequestHttpException();
}
try {
$this->resourceUpdateHandler->handle($resource, $con
fi
guration, $this->manager);
} catch (UpdateHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $resource, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::UPDATE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
if ($con
fi
guration->getParameters()->get('return_content', true)) {
return $this->createRestView($con
fi
guration, $resource, Response::HTTP_OK);
}
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
/**
* @return mixed
*/
protected function getParameter(string $name)
{
if (!$this->container instanceof ContainerInterface) {
throw new RuntimeException(sprintf(
'Container passed to "%s" has to implements "%s".',
self::class,
ContainerInterface::class,
));
}
return $this->container->getParameter($name);
}
/**
* @throws AccessDeniedException
*/
protected function isGrantedOr403(RequestCon
fi
guration $con
fi
guration, string $permission): void
{
if (!$con
fi
guration->hasPermission()) {
return;
}
$permission = $con
fi
guration->getPermission($permission);
if (!$this->authorizationChecker->isGranted($con
fi
guration, $permission)) {
throw new AccessDeniedException();
}
}
/**
* @throws NotFoundHttpException
*/
protected function
fi
ndOr404(RequestCon
fi
guration $con
fi
guration): ResourceInterface
{
if (null === $resource = $this->singleResourceProvider->get($con
fi
guration, $this->repository)) {
throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this->metadata->getHumanizedName()));
}
return $resource;
}
/**
* @param mixed $data
*/
protected function createRestView(RequestCon
fi
guration $con
fi
guration, $data, int $statusCode = null): Response
{
if (null === $this->viewHandler) {
throw new LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".');
}
$view = View::create($data, $statusCode);
return $this->viewHandler->handle($con
fi
guration, $view);
}
protected function getStateMachine(): StateMachineInterface
{
if (null === $this->stateMachine) {
throw new LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".');
}
return $this->stateMachine;
}
}
ResourceController
<?php
/*
* This
fi
le is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
*
fi
le that was distributed with this source code.
*/
declare(strict_types=1);
namespace SyliusBundleResourceBundleController;
use DoctrinePersistenceObjectManager;
use FOSRestBundleViewView;
use SyliusBundleResourceBundleEventResourceControllerEvent;
use SyliusComponentResourceExceptionDeleteHandlingException;
use SyliusComponentResourceExceptionUpdateHandlingException;
use SyliusComponentResourceFactoryFactoryInterface;
use SyliusComponentResourceMetadataMetadataInterface;
use SyliusComponentResourceModelResourceInterface;
use SyliusComponentResourceRepositoryRepositoryInterface;
use SyliusComponentResourceResourceActions;
use SymfonyComponentDependencyInjectionContainerAwareTrait;
use SymfonyComponentDependencyInjectionContainerInterface;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpKernelExceptionBadRequestHttpException;
use SymfonyComponentHttpKernelExceptionHttpException;
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
use SymfonyComponentSecurityCoreExceptionAccessDeniedException;
class ResourceController
{
use ControllerTrait;
use ContainerAwareTrait;
protected MetadataInterface $metadata;
protected RequestCon
fi
gurationFactoryInterface $requestCon
fi
gurationFactory;
protected ?ViewHandlerInterface $viewHandler;
protected RepositoryInterface $repository;
protected FactoryInterface $factory;
protected NewResourceFactoryInterface $newResourceFactory;
protected ObjectManager $manager;
protected SingleResourceProviderInterface $singleResourceProvider;
protected ResourcesCollectionProviderInterface $resourcesCollectionProvider;
protected ResourceFormFactoryInterface $resourceFormFactory;
protected RedirectHandlerInterface $redirectHandler;
protected FlashHelperInterface $
fl
ashHelper;
protected AuthorizationCheckerInterface $authorizationChecker;
protected EventDispatcherInterface $eventDispatcher;
protected ?StateMachineInterface $stateMachine;
protected ResourceUpdateHandlerInterface $resourceUpdateHandler;
protected ResourceDeleteHandlerInterface $resourceDeleteHandler;
public function __construct(
MetadataInterface $metadata,
RequestCon
fi
gurationFactoryInterface $requestCon
fi
gurationFactory,
?ViewHandlerInterface $viewHandler,
RepositoryInterface $repository,
FactoryInterface $factory,
NewResourceFactoryInterface $newResourceFactory,
ObjectManager $manager,
SingleResourceProviderInterface $singleResourceProvider,
ResourcesCollectionProviderInterface $resourcesFinder,
ResourceFormFactoryInterface $resourceFormFactory,
RedirectHandlerInterface $redirectHandler,
FlashHelperInterface $
fl
ashHelper,
AuthorizationCheckerInterface $authorizationChecker,
EventDispatcherInterface $eventDispatcher,
?StateMachineInterface $stateMachine,
ResourceUpdateHandlerInterface $resourceUpdateHandler,
ResourceDeleteHandlerInterface $resourceDeleteHandler,
) {
$this->metadata = $metadata;
$this->requestCon
fi
gurationFactory = $requestCon
fi
gurationFactory;
$this->viewHandler = $viewHandler;
$this->repository = $repository;
$this->factory = $factory;
$this->newResourceFactory = $newResourceFactory;
$this->manager = $manager;
$this->singleResourceProvider = $singleResourceProvider;
$this->resourcesCollectionProvider = $resourcesFinder;
$this->resourceFormFactory = $resourceFormFactory;
$this->redirectHandler = $redirectHandler;
$this->
fl
ashHelper = $
fl
ashHelper;
$this->authorizationChecker = $authorizationChecker;
$this->eventDispatcher = $eventDispatcher;
$this->stateMachine = $stateMachine;
$this->resourceUpdateHandler = $resourceUpdateHandler;
$this->resourceDeleteHandler = $resourceDeleteHandler;
}
public function showAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::SHOW);
$resource = $this->
fi
ndOr404($con
fi
guration);
$event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $con
fi
guration, $resource);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if ($con
fi
guration->isHtmlRequest()) {
return $this->render($con
fi
guration->getTemplate(ResourceActions::SHOW . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
]);
}
return $this->createRestView($con
fi
guration, $resource);
}
public function indexAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::INDEX);
$resources = $this->resourcesCollectionProvider->get($con
fi
guration, $this->repository);
$event = $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $con
fi
guration, $resources);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if ($con
fi
guration->isHtmlRequest()) {
return $this->render($con
fi
guration->getTemplate(ResourceActions::INDEX . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resources' => $resources,
$this->metadata->getPluralName() => $resources,
]);
}
return $this->createRestView($con
fi
guration, $resources);
}
public function createAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::CREATE);
$newResource = $this->newResourceFactory->create($con
fi
guration, $this->factory);
$form = $this->resourceFormFactory->create($con
fi
guration, $newResource);
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
$newResource = $form->getData();
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $newResource);
}
if ($con
fi
guration->hasStateMachine()) {
$stateMachine = $this->getStateMachine();
$stateMachine->apply($con
fi
guration, $newResource);
}
$this->repository->add($newResource);
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::CREATE, $newResource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $newResource, Response::HTTP_CREATED);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $newResource);
}
if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $con
fi
guration, $newResource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($con
fi
guration->getTemplate(ResourceActions::CREATE . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $newResource,
$this->metadata->getName() => $newResource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function updateAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::UPDATE);
$resource = $this->
fi
ndOr404($con
fi
guration);
$form = $this->resourceFormFactory->create($con
fi
guration, $resource);
$form->handleRequest($request);
if (
in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) &&
$form->isSubmitted() &&
$form->isValid()
) {
$resource = $form->getData();
/** @var ResourceControllerEvent $event */
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
try {
$this->resourceUpdateHandler->handle($resource, $con
fi
guration, $this->manager);
} catch (UpdateHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::UPDATE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
if ($con
fi
guration->getParameters()->get('return_content', false)) {
return $this->createRestView($con
fi
guration, $resource, Response::HTTP_OK);
}
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $form, Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($con
fi
guration->getTemplate(ResourceActions::UPDATE . '.html'), [
'con
fi
guration' => $con
fi
guration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function deleteAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::DELETE);
$resource = $this->
fi
ndOr404($con
fi
guration);
if ($con
fi
guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), (string) $request->request->get('_csrf_token'))) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
}
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
try {
$this->resourceDeleteHandler->handle($resource, $this->repository);
} catch (DeleteHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::DELETE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
public function bulkDeleteAction(Request $request): Response
{
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::BULK_DELETE);
$resources = $this->resourcesCollectionProvider->get($con
fi
guration, $this->repository);
if (
$con
fi
guration->isCsrfProtectionEnabled() &&
!$this->isCsrfTokenValid(ResourceActions::BULK_DELETE, (string) $request->request->get('_csrf_token'))
) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.');
}
$this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE, $con
fi
guration, $resources);
foreach ($resources as $resource) {
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($con
fi
guration, $resource);
}
try {
$this->resourceDeleteHandler->handle($resource, $this->repository);
} catch (DeleteHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con
fi
guration, $resource);
}
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::BULK_DELETE);
if (isset($postEvent)) {
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
}
return $this->redirectHandler->redirectToIndex($con
fi
guration);
}
public function applyStateMachineTransitionAction(Request $request): Response
{
$stateMachine = $this->getStateMachine();
$con
fi
guration = $this->requestCon
fi
gurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($con
fi
guration, ResourceActions::UPDATE);
$resource = $this->
fi
ndOr404($con
fi
guration);
if ($con
fi
guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->get('_csrf_token'))) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token.');
}
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if ($event->isStopped() && !$con
fi
guration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->
fl
ashHelper->addFlashFromEvent($con
fi
guration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
if (!$stateMachine->can($con
fi
guration, $resource)) {
throw new BadRequestHttpException();
}
try {
$this->resourceUpdateHandler->handle($resource, $con
fi
guration, $this->manager);
} catch (UpdateHandlingException $exception) {
if (!$con
fi
guration->isHtmlRequest()) {
return $this->createRestView($con
fi
guration, $resource, $exception->getApiResponseCode());
}
$this->
fl
ashHelper->addErrorFlash($con
fi
guration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($con
fi
guration);
}
if ($con
fi
guration->isHtmlRequest()) {
$this->
fl
ashHelper->addSuccessFlash($con
fi
guration, ResourceActions::UPDATE, $resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con
fi
guration, $resource);
if (!$con
fi
guration->isHtmlRequest()) {
if ($con
fi
guration->getParameters()->get('return_content', true)) {
return $this->createRestView($con
fi
guration, $resource, Response::HTTP_OK);
}
return $this->createRestView($con
fi
guration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($con
fi
guration, $resource);
}
/**
* @return mixed
*/
protected function getParameter(string $name)
{
if (!$this->container instanceof ContainerInterface) {
throw new RuntimeException(sprintf(
'Container passed to "%s" has to implements "%s".',
self::class,
ContainerInterface::class,
));
}
return $this->container->getParameter($name);
}
/**
* @throws AccessDeniedException
*/
protected function isGrantedOr403(RequestCon
fi
guration $con
fi
guration, string $permission): void
{
if (!$con
fi
guration->hasPermission()) {
return;
}
$permission = $con
fi
guration->getPermission($permission);
if (!$this->authorizationChecker->isGranted($con
fi
guration, $permission)) {
throw new AccessDeniedException();
}
}
/**
* @throws NotFoundHttpException
*/
protected function
fi
ndOr404(RequestCon
fi
guration $con
fi
guration): ResourceInterface
{
if (null === $resource = $this->singleResourceProvider->get($con
fi
guration, $this->repository)) {
throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this->metadata->getHumanizedName()));
}
return $resource;
}
/**
* @param mixed $data
*/
protected function createRestView(RequestCon
fi
guration $con
fi
guration, $data, int $statusCode = null): Response
{
if (null === $this->viewHandler) {
throw new LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".');
}
$view = View::create($data, $statusCode);
return $this->viewHandler->handle($con
fi
guration, $view);
}
protected function getStateMachine(): StateMachineInterface
{
if (null === $this->stateMachine) {
throw new LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".');
}
return $this->stateMachine;
}
}
ResourceController
!
interface ProviderInterface
{
public function provide(Operation $operation, Context $context): object|iterable|null;
}
interface ProcessorInterface
{
public function process(mixed $data, Operation $operation, Context $context): mixed;
}
#[AsResource]
#[Index(grid: BookGrid::class)]
#[Create]
class Book implements ResourceInterface
{
}
$grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%')
->setLimits([10, 25, 50, 100])
->addField(
Field::create('name', 'twig')
->setLabel('Name')
->setSortable(true)
)
->addFilter(
Filter::create('name', 'string')
->setLabel('app.ui.name')
->setEnabled(true)
->setFormOptions(['type' => 'contains'])
)
->addActionGroup(MainActionGroup::create(
Action::create('create', 'create')
))
;
Is Sylius DX still great?…
Is Sylius DX still great?…
Of course
Q&A
We are hiring!
Łukasz Chruściel
Mateusz Zalewski
@lchrusciel
@mpzalewski
Commerce Weavers @commerceweavers
Thank you!

More Related Content

Similar to SyliusCon - Typical pitfalls of Sylius development.pdf

Similar to SyliusCon - Typical pitfalls of Sylius development.pdf (20)

The Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup BelgiumThe Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup Belgium
 
The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014
 
Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...
 
Androidreadme
AndroidreadmeAndroidreadme
Androidreadme
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - Tryout
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Creating a modern web application using Symfony API Platform Atlanta
Creating a modern web application using  Symfony API Platform AtlantaCreating a modern web application using  Symfony API Platform Atlanta
Creating a modern web application using Symfony API Platform Atlanta
 
Complex Sites with Silex
Complex Sites with SilexComplex Sites with Silex
Complex Sites with Silex
 
Spring training
Spring trainingSpring training
Spring training
 
The Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaThe Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony Barcelona
 
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years laterSymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
 
Drupal 8, Where Did the Code Go? From Info Hook to Plugin
Drupal 8, Where Did the Code Go? From Info Hook to PluginDrupal 8, Where Did the Code Go? From Info Hook to Plugin
Drupal 8, Where Did the Code Go? From Info Hook to Plugin
 
api-platform: the ultimate API platform
api-platform: the ultimate API platformapi-platform: the ultimate API platform
api-platform: the ultimate API platform
 
Entities in drupal 7
Entities in drupal 7Entities in drupal 7
Entities in drupal 7
 
Using API Platform to build ticketing system #symfonycon
Using API Platform to build ticketing system #symfonyconUsing API Platform to build ticketing system #symfonycon
Using API Platform to build ticketing system #symfonycon
 
Grails Advanced
Grails Advanced Grails Advanced
Grails Advanced
 
Deploy Serverless Apps with Python: AWS Chalice Deep Dive (DEV427-R2) - AWS r...
Deploy Serverless Apps with Python: AWS Chalice Deep Dive (DEV427-R2) - AWS r...Deploy Serverless Apps with Python: AWS Chalice Deep Dive (DEV427-R2) - AWS r...
Deploy Serverless Apps with Python: AWS Chalice Deep Dive (DEV427-R2) - AWS r...
 
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocialIBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
 
JMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocialJMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocial
 
Rails Plugins - Linux For You, March 2011 Issue
Rails Plugins - Linux For You, March 2011 IssueRails Plugins - Linux For You, March 2011 Issue
Rails Plugins - Linux For You, March 2011 Issue
 

More from Łukasz Chruściel

More from Łukasz Chruściel (20)

Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024  - Need for Speed: Removing speed bumps in API ProjectsConFoo 2024  - Need for Speed: Removing speed bumps in API Projects
ConFoo 2024 - Need for Speed: Removing speed bumps in API Projects
 
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solutionConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
ConFoo 2024 - Sylius 2.0, top-notch eCommerce for customizable solution
 
SymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdfSymfonyLive Online 2023 - Is SOLID dead? .pdf
SymfonyLive Online 2023 - Is SOLID dead? .pdf
 
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
Worldwide Software Architecture Summit'23 - BDD and why most of us do it wron...
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf
 
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API SyliusaBoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
BoilingFrogs - Rozterki i decyzje. Czego się nauczyliśmy projektując API Syliusa
 
What we've learned designing new Sylius API
What we've learned designing new Sylius APIWhat we've learned designing new Sylius API
What we've learned designing new Sylius API
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdf
 
SymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfSymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdf
 
How to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets BlackfireHow to optimize background processes - when Sylius meets Blackfire
How to optimize background processes - when Sylius meets Blackfire
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Sylius and Api Platform The story of integration
Sylius and Api Platform The story of integrationSylius and Api Platform The story of integration
Sylius and Api Platform The story of integration
 
Dutch php a short tale about state machine
Dutch php   a short tale about state machineDutch php   a short tale about state machine
Dutch php a short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machine
 
BDD in practice based on an open source project
BDD in practice based on an open source projectBDD in practice based on an open source project
BDD in practice based on an open source project
 
Diversified application testing based on a Sylius project
Diversified application testing based on a Sylius projectDiversified application testing based on a Sylius project
Diversified application testing based on a Sylius project
 
Why do I love and hate php?
Why do I love and hate php?Why do I love and hate php?
Why do I love and hate php?
 

Recently uploaded

Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 

Recently uploaded (20)

AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
GraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysisGraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysis
 
Into the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdfInto the Box 2024 - Keynote Day 2 Slides.pdf
Into the Box 2024 - Keynote Day 2 Slides.pdf
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring Software
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting software
 

SyliusCon - Typical pitfalls of Sylius development.pdf

  • 3. Why do we do this?
  • 4.
  • 5.
  • 6.
  • 7.
  • 9. AI is not a threat for now
  • 10.
  • 11. Q&A
  • 13. N+1
  • 18.
  • 19. O(1)
  • 21. How many queries are executed here? { "@context": "/api/contexts/Order", "@id": "/api/orders", "@type": "hydra:Collection", "hydra:totalItems": 3, "hydra:member": [ { "@id": "/api/orders/1", "@type": "Order", "id": 1, "orderItems": [ "/api/order_items/1", "/api/order_items/2" ] }, { “…”: “…“ } ] } Amount of orders: 3 Every order associated with 2 items Sample query on the left No total fi eld
  • 22.
  • 23.
  • 24. O(M)
  • 25. Order list with additional fi eld
  • 26. How many queries are executed here? { "@context": "/api/contexts/Order", "@id": "/api/orders", "@type": "hydra:Collection", "hydra:totalItems": 3, "hydra:member": [ { "@id": "/api/orders/1", "@type": "Order", "id": 1, "orderItems": [ "/api/order_items/1", "/api/order_items/2" ], "total": 7000 }, { “…”: “…“ } ] } Amount of orders: 3 Every order associated with 2 items Sample query on the left Added “total()” as a function of product price and quantity of item
  • 27.
  • 29.
  • 32. #[ORMOneToMany(fetch: ‘LAZY')] => 11 #[ORMOneToMany(fetch: ‘EXTRA_LAZY')] => 11 #[ORMOneToMany(fetch: ‘EAGER')] => 5 (OrderItem and Product) #[ORMOneToMany(fetch: ‘EAGER')] (OrderItem and Product) + Serialisation groups => 4
  • 34. final class LoadItemsAndProductsExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface { private function apply(QueryBuilder $queryBuilder): void { $queryBuilder ->addSelect('oi', 'p') ->join(OrderItem::class, 'oi', Join::WITH, 'oi.originOrder = o') ->join(Product::class, 'p', Join::WITH, 'oi.product = p') ; } }
  • 35.
  • 37. { "@context": "/api/contexts/Order", "@id": "/api/orders", "@type": "hydra:Collection", "hydra:totalItems": 3, "hydra:member": [ { "@id": "/api/orders/1", "@type": "Order", "id": 1, "orderItems": [ "/api/order_items/1", "/api/order_items/2" ], "total": 7000 }, { "@id": "/api/order_items/1", "@type": "OrderItem", "id": 1, "product": "/api/products/1", "quantity": 2, "originOrder": "/api/orders/1", "price": 2000 }, { "@id": "/api/products/1", "@type": "Product", "id": 1, "name": "T-Shirt", "price": 1000 }, { "@id": "/api/order_items/2", "@type": "OrderItem", "id": 2, "product": "/api/products/2", "quantity": 1, "originOrder": "/api/orders/1", "price": 5000 }, { “…”: “…” } ] }
  • 38. So is single query an ultimate solution?
  • 41. Does it need to be sequential?
  • 42. Or defer it 😉
  • 43. Locking mechanism of product variants (inventory)
  • 45. Concurrency from the same user
  • 47. How does Doctrine collection handling works?
  • 48. Tests
  • 49.
  • 50.
  • 51.
  • 53. You don’t need to do BDD to work with Sylius Although we ecourage you to do so
  • 54. No. of Behat scenarios in our latest successfull Sylius project:
  • 55. 0 No. of Behat scenarios in our latest successfull Sylius project:
  • 63. What we want to achieve
  • 65. Core Team is actively working on it
  • 68.
  • 69. <?php /* * This fi le is part of the Sylius package. * * (c) Sylius Sp. z o.o. * * For the full copyright and license information, please view the LICENSE * fi le that was distributed with this source code. */ declare(strict_types=1); namespace SyliusBundleResourceBundleController; use DoctrinePersistenceObjectManager; use FOSRestBundleViewView; use SyliusBundleResourceBundleEventResourceControllerEvent; use SyliusComponentResourceExceptionDeleteHandlingException; use SyliusComponentResourceExceptionUpdateHandlingException; use SyliusComponentResourceFactoryFactoryInterface; use SyliusComponentResourceMetadataMetadataInterface; use SyliusComponentResourceModelResourceInterface; use SyliusComponentResourceRepositoryRepositoryInterface; use SyliusComponentResourceResourceActions; use SymfonyComponentDependencyInjectionContainerAwareTrait; use SymfonyComponentDependencyInjectionContainerInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentHttpKernelExceptionBadRequestHttpException; use SymfonyComponentHttpKernelExceptionHttpException; use SymfonyComponentHttpKernelExceptionNotFoundHttpException; use SymfonyComponentSecurityCoreExceptionAccessDeniedException; class ResourceController { use ControllerTrait; use ContainerAwareTrait; protected MetadataInterface $metadata; protected RequestCon fi gurationFactoryInterface $requestCon fi gurationFactory; protected ?ViewHandlerInterface $viewHandler; protected RepositoryInterface $repository; protected FactoryInterface $factory; protected NewResourceFactoryInterface $newResourceFactory; protected ObjectManager $manager; protected SingleResourceProviderInterface $singleResourceProvider; protected ResourcesCollectionProviderInterface $resourcesCollectionProvider; protected ResourceFormFactoryInterface $resourceFormFactory; protected RedirectHandlerInterface $redirectHandler; protected FlashHelperInterface $ fl ashHelper; protected AuthorizationCheckerInterface $authorizationChecker; protected EventDispatcherInterface $eventDispatcher; protected ?StateMachineInterface $stateMachine; protected ResourceUpdateHandlerInterface $resourceUpdateHandler; protected ResourceDeleteHandlerInterface $resourceDeleteHandler; public function __construct( MetadataInterface $metadata, RequestCon fi gurationFactoryInterface $requestCon fi gurationFactory, ?ViewHandlerInterface $viewHandler, RepositoryInterface $repository, FactoryInterface $factory, NewResourceFactoryInterface $newResourceFactory, ObjectManager $manager, SingleResourceProviderInterface $singleResourceProvider, ResourcesCollectionProviderInterface $resourcesFinder, ResourceFormFactoryInterface $resourceFormFactory, RedirectHandlerInterface $redirectHandler, FlashHelperInterface $ fl ashHelper, AuthorizationCheckerInterface $authorizationChecker, EventDispatcherInterface $eventDispatcher, ?StateMachineInterface $stateMachine, ResourceUpdateHandlerInterface $resourceUpdateHandler, ResourceDeleteHandlerInterface $resourceDeleteHandler, ) { $this->metadata = $metadata; $this->requestCon fi gurationFactory = $requestCon fi gurationFactory; $this->viewHandler = $viewHandler; $this->repository = $repository; $this->factory = $factory; $this->newResourceFactory = $newResourceFactory; $this->manager = $manager; $this->singleResourceProvider = $singleResourceProvider; $this->resourcesCollectionProvider = $resourcesFinder; $this->resourceFormFactory = $resourceFormFactory; $this->redirectHandler = $redirectHandler; $this-> fl ashHelper = $ fl ashHelper; $this->authorizationChecker = $authorizationChecker; $this->eventDispatcher = $eventDispatcher; $this->stateMachine = $stateMachine; $this->resourceUpdateHandler = $resourceUpdateHandler; $this->resourceDeleteHandler = $resourceDeleteHandler; } public function showAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::SHOW); $resource = $this-> fi ndOr404($con fi guration); $event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $con fi guration, $resource); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($con fi guration->isHtmlRequest()) { return $this->render($con fi guration->getTemplate(ResourceActions::SHOW . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, ]); } return $this->createRestView($con fi guration, $resource); } public function indexAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::INDEX); $resources = $this->resourcesCollectionProvider->get($con fi guration, $this->repository); $event = $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $con fi guration, $resources); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($con fi guration->isHtmlRequest()) { return $this->render($con fi guration->getTemplate(ResourceActions::INDEX . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resources' => $resources, $this->metadata->getPluralName() => $resources, ]); } return $this->createRestView($con fi guration, $resources); } public function createAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::CREATE); $newResource = $this->newResourceFactory->create($con fi guration, $this->factory); $form = $this->resourceFormFactory->create($con fi guration, $newResource); $form->handleRequest($request); if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) { $newResource = $form->getData(); $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $con fi guration, $newResource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $newResource); } if ($con fi guration->hasStateMachine()) { $stateMachine = $this->getStateMachine(); $stateMachine->apply($con fi guration, $newResource); } $this->repository->add($newResource); if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::CREATE, $newResource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $con fi guration, $newResource); if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $newResource, Response::HTTP_CREATED); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $newResource); } if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $con fi guration, $newResource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($con fi guration->getTemplate(ResourceActions::CREATE . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $newResource, $this->metadata->getName() => $newResource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function updateAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::UPDATE); $resource = $this-> fi ndOr404($con fi guration); $form = $this->resourceFormFactory->create($con fi guration, $resource); $form->handleRequest($request); if ( in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && $form->isValid() ) { $resource = $form->getData(); /** @var ResourceControllerEvent $event */ $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } try { $this->resourceUpdateHandler->handle($resource, $con fi guration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { if ($con fi guration->getParameters()->get('return_content', false)) { return $this->createRestView($con fi guration, $resource, Response::HTTP_OK); } return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $con fi guration, $resource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($con fi guration->getTemplate(ResourceActions::UPDATE . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function deleteAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::DELETE); $resource = $this-> fi ndOr404($con fi guration); if ($con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), (string) $request->request->get('_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::DELETE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } public function bulkDeleteAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::BULK_DELETE); $resources = $this->resourcesCollectionProvider->get($con fi guration, $this->repository); if ( $con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid(ResourceActions::BULK_DELETE, (string) $request->request->get('_csrf_token')) ) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE, $con fi guration, $resources); foreach ($resources as $resource) { $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con fi guration, $resource); } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::BULK_DELETE); if (isset($postEvent)) { $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } } return $this->redirectHandler->redirectToIndex($con fi guration); } public function applyStateMachineTransitionAction(Request $request): Response { $stateMachine = $this->getStateMachine(); $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::UPDATE); $resource = $this-> fi ndOr404($con fi guration); if ($con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->get('_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } if (!$stateMachine->can($con fi guration, $resource)) { throw new BadRequestHttpException(); } try { $this->resourceUpdateHandler->handle($resource, $con fi guration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $resource, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { if ($con fi guration->getParameters()->get('return_content', true)) { return $this->createRestView($con fi guration, $resource, Response::HTTP_OK); } return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } /** * @return mixed */ protected function getParameter(string $name) { if (!$this->container instanceof ContainerInterface) { throw new RuntimeException(sprintf( 'Container passed to "%s" has to implements "%s".', self::class, ContainerInterface::class, )); } return $this->container->getParameter($name); } /** * @throws AccessDeniedException */ protected function isGrantedOr403(RequestCon fi guration $con fi guration, string $permission): void { if (!$con fi guration->hasPermission()) { return; } $permission = $con fi guration->getPermission($permission); if (!$this->authorizationChecker->isGranted($con fi guration, $permission)) { throw new AccessDeniedException(); } } /** * @throws NotFoundHttpException */ protected function fi ndOr404(RequestCon fi guration $con fi guration): ResourceInterface { if (null === $resource = $this->singleResourceProvider->get($con fi guration, $this->repository)) { throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this->metadata->getHumanizedName())); } return $resource; } /** * @param mixed $data */ protected function createRestView(RequestCon fi guration $con fi guration, $data, int $statusCode = null): Response { if (null === $this->viewHandler) { throw new LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".'); } $view = View::create($data, $statusCode); return $this->viewHandler->handle($con fi guration, $view); } protected function getStateMachine(): StateMachineInterface { if (null === $this->stateMachine) { throw new LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".'); } return $this->stateMachine; } } ResourceController
  • 70. <?php /* * This fi le is part of the Sylius package. * * (c) Sylius Sp. z o.o. * * For the full copyright and license information, please view the LICENSE * fi le that was distributed with this source code. */ declare(strict_types=1); namespace SyliusBundleResourceBundleController; use DoctrinePersistenceObjectManager; use FOSRestBundleViewView; use SyliusBundleResourceBundleEventResourceControllerEvent; use SyliusComponentResourceExceptionDeleteHandlingException; use SyliusComponentResourceExceptionUpdateHandlingException; use SyliusComponentResourceFactoryFactoryInterface; use SyliusComponentResourceMetadataMetadataInterface; use SyliusComponentResourceModelResourceInterface; use SyliusComponentResourceRepositoryRepositoryInterface; use SyliusComponentResourceResourceActions; use SymfonyComponentDependencyInjectionContainerAwareTrait; use SymfonyComponentDependencyInjectionContainerInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentHttpKernelExceptionBadRequestHttpException; use SymfonyComponentHttpKernelExceptionHttpException; use SymfonyComponentHttpKernelExceptionNotFoundHttpException; use SymfonyComponentSecurityCoreExceptionAccessDeniedException; class ResourceController { use ControllerTrait; use ContainerAwareTrait; protected MetadataInterface $metadata; protected RequestCon fi gurationFactoryInterface $requestCon fi gurationFactory; protected ?ViewHandlerInterface $viewHandler; protected RepositoryInterface $repository; protected FactoryInterface $factory; protected NewResourceFactoryInterface $newResourceFactory; protected ObjectManager $manager; protected SingleResourceProviderInterface $singleResourceProvider; protected ResourcesCollectionProviderInterface $resourcesCollectionProvider; protected ResourceFormFactoryInterface $resourceFormFactory; protected RedirectHandlerInterface $redirectHandler; protected FlashHelperInterface $ fl ashHelper; protected AuthorizationCheckerInterface $authorizationChecker; protected EventDispatcherInterface $eventDispatcher; protected ?StateMachineInterface $stateMachine; protected ResourceUpdateHandlerInterface $resourceUpdateHandler; protected ResourceDeleteHandlerInterface $resourceDeleteHandler; public function __construct( MetadataInterface $metadata, RequestCon fi gurationFactoryInterface $requestCon fi gurationFactory, ?ViewHandlerInterface $viewHandler, RepositoryInterface $repository, FactoryInterface $factory, NewResourceFactoryInterface $newResourceFactory, ObjectManager $manager, SingleResourceProviderInterface $singleResourceProvider, ResourcesCollectionProviderInterface $resourcesFinder, ResourceFormFactoryInterface $resourceFormFactory, RedirectHandlerInterface $redirectHandler, FlashHelperInterface $ fl ashHelper, AuthorizationCheckerInterface $authorizationChecker, EventDispatcherInterface $eventDispatcher, ?StateMachineInterface $stateMachine, ResourceUpdateHandlerInterface $resourceUpdateHandler, ResourceDeleteHandlerInterface $resourceDeleteHandler, ) { $this->metadata = $metadata; $this->requestCon fi gurationFactory = $requestCon fi gurationFactory; $this->viewHandler = $viewHandler; $this->repository = $repository; $this->factory = $factory; $this->newResourceFactory = $newResourceFactory; $this->manager = $manager; $this->singleResourceProvider = $singleResourceProvider; $this->resourcesCollectionProvider = $resourcesFinder; $this->resourceFormFactory = $resourceFormFactory; $this->redirectHandler = $redirectHandler; $this-> fl ashHelper = $ fl ashHelper; $this->authorizationChecker = $authorizationChecker; $this->eventDispatcher = $eventDispatcher; $this->stateMachine = $stateMachine; $this->resourceUpdateHandler = $resourceUpdateHandler; $this->resourceDeleteHandler = $resourceDeleteHandler; } public function showAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::SHOW); $resource = $this-> fi ndOr404($con fi guration); $event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $con fi guration, $resource); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($con fi guration->isHtmlRequest()) { return $this->render($con fi guration->getTemplate(ResourceActions::SHOW . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, ]); } return $this->createRestView($con fi guration, $resource); } public function indexAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::INDEX); $resources = $this->resourcesCollectionProvider->get($con fi guration, $this->repository); $event = $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX, $con fi guration, $resources); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($con fi guration->isHtmlRequest()) { return $this->render($con fi guration->getTemplate(ResourceActions::INDEX . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resources' => $resources, $this->metadata->getPluralName() => $resources, ]); } return $this->createRestView($con fi guration, $resources); } public function createAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::CREATE); $newResource = $this->newResourceFactory->create($con fi guration, $this->factory); $form = $this->resourceFormFactory->create($con fi guration, $newResource); $form->handleRequest($request); if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) { $newResource = $form->getData(); $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $con fi guration, $newResource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $newResource); } if ($con fi guration->hasStateMachine()) { $stateMachine = $this->getStateMachine(); $stateMachine->apply($con fi guration, $newResource); } $this->repository->add($newResource); if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::CREATE, $newResource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $con fi guration, $newResource); if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $newResource, Response::HTTP_CREATED); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $newResource); } if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE, $con fi guration, $newResource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($con fi guration->getTemplate(ResourceActions::CREATE . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $newResource, $this->metadata->getName() => $newResource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function updateAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::UPDATE); $resource = $this-> fi ndOr404($con fi guration); $form = $this->resourceFormFactory->create($con fi guration, $resource); $form->handleRequest($request); if ( in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && $form->isValid() ) { $resource = $form->getData(); /** @var ResourceControllerEvent $event */ $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } try { $this->resourceUpdateHandler->handle($resource, $con fi guration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { if ($con fi guration->getParameters()->get('return_content', false)) { return $this->createRestView($con fi guration, $resource, Response::HTTP_OK); } return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE, $con fi guration, $resource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($con fi guration->getTemplate(ResourceActions::UPDATE . '.html'), [ 'con fi guration' => $con fi guration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function deleteAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::DELETE); $resource = $this-> fi ndOr404($con fi guration); if ($con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), (string) $request->request->get('_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::DELETE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } public function bulkDeleteAction(Request $request): Response { $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::BULK_DELETE); $resources = $this->resourcesCollectionProvider->get($con fi guration, $this->repository); if ( $con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid(ResourceActions::BULK_DELETE, (string) $request->request->get('_csrf_token')) ) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid csrf token.'); } $this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE, $con fi guration, $resources); foreach ($resources as $resource) { $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($con fi guration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $con fi guration, $resource); } if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::BULK_DELETE); if (isset($postEvent)) { $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } } return $this->redirectHandler->redirectToIndex($con fi guration); } public function applyStateMachineTransitionAction(Request $request): Response { $stateMachine = $this->getStateMachine(); $con fi guration = $this->requestCon fi gurationFactory->create($this->metadata, $request); $this->isGrantedOr403($con fi guration, ResourceActions::UPDATE); $resource = $this-> fi ndOr404($con fi guration); if ($con fi guration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->get('_csrf_token'))) { throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token.'); } $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE, $con fi guration, $resource); if ($event->isStopped() && !$con fi guration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this-> fl ashHelper->addFlashFromEvent($con fi guration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } if (!$stateMachine->can($con fi guration, $resource)) { throw new BadRequestHttpException(); } try { $this->resourceUpdateHandler->handle($resource, $con fi guration, $this->manager); } catch (UpdateHandlingException $exception) { if (!$con fi guration->isHtmlRequest()) { return $this->createRestView($con fi guration, $resource, $exception->getApiResponseCode()); } $this-> fl ashHelper->addErrorFlash($con fi guration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($con fi guration); } if ($con fi guration->isHtmlRequest()) { $this-> fl ashHelper->addSuccessFlash($con fi guration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $con fi guration, $resource); if (!$con fi guration->isHtmlRequest()) { if ($con fi guration->getParameters()->get('return_content', true)) { return $this->createRestView($con fi guration, $resource, Response::HTTP_OK); } return $this->createRestView($con fi guration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($con fi guration, $resource); } /** * @return mixed */ protected function getParameter(string $name) { if (!$this->container instanceof ContainerInterface) { throw new RuntimeException(sprintf( 'Container passed to "%s" has to implements "%s".', self::class, ContainerInterface::class, )); } return $this->container->getParameter($name); } /** * @throws AccessDeniedException */ protected function isGrantedOr403(RequestCon fi guration $con fi guration, string $permission): void { if (!$con fi guration->hasPermission()) { return; } $permission = $con fi guration->getPermission($permission); if (!$this->authorizationChecker->isGranted($con fi guration, $permission)) { throw new AccessDeniedException(); } } /** * @throws NotFoundHttpException */ protected function fi ndOr404(RequestCon fi guration $con fi guration): ResourceInterface { if (null === $resource = $this->singleResourceProvider->get($con fi guration, $this->repository)) { throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this->metadata->getHumanizedName())); } return $resource; } /** * @param mixed $data */ protected function createRestView(RequestCon fi guration $con fi guration, $data, int $statusCode = null): Response { if (null === $this->viewHandler) { throw new LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".'); } $view = View::create($data, $statusCode); return $this->viewHandler->handle($con fi guration, $view); } protected function getStateMachine(): StateMachineInterface { if (null === $this->stateMachine) { throw new LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".'); } return $this->stateMachine; } } ResourceController
  • 71. !
  • 72. interface ProviderInterface { public function provide(Operation $operation, Context $context): object|iterable|null; } interface ProcessorInterface { public function process(mixed $data, Operation $operation, Context $context): mixed; }
  • 74. $grid->addGrid(GridBuilder::create('app_user', '%app.model.user.class%') ->setLimits([10, 25, 50, 100]) ->addField( Field::create('name', 'twig') ->setLabel('Name') ->setSortable(true) ) ->addFilter( Filter::create('name', 'string') ->setLabel('app.ui.name') ->setEnabled(true) ->setFormOptions(['type' => 'contains']) ) ->addActionGroup(MainActionGroup::create( Action::create('create', 'create') )) ;
  • 75. Is Sylius DX still great?…
  • 76. Is Sylius DX still great?… Of course
  • 77. Q&A