SlideShare uma empresa Scribd logo
1 de 67
Baixar para ler offline
Sylius 2.0 New Features
Unveiling the Future
Łukasz Chruściel
Introduction
Timeline
v1.13 - April 2024
Symfony Work
fl
ow support
PriceHistory
Attributes & auto con
fi
guration
API not experimental anymore
v1.13 - April 2024
Symfony Work
fl
ow support
Symfony Work
fl
ow
Symfony Work
fl
ow
sylius_state_machine_abstraction:
default_adapter: symfony_workflow
Attributes
#[AsCommandDataTransformer]
#[AsDocumentationModi
fi
er]
#[AsPaymentCon
fi
gurationProvider]
Attributes in API
#[AsOrderProcessor(priority: 10)]
#[AsCartContext]
Attributes in OrderBundle
#[AsCatalogPromotionApplicatorCriteria]
#[AsCatalogPromotionPriceCalculator]
#[AsEntityObserver]
#[AsOrderItemUnitsTaxesApplicator]
#[AsOrderItemUnitsTaxesApplicator]
#[AsProductVariantMapProvider]
#[AsTaxCalculationStrategy]
#[AsUriBasedSectionResolver]
#[AsProductVariantMapProvider]
#[AsProductVariantMapProvider]
Attributes in CoreBundle
#[AsLocaleContext]
Attributes in LocaleBundle
#[AsCurrencyContext]
Attributes in CurrencyBundle
#[AsProductVariantResolver]
Attributes in ProductBundle
#[AsTaxCalculator]
Attributes in TaxationBundle
#[AsShippingCalculator]
#[AsShippingMethodResolver]
#[AsShippingMethodRuleChecker]
#[AsShippingCalculator]
Attributes in ShippingBundle
#[AsPromotionAction]
#[AsPromotionCouponEligibilityChecker]
#[AsPromotionEligibilityChecker]
#[AsPromotionRuleChecker]
Attributes in PromotionBundle
#[AsAttributeType]
Attributes in AttributeBundle
#[AsChannelContext]
#[AsRequestBasedChannelResolver]
Attributes in ChannelBundle
v1.14 - Autumn 2024
v1.14 - Autumn 2024
2.0 Compatibility layer
Resource
Attributes
Again
#[AsResource]
Operations
#[Index]
#[Show]
#[Create]
...
#[SyliusCrudRoutes]
New Controller
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace SyliusBundleResourceBundleController;
use DoctrinePersistenceObjectManager;
use FOSRestBundleViewView;
use SyliusBundleResourceBundleEventResourceControllerEvent;
use SyliusComponentResourceExceptionDeleteHandlingException;
use SyliusComponentResourceExceptionUpdateHandlingException;
use SyliusComponentResourceResourceActions;
use SyliusResourceDoctrinePersistenceRepositoryInterface;
use SyliusResourceFactoryFactoryInterface;
use SyliusResourceMetadataMetadataInterface;
use SyliusResourceModelResourceInterface;
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 RequestConfigurationFactoryInterface $requestConfigurationFactory;
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 $flashHelper;
protected AuthorizationCheckerInterface $authorizationChecker;
protected EventDispatcherInterface $eventDispatcher;
protected ?StateMachineInterface $stateMachine;
protected ResourceUpdateHandlerInterface $resourceUpdateHandler;
protected ResourceDeleteHandlerInterface $resourceDeleteHandler;
public function __construct(
MetadataInterface $metadata,
RequestConfigurationFactoryInterface $requestConfigurationFactory,
?ViewHandlerInterface $viewHandler,
RepositoryInterface $repository,
FactoryInterface $factory,
NewResourceFactoryInterface $newResourceFactory,
ObjectManager $manager,
SingleResourceProviderInterface $singleResourceProvider,
ResourcesCollectionProviderInterface $resourcesFinder,
ResourceFormFactoryInterface $resourceFormFactory,
RedirectHandlerInterface $redirectHandler,
FlashHelperInterface $flashHelper,
AuthorizationCheckerInterface $authorizationChecker,
EventDispatcherInterface $eventDispatcher,
?StateMachineInterface $stateMachine,
ResourceUpdateHandlerInterface $resourceUpdateHandler,
ResourceDeleteHandlerInterface $resourceDeleteHandler,
) {
$this->metadata = $metadata;
$this->requestConfigurationFactory = $requestConfigurationFactory;
$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->flashHelper = $flashHelper;
$this->authorizationChecker = $authorizationChecker;
$this->eventDispatcher = $eventDispatcher;
$this->stateMachine = $stateMachine;
$this->resourceUpdateHandler = $resourceUpdateHandler;
$this->resourceDeleteHandler = $resourceDeleteHandler;
}
public function showAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::SHOW);
$resource = $this->findOr404($configuration);
$event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $configuration,
$resource);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if ($configuration->isHtmlRequest()) {
return $this->render($configuration->getTemplate(ResourceActions::SHOW .
'.html'), [
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
]);
}
return $this->createRestView($configuration, $resource);
}
public function indexAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
}
return $this->createRestView($configuration, $resources);
}
public function createAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::CREATE);
$newResource = $this->newResourceFactory->create($configuration, $this->factory);
$form = $this->resourceFormFactory->create($configuration, $newResource);
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
$newResource = $form->getData();
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE,
$configuration, $newResource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($configuration,
$newResource);
}
if ($configuration->hasStateMachine()) {
$stateMachine = $this->getStateMachine();
$stateMachine->apply($configuration, $newResource);
}
$this->repository->add($newResource);
if ($configuration->isHtmlRequest()) {
$this->flashHelper->addSuccessFlash($configuration,
ResourceActions::CREATE, $newResource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE,
$configuration, $newResource);
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, $newResource,
Response::HTTP_CREATED);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($configuration,
$newResource);
}
if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, $form,
Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher-
>dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($configuration->getTemplate(ResourceActions::CREATE .
'.html'), [
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $newResource,
$this->metadata->getName() => $newResource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function updateAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::UPDATE);
$resource = $this->findOr404($configuration);
$form = $this->resourceFormFactory->create($configuration, $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,
$configuration, $resource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($configuration,
$resource);
}
try {
$this->resourceUpdateHandler->handle($resource, $configuration, $this-
>manager);
} catch (UpdateHandlingException $exception) {
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, $form, $exception-
>getApiResponseCode());
return $this->createRestView($configuration, null,
Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($configuration, $resource);
}
if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form-
>isSubmitted() && !$form->isValid()) {
$responseCode = Response::HTTP_UNPROCESSABLE_ENTITY;
}
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, $form,
Response::HTTP_BAD_REQUEST);
}
$initializeEvent = $this->eventDispatcher-
>dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource);
$initializeEventResponse = $initializeEvent->getResponse();
if (null !== $initializeEventResponse) {
return $initializeEventResponse;
}
return $this->render($configuration->getTemplate(ResourceActions::UPDATE .
'.html'), [
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $resource,
$this->metadata->getName() => $resource,
'form' => $form->createView(),
], null, $responseCode ?? Response::HTTP_OK);
}
public function deleteAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::DELETE);
$resource = $this->findOr404($configuration);
if ($configuration->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,
$configuration, $resource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToIndex($configuration, $resource);
}
try {
$this->resourceDeleteHandler->handle($resource, $this->repository);
} catch (DeleteHandlingException $exception) {
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, null, $exception-
>getApiResponseCode());
}
$this->flashHelper->addErrorFlash($configuration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($configuration);
}
if ($configuration->isHtmlRequest()) {
$this->flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE,
$resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE,
$configuration, $resource);
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToIndex($configuration, $resource);
}
public function bulkDeleteAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::BULK_DELETE);
$resources = $this->resourcesCollectionProvider->get($configuration, $this-
>repository);
if (
$configuration->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,
$configuration, $resources);
foreach ($resources as $resource) {
$event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE,
$configuration, $resource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT);
}
$this->flashHelper->addSuccessFlash($configuration, ResourceActions::BULK_DELETE);
if (isset($postEvent)) {
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
}
return $this->redirectHandler->redirectToIndex($configuration);
}
public function applyStateMachineTransitionAction(Request $request): Response
{
$stateMachine = $this->getStateMachine();
$configuration = $this->requestConfigurationFactory->create($this->metadata,
$request);
$this->isGrantedOr403($configuration, ResourceActions::UPDATE);
$resource = $this->findOr404($configuration);
if ($configuration->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,
$configuration, $resource);
if ($event->isStopped() && !$configuration->isHtmlRequest()) {
throw new HttpException($event->getErrorCode(), $event->getMessage());
}
if ($event->isStopped()) {
$this->flashHelper->addFlashFromEvent($configuration, $event);
$eventResponse = $event->getResponse();
if (null !== $eventResponse) {
return $eventResponse;
}
return $this->redirectHandler->redirectToResource($configuration, $resource);
}
if (!$stateMachine->can($configuration, $resource)) {
throw new BadRequestHttpException();
}
try {
$this->resourceUpdateHandler->handle($resource, $configuration, $this-
>manager);
} catch (UpdateHandlingException $exception) {
if (!$configuration->isHtmlRequest()) {
return $this->createRestView($configuration, $resource, $exception-
>getApiResponseCode());
}
$this->flashHelper->addErrorFlash($configuration, $exception->getFlash());
return $this->redirectHandler->redirectToReferer($configuration);
}
if ($configuration->isHtmlRequest()) {
$this->flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE,
$resource);
}
$postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE,
$configuration, $resource);
if (!$configuration->isHtmlRequest()) {
if ($configuration->getParameters()->get('return_content', true)) {
return $this->createRestView($configuration, $resource, Response::HTTP_OK);
}
return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT);
}
$postEventResponse = $postEvent->getResponse();
if (null !== $postEventResponse) {
return $postEventResponse;
}
return $this->redirectHandler->redirectToResource($configuration, $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(RequestConfiguration $configuration, string
$permission): void
{
if (!$configuration->hasPermission()) {
return;
}
$permission = $configuration->getPermission($permission);
if (!$this->authorizationChecker->isGranted($configuration, $permission)) {
throw new AccessDeniedException();
}
}
/**
* @throws NotFoundHttpException
*/
protected function findOr404(RequestConfiguration $configuration): ResourceInterface
{
if (null === $resource = $this->singleResourceProvider->get($configuration, $this-
>repository)) {
throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this-
>metadata->getHumanizedName()));
}
return $resource;
}
Old Architecture
ResourceController -> 582+456 LoC
New Controller
<?php
final class MainController
{
public function __construct(
private HttpOperationInitiatorInterface $operationInitiator,
private RequestContextInitiatorInterface $requestContextInitiator,
private ProviderInterface $provider,
private ProcessorInterface $processor,
) {
}
public function __invoke(Request $request): Response
{
$operation = $this->operationInitiator->initializeOperation($request);
if (null === $operation) {
throw new RuntimeException('Operation should not be null.');
}
$context = $this->requestContextInitiator->initializeContext($request);
if (null === $operation->canWrite()) {
$operation = $operation->withWrite(!$request->isMethodSafe());
}
$data = $this->provider->provide($operation, $context);
$valid = $request->attributes->getBoolean('is_valid', true);
if (!$valid) {
$operation = $operation->withWrite(false);
}
return $this->processor->process($data, $operation, $context);
}
}
Brand New Architecture
Providers
Processors
Factories
Responders
Metadata
Ground up rework
Fully backward compatible
Improved debugger
sylius:debug:resource
Grid
New
fl
avours
PHP Con
fi
g
<?php
return static function (GridConfig $grid) {
$grid->addGrid(
GridBuilder::create('app_ticket_php')
->addField(StringField::create('email'))
);
};
Service based
final class TicketGrid extends AbstractGrid
{
public static function getName(): string
{
return 'app_ticket';
}
public function buildGrid(GridBuilderInterface $gridBuilder): void
{
$gridBuilder
->addField(
StringField::create('email')
)
;
}
}
make:grid
Attributes
#[Index(grid: 'app_test')]
v2.0 - SyliusCon 2024
Payments
API-
fi
rst
Architecture
Messenger
Work
fl
ow
UI
Admin
Sylius TwigHooks
https://syliusdev-demobap.bunnyenv.com/admin/
Shop
API
Symfony compatibility
Symfony compatibility
Work
fl
ow vs WinzouStateMachine
Clock vs Sylius/Calendar
Symfony v7.0
New folder structure
src/
Addressing/
Model/
Symfony/
Doctrine/
Anything more?
Version compatibility
V1
V2
V1 V2
V1
Magento
Sylius
Release cycle
Symfony +1 year
Thank you!

Mais conteúdo relacionado

Semelhante a Unveiling the Future: Sylius 2.0 New Features

Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Apostrophe
ApostropheApostrophe
Apostrophetompunk
 
Using the Features API
Using the Features APIUsing the Features API
Using the Features APIcgmonroe
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 
Agile data presentation 3 - cambridge
Agile data   presentation 3 - cambridgeAgile data   presentation 3 - cambridge
Agile data presentation 3 - cambridgeRomans Malinovskis
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
Lift 2 0
Lift 2 0Lift 2 0
Lift 2 0SO
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On RailsJohn Wilker
 
ngMess: AngularJS Dependency Injection
ngMess: AngularJS Dependency InjectionngMess: AngularJS Dependency Injection
ngMess: AngularJS Dependency InjectionDzmitry Ivashutsin
 
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the AirAvitoTech
 
Application Layer in PHP
Application Layer in PHPApplication Layer in PHP
Application Layer in PHPPer Bernhardt
 

Semelhante a Unveiling the Future: Sylius 2.0 New Features (20)

Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Using the Features API
Using the Features APIUsing the Features API
Using the Features API
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
Agile data presentation 3 - cambridge
Agile data   presentation 3 - cambridgeAgile data   presentation 3 - cambridge
Agile data presentation 3 - cambridge
 
Reduxing like a pro
Reduxing like a proReduxing like a pro
Reduxing like a pro
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Lift 2 0
Lift 2 0Lift 2 0
Lift 2 0
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
 
ngMess: AngularJS Dependency Injection
ngMess: AngularJS Dependency InjectionngMess: AngularJS Dependency Injection
ngMess: AngularJS Dependency Injection
 
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
 
Application Layer in PHP
Application Layer in PHPApplication Layer in PHP
Application Layer in PHP
 

Mais de Łukasz Chruściel

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Łukasz Chruściel
 
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Łukasz Chruściel
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfŁukasz Chruściel
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsŁukasz Chruściel
 
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Łukasz Chruściel
 
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...Łukasz Chruściel
 
4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdf4Developers - Rozterki i decyzje.pdf
4Developers - Rozterki i decyzje.pdfŁukasz Chruściel
 
4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdf4Developers - Sylius CRUD generation revisited.pdf
4Developers - Sylius CRUD generation revisited.pdfŁukasz Chruściel
 
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Łukasz Chruściel
 
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Łukasz Chruściel
 
How to optimize background processes.pdf
How to optimize background processes.pdfHow to optimize background processes.pdf
How to optimize background processes.pdfŁukasz Chruściel
 
SymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfSymfonyCon - Dilemmas and decisions..pdf
SymfonyCon - Dilemmas and decisions..pdfŁukasz Chruściel
 
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Łukasz Chruściel
 
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Łukasz Chruściel
 
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Łukasz Chruściel
 
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Łukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machineŁukasz Chruściel
 
A short tale about state machine
A short tale about state machineA short tale about state machine
A short tale about state machineŁukasz Chruściel
 
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Łukasz Chruściel
 
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Łukasz Chruściel
 

Mais de Łukasz Chruściel (20)

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
 
SyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdfSyliusCon - Typical pitfalls of Sylius development.pdf
SyliusCon - Typical pitfalls of Sylius development.pdf
 
Need for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API ProjectsNeed for Speed: Removing speed bumps in API Projects
Need for Speed: Removing speed bumps in API Projects
 
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
 

Último

A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationHelp Desk Migration
 
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 softwareinfo611746
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfFurqanuddin10
 
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesGraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesNeo4j
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockSkilrock Technologies
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)Max Lee
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfDeskTrack
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignNeo4j
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAlluxio, Inc.
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTier1 app
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion Clinic
 
Breaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfBreaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfMeon Technology
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems ApproachNeo4j
 
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 Strimzisteffenkarlsson2
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Krakówbim.edu.pl
 
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...rajkumar669520
 
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityAPVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityamy56318795
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionWave PLM
 
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.
 

Último (20)

A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data Migration
 
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
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product UpdatesGraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
GraphSummit Stockholm - Neo4j - Knowledge Graphs and Product Updates
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by Skilrock
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
 
Breaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfBreaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdf
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 
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
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
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...
 
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityAPVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion Production
 
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...
 

Unveiling the Future: Sylius 2.0 New Features

  • 1. Sylius 2.0 New Features Unveiling the Future Łukasz Chruściel
  • 5. Symfony Work fl ow support PriceHistory Attributes & auto con fi guration API not experimental anymore v1.13 - April 2024
  • 22. v1.14 - Autumn 2024 2.0 Compatibility layer
  • 29. * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ declare(strict_types=1); namespace SyliusBundleResourceBundleController; use DoctrinePersistenceObjectManager; use FOSRestBundleViewView; use SyliusBundleResourceBundleEventResourceControllerEvent; use SyliusComponentResourceExceptionDeleteHandlingException; use SyliusComponentResourceExceptionUpdateHandlingException; use SyliusComponentResourceResourceActions; use SyliusResourceDoctrinePersistenceRepositoryInterface; use SyliusResourceFactoryFactoryInterface; use SyliusResourceMetadataMetadataInterface; use SyliusResourceModelResourceInterface; 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 RequestConfigurationFactoryInterface $requestConfigurationFactory; 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 $flashHelper; protected AuthorizationCheckerInterface $authorizationChecker; protected EventDispatcherInterface $eventDispatcher; protected ?StateMachineInterface $stateMachine; protected ResourceUpdateHandlerInterface $resourceUpdateHandler; protected ResourceDeleteHandlerInterface $resourceDeleteHandler; public function __construct( MetadataInterface $metadata, RequestConfigurationFactoryInterface $requestConfigurationFactory, ?ViewHandlerInterface $viewHandler, RepositoryInterface $repository, FactoryInterface $factory, NewResourceFactoryInterface $newResourceFactory, ObjectManager $manager, SingleResourceProviderInterface $singleResourceProvider, ResourcesCollectionProviderInterface $resourcesFinder, ResourceFormFactoryInterface $resourceFormFactory, RedirectHandlerInterface $redirectHandler, FlashHelperInterface $flashHelper, AuthorizationCheckerInterface $authorizationChecker, EventDispatcherInterface $eventDispatcher, ?StateMachineInterface $stateMachine, ResourceUpdateHandlerInterface $resourceUpdateHandler, ResourceDeleteHandlerInterface $resourceDeleteHandler, ) { $this->metadata = $metadata; $this->requestConfigurationFactory = $requestConfigurationFactory; $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->flashHelper = $flashHelper; $this->authorizationChecker = $authorizationChecker; $this->eventDispatcher = $eventDispatcher; $this->stateMachine = $stateMachine; $this->resourceUpdateHandler = $resourceUpdateHandler; $this->resourceDeleteHandler = $resourceDeleteHandler; } public function showAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::SHOW); $resource = $this->findOr404($configuration); $event = $this->eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $resource); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if ($configuration->isHtmlRequest()) { return $this->render($configuration->getTemplate(ResourceActions::SHOW . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, ]); } return $this->createRestView($configuration, $resource); } public function indexAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, } return $this->createRestView($configuration, $resources); } public function createAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::CREATE); $newResource = $this->newResourceFactory->create($configuration, $this->factory); $form = $this->resourceFormFactory->create($configuration, $newResource); $form->handleRequest($request); if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) { $newResource = $form->getData(); $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE, $configuration, $newResource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $newResource); } if ($configuration->hasStateMachine()) { $stateMachine = $this->getStateMachine(); $stateMachine->apply($configuration, $newResource); } $this->repository->add($newResource); if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::CREATE, $newResource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE, $configuration, $newResource); if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $newResource, Response::HTTP_CREATED); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $newResource); } if ($request->isMethod('POST') && $form->isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher- >dispatchInitializeEvent(ResourceActions::CREATE, $configuration, $newResource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($configuration->getTemplate(ResourceActions::CREATE . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $newResource, $this->metadata->getName() => $newResource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function updateAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::UPDATE); $resource = $this->findOr404($configuration); $form = $this->resourceFormFactory->create($configuration, $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, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } try { $this->resourceUpdateHandler->handle($resource, $configuration, $this- >manager); } catch (UpdateHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, $exception- >getApiResponseCode()); return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form- >isSubmitted() && !$form->isValid()) { $responseCode = Response::HTTP_UNPROCESSABLE_ENTITY; } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $form, Response::HTTP_BAD_REQUEST); } $initializeEvent = $this->eventDispatcher- >dispatchInitializeEvent(ResourceActions::UPDATE, $configuration, $resource); $initializeEventResponse = $initializeEvent->getResponse(); if (null !== $initializeEventResponse) { return $initializeEventResponse; } return $this->render($configuration->getTemplate(ResourceActions::UPDATE . '.html'), [ 'configuration' => $configuration, 'metadata' => $this->metadata, 'resource' => $resource, $this->metadata->getName() => $resource, 'form' => $form->createView(), ], null, $responseCode ?? Response::HTTP_OK); } public function deleteAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::DELETE); $resource = $this->findOr404($configuration); if ($configuration->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, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $resource); } try { $this->resourceDeleteHandler->handle($resource, $this->repository); } catch (DeleteHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, $exception- >getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::DELETE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE, $configuration, $resource); if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToIndex($configuration, $resource); } public function bulkDeleteAction(Request $request): Response { $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::BULK_DELETE); $resources = $this->resourcesCollectionProvider->get($configuration, $this- >repository); if ( $configuration->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, $configuration, $resources); foreach ($resources as $resource) { $event = $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $this->flashHelper->addSuccessFlash($configuration, ResourceActions::BULK_DELETE); if (isset($postEvent)) { $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } } return $this->redirectHandler->redirectToIndex($configuration); } public function applyStateMachineTransitionAction(Request $request): Response { $stateMachine = $this->getStateMachine(); $configuration = $this->requestConfigurationFactory->create($this->metadata, $request); $this->isGrantedOr403($configuration, ResourceActions::UPDATE); $resource = $this->findOr404($configuration); if ($configuration->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, $configuration, $resource); if ($event->isStopped() && !$configuration->isHtmlRequest()) { throw new HttpException($event->getErrorCode(), $event->getMessage()); } if ($event->isStopped()) { $this->flashHelper->addFlashFromEvent($configuration, $event); $eventResponse = $event->getResponse(); if (null !== $eventResponse) { return $eventResponse; } return $this->redirectHandler->redirectToResource($configuration, $resource); } if (!$stateMachine->can($configuration, $resource)) { throw new BadRequestHttpException(); } try { $this->resourceUpdateHandler->handle($resource, $configuration, $this- >manager); } catch (UpdateHandlingException $exception) { if (!$configuration->isHtmlRequest()) { return $this->createRestView($configuration, $resource, $exception- >getApiResponseCode()); } $this->flashHelper->addErrorFlash($configuration, $exception->getFlash()); return $this->redirectHandler->redirectToReferer($configuration); } if ($configuration->isHtmlRequest()) { $this->flashHelper->addSuccessFlash($configuration, ResourceActions::UPDATE, $resource); } $postEvent = $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE, $configuration, $resource); if (!$configuration->isHtmlRequest()) { if ($configuration->getParameters()->get('return_content', true)) { return $this->createRestView($configuration, $resource, Response::HTTP_OK); } return $this->createRestView($configuration, null, Response::HTTP_NO_CONTENT); } $postEventResponse = $postEvent->getResponse(); if (null !== $postEventResponse) { return $postEventResponse; } return $this->redirectHandler->redirectToResource($configuration, $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(RequestConfiguration $configuration, string $permission): void { if (!$configuration->hasPermission()) { return; } $permission = $configuration->getPermission($permission); if (!$this->authorizationChecker->isGranted($configuration, $permission)) { throw new AccessDeniedException(); } } /** * @throws NotFoundHttpException */ protected function findOr404(RequestConfiguration $configuration): ResourceInterface { if (null === $resource = $this->singleResourceProvider->get($configuration, $this- >repository)) { throw new NotFoundHttpException(sprintf('The "%s" has not been found', $this- >metadata->getHumanizedName())); } return $resource; } Old Architecture ResourceController -> 582+456 LoC
  • 30. New Controller <?php final class MainController { public function __construct( private HttpOperationInitiatorInterface $operationInitiator, private RequestContextInitiatorInterface $requestContextInitiator, private ProviderInterface $provider, private ProcessorInterface $processor, ) { } public function __invoke(Request $request): Response { $operation = $this->operationInitiator->initializeOperation($request); if (null === $operation) { throw new RuntimeException('Operation should not be null.'); } $context = $this->requestContextInitiator->initializeContext($request); if (null === $operation->canWrite()) { $operation = $operation->withWrite(!$request->isMethodSafe()); } $data = $this->provider->provide($operation, $context); $valid = $request->attributes->getBoolean('is_valid', true); if (!$valid) { $operation = $operation->withWrite(false); } return $this->processor->process($data, $operation, $context); } }
  • 32. Ground up rework Fully backward compatible
  • 35. Grid
  • 37. PHP Con fi g <?php return static function (GridConfig $grid) { $grid->addGrid( GridBuilder::create('app_ticket_php') ->addField(StringField::create('email')) ); };
  • 38. Service based final class TicketGrid extends AbstractGrid { public static function getName(): string { return 'app_ticket'; } public function buildGrid(GridBuilderInterface $gridBuilder): void { $gridBuilder ->addField( StringField::create('email') ) ; } }
  • 44.
  • 47.
  • 48. UI
  • 49. Admin
  • 50.
  • 51.
  • 52.
  • 55. Shop
  • 56.
  • 57. API
  • 58.
  • 60. Symfony compatibility Work fl ow vs WinzouStateMachine Clock vs Sylius/Calendar Symfony v7.0