From d9f08304cdf335689481c6f354bb963890979463 Mon Sep 17 00:00:00 2001 From: TheMilek Date: Wed, 11 Mar 2026 08:23:24 +0100 Subject: [PATCH 1/3] Make constraints compatible with symfony 8 --- src/Bundle/Validator/Constraints/Disabled.php | 12 ++++++++++++ src/Bundle/Validator/Constraints/Enabled.php | 12 ++++++++++++ .../UniqueWithinCollectionConstraint.php | 14 ++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/Bundle/Validator/Constraints/Disabled.php b/src/Bundle/Validator/Constraints/Disabled.php index b4ae915f8..303294e38 100644 --- a/src/Bundle/Validator/Constraints/Disabled.php +++ b/src/Bundle/Validator/Constraints/Disabled.php @@ -14,6 +14,7 @@ namespace Sylius\Bundle\ResourceBundle\Validator\Constraints; use Sylius\Bundle\ResourceBundle\Validator\DisabledValidator; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; #[\Attribute] @@ -21,6 +22,17 @@ final class Disabled extends Constraint { public string $message = 'sylius.resource.not_disabled'; + #[HasNamedArguments] + public function __construct( + string $message = 'sylius.resource.not_disabled', + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(groups: $groups, payload: $payload); + + $this->message = $message; + } + public function getTargets(): array { return [self::PROPERTY_CONSTRAINT, self::CLASS_CONSTRAINT]; diff --git a/src/Bundle/Validator/Constraints/Enabled.php b/src/Bundle/Validator/Constraints/Enabled.php index 5c160ef51..5f7e11b57 100644 --- a/src/Bundle/Validator/Constraints/Enabled.php +++ b/src/Bundle/Validator/Constraints/Enabled.php @@ -14,6 +14,7 @@ namespace Sylius\Bundle\ResourceBundle\Validator\Constraints; use Sylius\Bundle\ResourceBundle\Validator\EnabledValidator; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; #[\Attribute] @@ -21,6 +22,17 @@ final class Enabled extends Constraint { public string $message = 'sylius.resource.not_enabled'; + #[HasNamedArguments] + public function __construct( + string $message = 'sylius.resource.not_enabled', + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(groups: $groups, payload: $payload); + + $this->message = $message; + } + public function getTargets(): array { return [self::PROPERTY_CONSTRAINT, self::CLASS_CONSTRAINT]; diff --git a/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php b/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php index 3a7fb1e40..6fafe3730 100644 --- a/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php +++ b/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php @@ -14,6 +14,7 @@ namespace Sylius\Bundle\ResourceBundle\Validator\Constraints; use Sylius\Bundle\ResourceBundle\Validator\UniqueWithinCollectionConstraintValidator; +use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; #[\Attribute] @@ -23,6 +24,19 @@ final class UniqueWithinCollectionConstraint extends Constraint public string $attributePath = 'code'; + #[HasNamedArguments] + public function __construct( + string $message = 'This code must be unique within this collection.', + string $attributePath = 'code', + ?array $groups = null, + mixed $payload = null, + ) { + parent::__construct(groups: $groups, payload: $payload); + + $this->message = $message; + $this->attributePath = $attributePath; + } + public function validatedBy(): string { return UniqueWithinCollectionConstraintValidator::class; From fb9c500f9aa75ac2e403b9f38514f0ce4dae8065 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Mon, 22 Jun 2026 06:46:15 +0200 Subject: [PATCH 2/3] Preserve BC for array options on Symfony 8 validator constraints --- UPGRADE.md | 18 ++++++ src/Bundle/Validator/Constraints/Disabled.php | 17 +++++ src/Bundle/Validator/Constraints/Enabled.php | 17 +++++ .../UniqueWithinCollectionConstraint.php | 18 ++++++ .../Validator/Constraints/DisabledTest.php | 57 +++++++++++++++++ .../Validator/Constraints/EnabledTest.php | 57 +++++++++++++++++ .../UniqueWithinCollectionConstraintTest.php | 63 +++++++++++++++++++ 7 files changed, 247 insertions(+) create mode 100644 tests/Bundle/Validator/Constraints/DisabledTest.php create mode 100644 tests/Bundle/Validator/Constraints/EnabledTest.php create mode 100644 tests/Bundle/Validator/Constraints/UniqueWithinCollectionConstraintTest.php diff --git a/UPGRADE.md b/UPGRADE.md index 27713fff0..fbb553baf 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,23 @@ ## UPGRADE FOR `1.14.x` +### FROM `1.14.1` to `1.14.2` + +#### Validator Constraints + +The `Enabled`, `Disabled` and `UniqueWithinCollectionConstraint` constraints now use named arguments +(`#[HasNamedArguments]`) to be compatible with Symfony 8. + +Passing an array of options to configure these constraints is deprecated and will be removed in 2.0. +Use named arguments instead: + +```php +// Before (deprecated) +new Enabled(['message' => 'My message']); + +// After +new Enabled(message: 'My message'); +``` + ### FROM `1.13.x` to `1.14.x` #### Minimal Requirements diff --git a/src/Bundle/Validator/Constraints/Disabled.php b/src/Bundle/Validator/Constraints/Disabled.php index 303294e38..b693c6055 100644 --- a/src/Bundle/Validator/Constraints/Disabled.php +++ b/src/Bundle/Validator/Constraints/Disabled.php @@ -22,12 +22,29 @@ final class Disabled extends Constraint { public string $message = 'sylius.resource.not_disabled'; + /** + * @param array{message?: string, groups?: array|null, payload?: mixed}|null $options + */ #[HasNamedArguments] public function __construct( + ?array $options = null, string $message = 'sylius.resource.not_disabled', ?array $groups = null, mixed $payload = null, ) { + if (\is_array($options)) { + trigger_deprecation( + 'sylius/resource-bundle', + '1.14', + 'Passing an array of options to configure the "%s" constraint is deprecated and will be removed in 2.0, use named arguments instead.', + static::class, + ); + + $message = $options['message'] ?? $message; + $groups ??= $options['groups'] ?? null; + $payload ??= $options['payload'] ?? null; + } + parent::__construct(groups: $groups, payload: $payload); $this->message = $message; diff --git a/src/Bundle/Validator/Constraints/Enabled.php b/src/Bundle/Validator/Constraints/Enabled.php index 5f7e11b57..04c147c73 100644 --- a/src/Bundle/Validator/Constraints/Enabled.php +++ b/src/Bundle/Validator/Constraints/Enabled.php @@ -22,12 +22,29 @@ final class Enabled extends Constraint { public string $message = 'sylius.resource.not_enabled'; + /** + * @param array{message?: string, groups?: array|null, payload?: mixed}|null $options + */ #[HasNamedArguments] public function __construct( + ?array $options = null, string $message = 'sylius.resource.not_enabled', ?array $groups = null, mixed $payload = null, ) { + if (\is_array($options)) { + trigger_deprecation( + 'sylius/resource-bundle', + '1.14', + 'Passing an array of options to configure the "%s" constraint is deprecated and will be removed in 2.0, use named arguments instead.', + static::class, + ); + + $message = $options['message'] ?? $message; + $groups ??= $options['groups'] ?? null; + $payload ??= $options['payload'] ?? null; + } + parent::__construct(groups: $groups, payload: $payload); $this->message = $message; diff --git a/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php b/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php index 6fafe3730..ad3c17755 100644 --- a/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php +++ b/src/Bundle/Validator/Constraints/UniqueWithinCollectionConstraint.php @@ -24,13 +24,31 @@ final class UniqueWithinCollectionConstraint extends Constraint public string $attributePath = 'code'; + /** + * @param array{message?: string, attributePath?: string, groups?: array|null, payload?: mixed}|null $options + */ #[HasNamedArguments] public function __construct( + ?array $options = null, string $message = 'This code must be unique within this collection.', string $attributePath = 'code', ?array $groups = null, mixed $payload = null, ) { + if (\is_array($options)) { + trigger_deprecation( + 'sylius/resource-bundle', + '1.14', + 'Passing an array of options to configure the "%s" constraint is deprecated and will be removed in 2.0, use named arguments instead.', + static::class, + ); + + $message = $options['message'] ?? $message; + $attributePath = $options['attributePath'] ?? $attributePath; + $groups ??= $options['groups'] ?? null; + $payload ??= $options['payload'] ?? null; + } + parent::__construct(groups: $groups, payload: $payload); $this->message = $message; diff --git a/tests/Bundle/Validator/Constraints/DisabledTest.php b/tests/Bundle/Validator/Constraints/DisabledTest.php new file mode 100644 index 000000000..29e78e790 --- /dev/null +++ b/tests/Bundle/Validator/Constraints/DisabledTest.php @@ -0,0 +1,57 @@ +assertSame('sylius.resource.not_disabled', $constraint->message); + } + + public function testAcceptsNamedArguments(): void + { + $constraint = new Disabled(message: 'Custom message'); + + $this->assertSame('Custom message', $constraint->message); + } + + /** + * @group legacy + */ + public function testSupportsLegacyArrayOptionsAndTriggersDeprecation(): void + { + $deprecations = []; + set_error_handler(static function (int $type, string $message) use (&$deprecations): bool { + $deprecations[] = $message; + + return true; + }, \E_USER_DEPRECATED); + + try { + $constraint = new Disabled(['message' => 'Legacy message']); + } finally { + restore_error_handler(); + } + + $this->assertSame('Legacy message', $constraint->message); + $this->assertCount(1, $deprecations); + $this->assertStringContainsString('Passing an array of options', $deprecations[0]); + } +} diff --git a/tests/Bundle/Validator/Constraints/EnabledTest.php b/tests/Bundle/Validator/Constraints/EnabledTest.php new file mode 100644 index 000000000..487f8df40 --- /dev/null +++ b/tests/Bundle/Validator/Constraints/EnabledTest.php @@ -0,0 +1,57 @@ +assertSame('sylius.resource.not_enabled', $constraint->message); + } + + public function testAcceptsNamedArguments(): void + { + $constraint = new Enabled(message: 'Custom message'); + + $this->assertSame('Custom message', $constraint->message); + } + + /** + * @group legacy + */ + public function testSupportsLegacyArrayOptionsAndTriggersDeprecation(): void + { + $deprecations = []; + set_error_handler(static function (int $type, string $message) use (&$deprecations): bool { + $deprecations[] = $message; + + return true; + }, \E_USER_DEPRECATED); + + try { + $constraint = new Enabled(['message' => 'Legacy message']); + } finally { + restore_error_handler(); + } + + $this->assertSame('Legacy message', $constraint->message); + $this->assertCount(1, $deprecations); + $this->assertStringContainsString('Passing an array of options', $deprecations[0]); + } +} diff --git a/tests/Bundle/Validator/Constraints/UniqueWithinCollectionConstraintTest.php b/tests/Bundle/Validator/Constraints/UniqueWithinCollectionConstraintTest.php new file mode 100644 index 000000000..06c3f660a --- /dev/null +++ b/tests/Bundle/Validator/Constraints/UniqueWithinCollectionConstraintTest.php @@ -0,0 +1,63 @@ +assertSame('This code must be unique within this collection.', $constraint->message); + $this->assertSame('code', $constraint->attributePath); + } + + public function testAcceptsNamedArguments(): void + { + $constraint = new UniqueWithinCollectionConstraint(message: 'Custom message', attributePath: 'name'); + + $this->assertSame('Custom message', $constraint->message); + $this->assertSame('name', $constraint->attributePath); + } + + /** + * @group legacy + */ + public function testSupportsLegacyArrayOptionsAndTriggersDeprecation(): void + { + $deprecations = []; + set_error_handler(static function (int $type, string $message) use (&$deprecations): bool { + $deprecations[] = $message; + + return true; + }, \E_USER_DEPRECATED); + + try { + $constraint = new UniqueWithinCollectionConstraint([ + 'message' => 'Legacy message', + 'attributePath' => 'name', + ]); + } finally { + restore_error_handler(); + } + + $this->assertSame('Legacy message', $constraint->message); + $this->assertSame('name', $constraint->attributePath); + $this->assertCount(1, $deprecations); + $this->assertStringContainsString('Passing an array of options', $deprecations[0]); + } +} From e5e5700b8834957882c2d40f566ab0c1e4efa855 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Mon, 22 Jun 2026 07:15:50 +0200 Subject: [PATCH 3/3] Fix PHPStan on Doctrine ORM 3 / Persistence 4 (final MappingDriverChain) --- phpstan.neon | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index 7806d08bd..bc9b5de55 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -108,6 +108,13 @@ parameters: # See: src/Bundle/DependencyInjection/Driver/Doctrine/AbstractDoctrineDriver.php:46 - '/Class Doctrine\\Common\\Persistence\\ObjectManager not found/' + # Doctrine Persistence 4.x marks MappingDriverChain as @final + # ResourceMappingDriverChain has always extended it to add resource mapping support + # The parent is only soft-final (@final phpdoc), so extending it stays runtime-safe + # Pulled in transitively by Doctrine ORM 3.x; ORM 2.x ships an older, non-final version + # See: src/Bundle/Doctrine/ResourceMappingDriverChain.php + - '/Class Sylius\\Bundle\\ResourceBundle\\Doctrine\\ResourceMappingDriverChain extends @final class Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain/' + # Doctrine ORM 3.x introduces new mapping classes # AssociationMapping class exists only in ORM 3.x, not in ORM 2.x # Code supports both versions, so we need to ignore these errors when running with ORM 2.x