[Api][Attribute] Switch storage type setting from persister to event subscriber

This commit is contained in:
Jan Goralski 2023-09-11 16:35:25 +02:00 committed by Grzegorz Sadowski
parent cf5f3ac76b
commit 41ba211323
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
8 changed files with 233 additions and 43 deletions

View file

@ -9,13 +9,13 @@ Feature: Deleting a product attribute
And the store has a product "44 Magnum"
@ui @api
Scenario: Try deleting a attribute from the registry when product use him
Scenario: Try deleting a attribute from the registry when a product uses it
Given this product has a text attribute "Gun caliber" with value "11 mm"
When I delete this product attribute
Then I should be notified that it is in use
@ui @api
Scenario: Deleting a text product attribute when not by used
Scenario: Deleting a text product attribute when it's not used
Given the store has a text product attribute "Gun caliber"
When I delete this product attribute
Then I should be notified that it has been successfully deleted

View file

@ -770,6 +770,26 @@ parameters:
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/MessengerDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\ProductAttributeDataPersister\\:\\:persist\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\ProductAttributeDataPersister\\:\\:remove\\(\\) has no return type specified\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\ProductAttributeDataPersister\\:\\:remove\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\ProductAttributeDataPersister\\:\\:supports\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
-
message: "#^Method Sylius\\\\Bundle\\\\ApiBundle\\\\DataPersister\\\\ProductTaxonDataPersister\\:\\:persist\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
@ -2250,6 +2270,11 @@ parameters:
count: 1
path: src/Sylius/Bundle/CoreBundle/CatalogPromotion/Checker/CatalogPromotionEligibilityChecker.php
-
message: "#^Method Sylius\\\\Bundle\\\\CoreBundle\\\\CatalogPromotion\\\\Checker\\\\InForTaxonsScopeVariantChecker\\:\\:__construct\\(\\) has parameter \\$taxonRepository with generic interface Sylius\\\\Component\\\\Taxonomy\\\\Repository\\\\TaxonRepositoryInterface but does not specify its types\\: T$#"
count: 1
path: src/Sylius/Bundle/CoreBundle/CatalogPromotion/Checker/InForTaxonsScopeVariantChecker.php
-
message: "#^Method Sylius\\\\Bundle\\\\CoreBundle\\\\CatalogPromotion\\\\Checker\\\\ProductVariantForCatalogPromotionEligibility\\:\\:__construct\\(\\) has parameter \\$locator with generic class Symfony\\\\Component\\\\DependencyInjection\\\\ServiceLocator but does not specify its types\\: T$#"
count: 1

View file

@ -7,17 +7,13 @@ namespace Sylius\Bundle\ApiBundle\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Sylius\Bundle\ApiBundle\Exception\ProductAttributeCannotBeRemoved;
use Sylius\Component\Attribute\AttributeType\AttributeTypeInterface;
use Sylius\Component\Product\Model\ProductAttributeInterface;
use Sylius\Component\Registry\ServiceRegistryInterface;
use Webmozart\Assert\Assert;
/** @experimental */
final class ProductAttributeDataPersister implements ContextAwareDataPersisterInterface
{
public function __construct(
private ContextAwareDataPersisterInterface $decoratedDataPersister,
private ServiceRegistryInterface $attributeTypeRegistry,
) {
}
@ -29,13 +25,6 @@ final class ProductAttributeDataPersister implements ContextAwareDataPersisterIn
/** @param ProductAttributeInterface $data */
public function persist($data, array $context = [])
{
if (null === $data->getStorageType() && null !== $data->getType()) {
/** @var AttributeTypeInterface $attributeType */
$attributeType = $this->attributeTypeRegistry->get($data->getType());
$data->setStorageType($attributeType->getStorageType());
}
return $this->decoratedDataPersister->persist($data, $context);
}

View file

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use Sylius\Component\Attribute\AttributeType\AttributeTypeInterface;
use Sylius\Component\Attribute\Model\AttributeInterface;
use Sylius\Component\Registry\ServiceRegistryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/** @experimental */
final class AttributeEventSubscriber implements EventSubscriberInterface
{
public function __construct(private ServiceRegistryInterface $attributeTypeRegistry)
{
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::VIEW => ['assignStorageType', EventPriorities::PRE_VALIDATE],
];
}
public function assignStorageType(ViewEvent $event): void
{
$attribute = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (
!$attribute instanceof AttributeInterface ||
!in_array($method, [Request::METHOD_POST, Request::METHOD_PUT], true)
) {
return;
}
if (null === $attribute->getType() || '' === $attribute->getType()) {
return;
}
if (
null !== $attribute->getStorageType() ||
!$this->attributeTypeRegistry->has($attribute->getType())
) {
return;
}
/** @var AttributeTypeInterface $attributeType */
$attributeType = $this->attributeTypeRegistry->get($attribute->getType());
$attribute->setStorageType($attributeType->getStorageType());
$event->setControllerResult($attribute);
}
}

View file

@ -55,7 +55,6 @@
<service id="Sylius\Bundle\ApiBundle\DataPersister\ProductAttributeDataPersister">
<argument type="service" id="api_platform.doctrine.orm.data_persister" />
<argument type="service" id="sylius.registry.attribute_type" />
<tag name="api_platform.data_persister" />
</service>

View file

@ -54,5 +54,10 @@
<argument type="service" id="sylius.generator.taxon_slug" />
<tag name="kernel.event_subscriber" />
</service>
<service id="Sylius\Bundle\ApiBundle\EventSubscriber\AttributeEventSubscriber">
<argument type="service" id="sylius.registry.attribute_type" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

View file

@ -17,17 +17,13 @@ use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Exception\ProductAttributeCannotBeRemoved;
use Sylius\Component\Attribute\AttributeType\AttributeTypeInterface;
use Sylius\Component\Attribute\AttributeType\TextAttributeType;
use Sylius\Component\Attribute\Model\AttributeValueInterface;
use Sylius\Component\Product\Model\ProductAttributeInterface;
use Sylius\Component\Registry\ServiceRegistryInterface;
final class ProductAttributeDataPersisterSpec extends ObjectBehavior
{
function let(ContextAwareDataPersisterInterface $persister, ServiceRegistryInterface $typesRegistry): void
function let(ContextAwareDataPersisterInterface $persister): void
{
$this->beConstructedWith($persister, $typesRegistry);
$this->beConstructedWith($persister);
}
function it_is_a_context_aware_persister(): void
@ -41,32 +37,10 @@ final class ProductAttributeDataPersisterSpec extends ObjectBehavior
$this->supports($productAttribute)->shouldReturn(true);
}
function it_uses_inner_persister_to_persist_product_attribute_when_storage_type_is_set(
function it_uses_inner_persister_to_persist_product_attribute(
ContextAwareDataPersisterInterface $persister,
ProductAttributeInterface $productAttribute,
): void {
$productAttribute->getType()->willReturn(TextAttributeType::TYPE);
$productAttribute->getStorageType()->willReturn(AttributeValueInterface::STORAGE_TEXT);
$persister->persist($productAttribute, [])->shouldBeCalled();
$this->persist($productAttribute, []);
}
function it_sets_storage_type_when_none_is_set_but_type_is(
ContextAwareDataPersisterInterface $persister,
ServiceRegistryInterface $typesRegistry,
AttributeTypeInterface $attributeType,
ProductAttributeInterface $productAttribute,
): void {
$productAttribute->getType()->willReturn(TextAttributeType::TYPE);
$productAttribute->getStorageType()->willReturn(null);
$attributeType->getStorageType()->willReturn(AttributeValueInterface::STORAGE_TEXT);
$typesRegistry->get(TextAttributeType::TYPE)->willReturn($attributeType);
$productAttribute->setStorageType(AttributeValueInterface::STORAGE_TEXT)->shouldBeCalled();
$persister->persist($productAttribute, [])->shouldBeCalled();
$this->persist($productAttribute, []);

View file

@ -0,0 +1,138 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace spec\Sylius\Bundle\ApiBundle\EventSubscriber;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Component\Attribute\AttributeType\AttributeTypeInterface;
use Sylius\Component\Attribute\Model\AttributeInterface;
use Sylius\Component\Registry\ServiceRegistryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
final class AttributeEventSubscriberSpec extends ObjectBehavior
{
function let(ServiceRegistryInterface $registry): void
{
$this->beConstructedWith($registry);
}
function it_implements_event_subscriber_interface(): void
{
$this->shouldImplement(EventSubscriberInterface::class);
}
function it_does_nothing_when_controller_result_is_not_an_attribute(
ServiceRegistryInterface $registry,
HttpKernelInterface $kernel,
Request $request,
): void {
$request->getMethod()->shouldBeCalled();
$registry->has(Argument::any())->shouldNotBeCalled();
$this->assignStorageType(new ViewEvent(
$kernel->getWrappedObject(),
$request->getWrappedObject(),
HttpKernelInterface::MAIN_REQUEST,
new \stdClass(),
));
}
function it_does_nothing_when_attribute_has_no_type(
ServiceRegistryInterface $registry,
HttpKernelInterface $kernel,
Request $request,
AttributeInterface $attribute,
): void {
$request->getMethod()->willReturn(Request::METHOD_POST);
$attribute->getType()->willReturn(null);
$registry->has(Argument::any())->shouldNotBeCalled();
$this->assignStorageType(new ViewEvent(
$kernel->getWrappedObject(),
$request->getWrappedObject(),
HttpKernelInterface::MAIN_REQUEST,
$attribute->getWrappedObject(),
));
}
function it_does_nothing_when_attribute_has_a_storage_type(
ServiceRegistryInterface $registry,
HttpKernelInterface $kernel,
Request $request,
AttributeInterface $attribute,
): void {
$request->getMethod()->willReturn(Request::METHOD_POST);
$attribute->getType()->willReturn('text');
$attribute->getStorageType()->willReturn('text');
$registry->has(Argument::any())->shouldNotBeCalled();
$this->assignStorageType(new ViewEvent(
$kernel->getWrappedObject(),
$request->getWrappedObject(),
HttpKernelInterface::MAIN_REQUEST,
$attribute->getWrappedObject(),
));
}
function it_does_nothing_when_attribute_type_is_not_registered(
ServiceRegistryInterface $registry,
HttpKernelInterface $kernel,
Request $request,
AttributeInterface $attribute,
): void {
$request->getMethod()->willReturn(Request::METHOD_POST);
$attribute->getType()->willReturn('foo');
$attribute->getStorageType()->willReturn(null);
$registry->has('foo')->willReturn(false);
$this->assignStorageType(new ViewEvent(
$kernel->getWrappedObject(),
$request->getWrappedObject(),
HttpKernelInterface::MAIN_REQUEST,
$attribute->getWrappedObject(),
));
}
function it_sets_storage_type_based_on_set_attribute_type(
ServiceRegistryInterface $registry,
HttpKernelInterface $kernel,
Request $request,
AttributeInterface $attribute,
AttributeTypeInterface $attributeType,
): void {
$request->getMethod()->willReturn(Request::METHOD_POST);
$attribute->getType()->willReturn('foo');
$attribute->getStorageType()->willReturn(null);
$registry->has('foo')->willReturn(true);
$registry->get('foo')->willReturn($attributeType);
$attributeType->getStorageType()->willReturn('bar');
$attribute->setStorageType('bar')->shouldBeCalled();
$this->assignStorageType(new ViewEvent(
$kernel->getWrappedObject(),
$request->getWrappedObject(),
HttpKernelInterface::MAIN_REQUEST,
$attribute->getWrappedObject(),
));
}
}