Skip to content

Commit

Permalink
feature #727 Bulk update (loic425)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.11 branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | 
| License         | MIT


Commits
-------

695c1a0 Add Event dispatcher provider
4bb7ff0 Flash helper with plural name
0322a5b Bulk update
8935446 Configure state machine resource metadata collection factory
75135eb Fix Psalm error
864dfe6 Remove processor on apply state machine transition
7a72f9e Fix coding standard
82e10a6 Fix flash helper unused code
2e34e82 Remove extra blank lines
ec94944 Improve testing with multiple subscriptions
af51e89 Fix PHPUnit tests
  • Loading branch information
Zales0123 authored Jun 22, 2023
2 parents 7d0b2a4 + af51e89 commit 5ba5c51
Show file tree
Hide file tree
Showing 25 changed files with 707 additions and 173 deletions.
21 changes: 15 additions & 6 deletions src/Bundle/Resources/config/services/metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@
<service id="sylius.resource_metadata_collection.factory.attributes" class="Sylius\Component\Resource\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory">
<argument type="service" id="sylius.resource_registry" />
<argument type="service" id="sylius.routing.factory.operation_route_name_factory" />
<argument>%sylius.state_machine_component.default%</argument>
</service>
<service id="Sylius\Component\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface" alias="sylius.resource_metadata_collection.factory.attributes" />
<service id="sylius.resource_metadata_collection.factory" alias="sylius.resource_metadata_collection.factory.attributes" />

<service id="sylius.resource_metadata_collection.factory.cached"
class="Sylius\Component\Resource\Metadata\Resource\Factory\CachedResourceMetadataCollectionFactory"
decorates="sylius.resource_metadata_collection.factory"
decoration-priority="-10"
<service id="sylius.resource_metadata_collection.factory.state_machine"
class="Sylius\Component\Resource\Metadata\Resource\Factory\StateMachineResourceMetadataCollectionFactory"
decorates="sylius.resource_metadata_collection.factory.attributes"
decoration-priority="300"
>
<argument type="service" id="sylius.cache.metadata.resource_collection" />
<argument type="service" id="sylius.resource_registry" />
<argument type="service" id=".inner" />
<argument>%sylius.state_machine_component.default%</argument>
</service>

<service id="sylius.resource_metadata_collection.factory.doctrine"
Expand Down Expand Up @@ -75,6 +75,15 @@
<argument>%sylius.resource.settings%</argument>
</service>

<service id="sylius.resource_metadata_collection.factory.cached"
class="Sylius\Component\Resource\Metadata\Resource\Factory\CachedResourceMetadataCollectionFactory"
decorates="sylius.resource_metadata_collection.factory.attributes"
decoration-priority="-10"
>
<argument type="service" id="sylius.cache.metadata.resource_collection" />
<argument type="service" id=".inner" />
</service>

<service id="sylius.resource_metadata_operation.initiator.http_operation" class="Sylius\Component\Resource\Metadata\Operation\HttpOperationInitiator">
<argument type="service" id="sylius.resource_registry" />
<argument type="service" id="sylius.resource_metadata_collection.factory" />
Expand Down
8 changes: 4 additions & 4 deletions src/Bundle/Resources/config/services/routing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@
<argument type="service" id=".inner" />
</service>

<service id="sylius.routing.factory.operation_route_path_factory.update"
class="Sylius\Component\Resource\Symfony\Routing\Factory\UpdateOperationRoutePathFactory"
<service id="sylius.routing.factory.operation_route_path_factory.bulk_operation"
class="Sylius\Component\Resource\Symfony\Routing\Factory\BulkOperationRoutePathFactory"
decorates="sylius.routing.factory.operation_route_path_factory.default"
decoration-priority="-40"
>
<argument type="service" id=".inner" />
</service>

<service id="sylius.routing.factory.operation_route_path_factory.bulk_operation"
class="Sylius\Component\Resource\Symfony\Routing\Factory\BulkOperationRoutePathFactory"
<service id="sylius.routing.factory.operation_route_path_factory.update"
class="Sylius\Component\Resource\Symfony\Routing\Factory\UpdateOperationRoutePathFactory"
decorates="sylius.routing.factory.operation_route_path_factory.default"
decoration-priority="-30"
>
Expand Down
18 changes: 18 additions & 0 deletions src/Bundle/Resources/config/services/state.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@

<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sylius.resource_factory.expression_language" class="Symfony\Component\ExpressionLanguage\ExpressionLanguage" />

<service id="sylius.resource_factory.argument_parser" class="Sylius\Component\Resource\Factory\ArgumentParser">
<argument type="service" id="sylius.resource_factory.expression_language" />
<argument type="service" id="request_stack" />
<argument type="service" id="security.token_storage" on-invalid="null" />
</service>
<service id="Sylius\Component\Resource\Factory\ArgumentParserInterface" alias="sylius.resource_factory.argument_parser" />

<service id="sylius.state.provider.event_dispatcher"
class="Sylius\Component\Resource\State\EventDispatcherProvider"
decorates="sylius.state.provider"
decoration-priority="-32"
>
<argument type="service" id=".inner" />
<argument type="service" id="sylius.dispatcher.operation" />
</service>

<service id="sylius.state.provider" class="Sylius\Component\Resource\State\Provider">
<argument type="tagged_locator" tag="sylius.state_provider" />
</service>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% set path = options.link.url|default(path(options.link.route|default(grid.requestConfiguration.getRouteName('bulk_accept')), options.link.parameters|default({}))) %}

<form action="{{ path }}" method="post">
<input type="hidden" name="_method" value="PATCH">
<button type="submit">
<i class="icon trash"></i> Bulk accept
</button>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('bulk_accept') }}" />

{% for resource in grid.data %}
<input type="hidden" name="ids[]" value="{{ resource.id }}" />
{% endfor %}
</form>
4 changes: 1 addition & 3 deletions src/Component/Metadata/ApplyStateMachineTransition.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

namespace Sylius\Component\Resource\Metadata;

use Sylius\Component\Resource\StateMachine\State\ApplyStateMachineTransitionProcessor;

/**
* @experimental
*/
Expand Down Expand Up @@ -52,7 +50,7 @@ public function __construct(
shortName: $shortName ?? $stateMachineTransition ?? 'apply_state_machine_transition',
name: $name,
provider: $provider,
processor: $processor ?? ApplyStateMachineTransitionProcessor::class,
processor: $processor,
responder: $responder,
repository: $repository,
repositoryMethod: $repositoryMethod,
Expand Down
117 changes: 117 additions & 0 deletions src/Component/Metadata/BulkUpdate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Component\Resource\Metadata;

/**
* @experimental
*/
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
final class BulkUpdate extends HttpOperation implements UpdateOperationInterface, StateMachineAwareOperationInterface, BulkOperationInterface
{
public function __construct(
?array $methods = null,
?string $path = null,
?string $routePrefix = null,
?string $template = null,
?string $shortName = null,
?string $name = null,
string|callable|null $provider = null,
string|callable|null $processor = null,
string|callable|null $responder = null,
string|callable|null $repository = null,
?string $repositoryMethod = null,
?array $repositoryArguments = null,
?string $grid = null,
?bool $read = null,
?bool $write = null,
?bool $validate = null,
?bool $deserialize = null,
?bool $serialize = null,
?string $formType = null,
?array $formOptions = null,
?array $validationContext = null,
?string $eventShortName = null,
?string $redirectToRoute = null,
?array $redirectArguments = null,
private ?string $stateMachineComponent = null,
private ?string $stateMachineTransition = null,
private ?string $stateMachineGraph = null,
) {
parent::__construct(
methods: $methods ?? ['PUT', 'PATCH'],
path: $path,
routePrefix: $routePrefix,
template: $template,
shortName: $shortName ?? 'bulk_update',
name: $name,
provider: $provider,
processor: $processor,
responder: $responder,
repository: $repository,
repositoryMethod: $repositoryMethod,
repositoryArguments: $repositoryArguments,
grid: $grid,
read: $read,
write: $write,
validate: $validate,
deserialize: $deserialize,
serialize: $serialize,
formType: $formType,
formOptions: $formOptions,
validationContext: $validationContext,
eventShortName: $eventShortName,
redirectToRoute: $redirectToRoute,
redirectArguments: $redirectArguments,
);
}

public function getStateMachineComponent(): ?string
{
return $this->stateMachineComponent;
}

public function withStateMachineComponent(?string $stateMachineComponent): self
{
$self = clone $this;
$self->stateMachineComponent = $stateMachineComponent;

return $self;
}

public function getStateMachineTransition(): ?string
{
return $this->stateMachineTransition;
}

public function withStateMachineTransition(string $stateMachineTransition): self
{
$self = clone $this;
$self->stateMachineTransition = $stateMachineTransition;

return $self;
}

public function getStateMachineGraph(): ?string
{
return $this->stateMachineGraph;
}

public function withStateMachineGraph(string $stateMachineGraph): self
{
$self = clone $this;
$self->stateMachineGraph = $stateMachineGraph;

return $self;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Sylius\Component\Resource\Metadata\RegistryInterface;
use Sylius\Component\Resource\Metadata\Resource as ResourceMetadata;
use Sylius\Component\Resource\Metadata\Resource\ResourceMetadataCollection;
use Sylius\Component\Resource\Metadata\StateMachineAwareOperationInterface;
use Sylius\Component\Resource\Reflection\ClassReflection;
use Sylius\Component\Resource\Symfony\Request\State\Provider;
use Sylius\Component\Resource\Symfony\Request\State\Responder;
Expand All @@ -32,7 +31,6 @@ final class AttributesResourceMetadataCollectionFactory implements ResourceMetad
public function __construct(
private RegistryInterface $resourceRegistry,
private OperationRouteNameFactory $operationRouteNameFactory,
private ?string $defaultStateMachineComponent,
) {
}

Expand Down Expand Up @@ -184,17 +182,6 @@ private function getOperationWithDefaults(ResourceMetadata $resource, Operation
$formOptions = $this->buildFormOptions($operation, $resourceConfiguration);
$operation = $operation->withFormOptions($formOptions);

if (
$operation instanceof StateMachineAwareOperationInterface &&
null === $operation->getStateMachineComponent() &&
method_exists($resourceConfiguration, 'getStateMachineComponent')
) {
$stateMachineComponent = $resourceConfiguration->getStateMachineComponent() ?? $this->defaultStateMachineComponent;

/** @var Operation $operation */
$operation = $operation->withStateMachineComponent($stateMachineComponent);
}

if ($operation instanceof HttpOperation) {
if (null === $operation->getRoutePrefix()) {
$operation = $operation->withRoutePrefix($resource->getRoutePrefix() ?? null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Sylius\Component\Resource\Metadata\Resource\Factory;

use Sylius\Component\Resource\Metadata\BulkOperationInterface;
use Sylius\Component\Resource\Metadata\CreateOperationInterface;
use Sylius\Component\Resource\Metadata\DeleteOperationInterface;
use Sylius\Component\Resource\Metadata\HttpOperation;
Expand Down Expand Up @@ -65,7 +66,18 @@ private function addDefaults(ResourceMetadata $resource, HttpOperation $operatio
return $operation;
}

if ($operation instanceof CreateOperationInterface || $operation instanceof UpdateOperationInterface) {
if ($operation instanceof BulkOperationInterface) {
$newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'index');

if (null !== $newOperation) {
return $newOperation;
}
}

if (
$operation instanceof CreateOperationInterface ||
$operation instanceof UpdateOperationInterface
) {
$newOperation = $this->setRedirectIfRouteExists($resource, $operation, 'show');

if (null !== $newOperation) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Component\Resource\Metadata\Resource\Factory;

use Sylius\Component\Resource\Metadata\MetadataInterface;
use Sylius\Component\Resource\Metadata\Operation;
use Sylius\Component\Resource\Metadata\Operations;
use Sylius\Component\Resource\Metadata\RegistryInterface;
use Sylius\Component\Resource\Metadata\Resource as ResourceMetadata;
use Sylius\Component\Resource\Metadata\Resource\ResourceMetadataCollection;
use Sylius\Component\Resource\Metadata\StateMachineAwareOperationInterface;
use Sylius\Component\Resource\StateMachine\State\ApplyStateMachineTransitionProcessor;

final class StateMachineResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
{
public function __construct(
private RegistryInterface $resourceRegistry,
private ResourceMetadataCollectionFactoryInterface $decorated,
private ?string $defaultStateMachineComponent,
) {
}

public function create(string $resourceClass): ResourceMetadataCollection
{
$resourceCollectionMetadata = $this->decorated->create($resourceClass);

/** @var ResourceMetadata $resource */
foreach ($resourceCollectionMetadata->getIterator() as $i => $resource) {
$resourceConfiguration = $this->resourceRegistry->get($resource->getAlias() ?? '');
$operations = $resource->getOperations() ?? new Operations();

/** @var Operation $operation */
foreach ($operations as $operation) {
/** @var string $key */
$key = $operation->getName();

$operations->add($key, $this->addDefaults($resourceConfiguration, $operation));
}

$resource = $resource->withOperations($operations);

$resourceCollectionMetadata[$i] = $resource;
}

return $resourceCollectionMetadata;
}

private function addDefaults(MetadataInterface $resourceConfiguration, Operation $operation): Operation
{
if (!$operation instanceof StateMachineAwareOperationInterface) {
return $operation;
}

if (
null === $operation->getStateMachineComponent() &&
method_exists($resourceConfiguration, 'getStateMachineComponent')
) {
$stateMachineComponent = $resourceConfiguration->getStateMachineComponent() ?? $this->defaultStateMachineComponent;

/** @var Operation $operation */
$operation = $operation->withStateMachineComponent($stateMachineComponent);
}

if (
method_exists($operation, 'getStateMachineTransition') &&
null !== $operation->getStateMachineTransition() &&
null === $operation->getProcessor()
) {
$operation = $operation->withProcessor(ApplyStateMachineTransitionProcessor::class);
}

return $operation;
}
}
Loading

0 comments on commit 5ba5c51

Please sign in to comment.