diff --git a/features/product/managing_product_attributes/deleting_product_attribute.feature b/features/product/managing_product_attributes/deleting_product_attribute.feature
index d1eebb2c0b..37f489f72d 100644
--- a/features/product/managing_product_attributes/deleting_product_attribute.feature
+++ b/features/product/managing_product_attributes/deleting_product_attribute.feature
@@ -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
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 4a38d545b6..9ffaa2c6d2 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -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
diff --git a/src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php b/src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
index f807ee76c8..85fdd466a4 100644
--- a/src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
+++ b/src/Sylius/Bundle/ApiBundle/DataPersister/ProductAttributeDataPersister.php
@@ -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);
}
diff --git a/src/Sylius/Bundle/ApiBundle/EventSubscriber/AttributeEventSubscriber.php b/src/Sylius/Bundle/ApiBundle/EventSubscriber/AttributeEventSubscriber.php
new file mode 100644
index 0000000000..f52943b662
--- /dev/null
+++ b/src/Sylius/Bundle/ApiBundle/EventSubscriber/AttributeEventSubscriber.php
@@ -0,0 +1,60 @@
+ ['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);
+ }
+}
diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/data_persisters.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/data_persisters.xml
index aa0a329656..00ae7067dd 100644
--- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/data_persisters.xml
+++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/data_persisters.xml
@@ -55,7 +55,6 @@
-
diff --git a/src/Sylius/Bundle/ApiBundle/Resources/config/services/event_subscribers.xml b/src/Sylius/Bundle/ApiBundle/Resources/config/services/event_subscribers.xml
index 197917f45c..6e6b43702b 100644
--- a/src/Sylius/Bundle/ApiBundle/Resources/config/services/event_subscribers.xml
+++ b/src/Sylius/Bundle/ApiBundle/Resources/config/services/event_subscribers.xml
@@ -54,5 +54,10 @@
+
+
+
+
+
diff --git a/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductAttributeDataPersisterSpec.php b/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductAttributeDataPersisterSpec.php
index 88f71d505a..b9761c9362 100644
--- a/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductAttributeDataPersisterSpec.php
+++ b/src/Sylius/Bundle/ApiBundle/spec/DataPersister/ProductAttributeDataPersisterSpec.php
@@ -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, []);
diff --git a/src/Sylius/Bundle/ApiBundle/spec/EventSubscriber/AttributeEventSubscriberSpec.php b/src/Sylius/Bundle/ApiBundle/spec/EventSubscriber/AttributeEventSubscriberSpec.php
new file mode 100644
index 0000000000..efd21254b0
--- /dev/null
+++ b/src/Sylius/Bundle/ApiBundle/spec/EventSubscriber/AttributeEventSubscriberSpec.php
@@ -0,0 +1,138 @@
+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(),
+ ));
+ }
+}