Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DoctrineBridge] Allow to ignore specific nullable fields in UniqueEntity #49293

Merged
merged 1 commit into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@ public function testValidateUniquenessWithNull(UniqueEntity $constraint)

/**
* @dataProvider provideConstraintsWithIgnoreNullDisabled
* @dataProvider provideConstraintsWithIgnoreNullEnabledOnFirstField
*/
public function testValidateUniquenessWithIgnoreNullDisabled(UniqueEntity $constraint)
public function testValidateUniquenessWithIgnoreNullDisableOnSecondField(UniqueEntity $constraint)
{
$entity1 = new DoubleNameEntity(1, 'Foo', null);
$entity2 = new DoubleNameEntity(2, 'Foo', null);
Expand Down Expand Up @@ -304,6 +305,7 @@ public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgno

/**
* @dataProvider provideConstraintsWithIgnoreNullEnabled
* @dataProvider provideConstraintsWithIgnoreNullEnabledOnFirstField
*/
public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored(UniqueEntity $constraint)
{
Expand Down Expand Up @@ -338,6 +340,18 @@ public static function provideConstraintsWithIgnoreNullEnabled(): iterable
yield 'Named arguments' => [new UniqueEntity(message: 'myMessage', fields: ['name', 'name2'], em: 'foo', ignoreNull: true)];
}

public static function provideConstraintsWithIgnoreNullEnabledOnFirstField(): iterable
{
yield 'Doctrine style (name field)' => [new UniqueEntity([
'message' => 'myMessage',
'fields' => ['name', 'name2'],
'em' => self::EM_NAME,
'ignoreNull' => 'name',
])];

yield 'Named arguments (name field)' => [new UniqueEntity(message: 'myMessage', fields: ['name', 'name2'], em: 'foo', ignoreNull: 'name')];
}

public function testValidateUniquenessWithValidCustomErrorPath()
{
$constraint = new UniqueEntity([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class UniqueEntity extends Constraint
protected static $errorNames = self::ERROR_NAMES;

/**
* @param array|string $fields the combination of fields that must contain unique values or a set of options
* @param array|string $fields The combination of fields that must contain unique values or a set of options
* @param bool|array|string $ignoreNull The combination of fields that ignore null values
*/
public function __construct(
$fields,
Expand All @@ -55,7 +56,7 @@ public function __construct(
string $entityClass = null,
string $repositoryMethod = null,
string $errorPath = null,
bool $ignoreNull = null,
bool|string|array $ignoreNull = null,
VincentLanglet marked this conversation as resolved.
Show resolved Hide resolved
array $groups = null,
$payload = null,
array $options = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function validate(mixed $entity, Constraint $constraint)
$class = $em->getClassMetadata($entity::class);

$criteria = [];
$hasNullValue = false;
$hasIgnorableNullValue = false;

foreach ($fields as $fieldName) {
if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) {
Expand All @@ -96,11 +96,9 @@ public function validate(mixed $entity, Constraint $constraint)

$fieldValue = $class->reflFields[$fieldName]->getValue($entity);

if (null === $fieldValue) {
$hasNullValue = true;
}
if (null === $fieldValue && $this->ignoreNullForField($constraint, $fieldName)) {
$hasIgnorableNullValue = true;

if ($constraint->ignoreNull && null === $fieldValue) {
continue;
}

Expand All @@ -116,7 +114,7 @@ public function validate(mixed $entity, Constraint $constraint)
}

// validation doesn't fail if one of the fields is null and if null values should be ignored
if ($hasNullValue && $constraint->ignoreNull) {
if ($hasIgnorableNullValue) {
return;
}

Expand Down Expand Up @@ -195,6 +193,15 @@ public function validate(mixed $entity, Constraint $constraint)
->addViolation();
}

private function ignoreNullForField(UniqueEntity $constraint, string $fieldName): bool
{
if (\is_bool($constraint->ignoreNull)) {
return $constraint->ignoreNull;
VincentLanglet marked this conversation as resolved.
Show resolved Hide resolved
}

return \in_array($fieldName, (array) $constraint->ignoreNull, true);
}

private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, mixed $value): string
{
if (!\is_object($value) || $value instanceof \DateTimeInterface) {
Expand Down