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

[Images][Taxon] Validation for unique code during creation/edition #6311

Merged
merged 3 commits into from
Oct 12, 2016

Conversation

tuka217
Copy link
Contributor

@tuka217 tuka217 commented Oct 5, 2016

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

public function validate($images, Constraint $constraint)
{
/** @var ImageInterface[] $imagesFromDatabase */
$imagesFromDatabase = $this->resourceRepository->findAll();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting all of them will be sloooooooooow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can filter them by owner.


/** @var ImageInterface[] $images */
foreach ($images as $key => $image) {
foreach ($imagesFromDatabase as $imageFromDatabase) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$images -> $newImages
$imagesFromDatabase -> $persistedImages
?

foreach ($images as $key => $image) {
foreach ($imagesFromDatabase as $imageFromDatabase) {
if ($imageFromDatabase->getOwner() === $image->getOwner() && $imageFromDatabase->getCode() === $image->getCode()) {
$this->context->addViolationAt(sprintf('[%d].code', $key), $constraint->message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addViolationAt is deprecated, we should use buildViolation($message)->atPath($path)->addViolation().

/**
* {@inheritdoc}
*/
public function validate($images, Constraint $constraint)
Copy link
Contributor

@pamil pamil Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw. isn't something like that enough? Do we even need to use the repository?

$imagesCodesUsage = array_count_values(array_map(function (ImageInterface $image) { 
  return $image->getCode(); 
}, $images));

$duplicatedImages = array_filter($images, function (ImageInterface $image) use ($imagesCodesUsage) {
  return $imagesCodesUsage[$image->getCode()] > 1;
});

foreach (array_keys($duplicatedImages) as $key) {
  $this->context->addViolationAt(sprintf('[%d].code', $key), $constraint->message);
}

Copy link
Contributor Author

@tuka217 tuka217 Oct 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But first step return an warning which stopped a work of validator. And second one too.

ImageInterface $secondImage,
ImageInterface $thirdImage,
ImageAwareInterface $owner,
ImageUniqueCode $constraint,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use new to create it inside spec, mocking constraint gives us no profit.

Copy link
Contributor Author

@tuka217 tuka217 Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of profit gives creating new constraint? It has only an message.

Copy link
Contributor

@pamil pamil Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An message which is null while mocking it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But message can be any string. In fact it should be argument with type string

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, my bad, mocking does indeed preserve the properties value (but does not run the constructor).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And ok, it should be create by new. But not because that the message is null, but more because the kind of instance of ImageUnigeCode does not metter.

$secondImage->getOwner()->willReturn($owner);

$firstImage->getCode()->willReturn('car');
$secondImage->getCode()->willReturn('car');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is second image even used in this spec?

ImageInterface $thirdImage,
ImageAwareInterface $owner,
ImageUniqueCode $constraint,
RepositoryInterface $imageRepository
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be the first argument as it's injected to the constructor.

@pjedrzejewski
Copy link
Member

@tuka217 Is it still a WIP?

@tuka217
Copy link
Contributor Author

tuka217 commented Oct 6, 2016

Yups, 5 minutes.

@tuka217 tuka217 changed the title [WIP][Images][Taxon] Validation for unique code during creation/edition [Images][Taxon] Validation for unique code during creation/edition Oct 6, 2016
$this->context->addViolationAt(sprintf('[%d].code', $key), $constraint->message);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't have to check the owner, as we always receive collection of images of a single "owner". The following implementation would be simpler:

$imagesCodes = [];

/** @var ImageInterface[] $images */
foreach ($images as $key => $image) {
    if (array_key_exists($imagesCodes[$image->getCode()])) {
        $this->context->addViolationAt(sprintf('[%d].code', $key), $constraint->message);
    }
    $imagesCodes[$image->getCode()] = $image;
}

Copy link
Contributor Author

@tuka217 tuka217 Oct 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't add the violation to a first element with duplicate code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the following?

$imagesCodes = [];

/** @var ImageInterface[] $images */
foreach ($images as $key => $image) {
    if (!array_key_exists($imagesCodes[$image->getCode()])) {
        $imagesCodes[$image->getCode()] = $key;
        continue;
    }

    $this->context->addViolationAt(sprintf('[%d].code', $key), $constraint->message);
    if (false !== $imagesCodes[$image->getCode()]) {
        $this->context->addViolationAt(sprintf('[%d].code', $imagesCodes[$image->getCode()]), $constraint->message);
        $imagesCodes[$image->getCode()] = false;
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be faster than filtering?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filtering is like for loop, so for each image you iterate over all images and the complexity is exponential O(n^2), so for 10 images we have 100 iterations (10*10). With the above code the complexity is linear O(n), so we would have only 10 iterations.

Copy link
Contributor Author

@tuka217 tuka217 Oct 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solution with "filter" has only two loops, so if you want the more readable solution I would chose this. But, please decide which one will be the best, because it the 5th change for this simple class which (btw) will be evolved in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering only collection of images it should never be so big that the complexity will influence the performance in any noticeable way, so I'm for @pamil's solution as it's clean and easy to understand. When there will be use cases for bigger collections and performance issues we can provide less complex solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tuka217 the current solution is readable but doesn't assign validation error to the first image with duplicated code.

Copy link
Contributor Author

@tuka217 tuka217 Oct 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's assign, but I changed it do your solution and please, leave it in this form. It's not so hard to understand what this method do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tuka217 I hate this new Github interface! My comment was to the old implementation because I didn't see the updated code... 😕

@pjedrzejewski pjedrzejewski merged commit 871243c into Sylius:master Oct 12, 2016
@pjedrzejewski
Copy link
Member

Thank you Ania, good job! ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants