Open kgilden opened 5 years ago
Hm I see, thanks for the detailed report and managing to point the exact issue.
The original idea behind the deep cloning was to achieve real immutability. There was various ideas of using the FixtureSet
relying on it being an immutable "tree". It was also a wish of mine to push for a lot of small classes and heavily relying on testing, immutability and final classes. alice 2.x was design-wise much simpler, but very flawed and impossible to debug. 3.x is certainly way more complex in terms of design/architecture (for a similar feature-set), but at least it more testable, extendable and deterministic.
There's however been a lot of changes and I've not managed to free as much time neither as much need to really take advantage of the whole design. So I think regarding the immutability of the values there is some stuff to reconsider. I wouldn't go as far as removing the deep cloning everywhere as I'm certain it's being used in some places, but at the very least I don't think non definition values, such as injected & resolved objects should be deep cloned. And this should I think solve your issue
Right, thanks!
So if I understood correctly, a potential fix would be checking whether the passed value is some instance of the alice package or a foreign object? Basically, the __clone
method of each alice class that can be passed as value should recursively clone their wrapped alice class, but not a foreign object?
I did, however manage to find a workaround by going with a custom provider and enforcing uniqueness there by keeping track of which elements have been used already. Adding it here for reference, if someone else runs into the same problem.
use Faker\Generator;
final class UniqueElementProvider
{
private $generator;
private $usedElements = [];
public function __construct(Generator $generator)
{
$this->generator = $generator;
}
public function uniqueElements(array $elements, int $count = 1): array
{
$this->usedElements = [];
$unusedElements = array_udiff($elements, $this->usedElements, function ($a, $b) {
return spl_object_hash($a) <=> spl_object_hash($b);
});
$chosenElements = $this->generator->randomElements($unusedElements, $count);
$this->usedElements = array_merge($this->usedElements, $chosenElements);
return $chosenElements;
}
}
So if I understood correctly, a potential fix would be checking whether the passed value is some instance of the alice package or a foreign object?
Yes
Basically, the __clone method of each alice class that can be passed as value should recursively clone their wrapped alice class, but not a foreign object?
I would be a bit simpler and just look up the ValueInterface
children classes and only do the deep clone if not an instance of ValueInterface
I can confirm the issue is still there.
fzaninotto/faker v1.9.1
hautelook/alice-bundle v2.7.2
myclabs/deep-copy 1.9.5
nelmio/alice v3.6.0
theofidry/alice-data-fixtures v1.1.2
I can set up a reproduction project if anyone needs one, feel free to ask :+1:
I just ran into the same issue. Fix by @kgilden worked for me with slight modification.
Out of 500 fixtures of doctrine entities, pick 5 random Elements as a relation. Because the relationship is unique, no single one of the 500 fixtures may be picked again.
I tried this:
App\Entity\A:
a{1..100}:
b (unique): '5x @b*'
Not clearing the usedElements
in @kgilden example was the solution. Removing this line:
$this->usedElements = [];
The result is:
App\Entity\A:
a{1..100}:
b: '<uniqueElements(@b{1..500}, 5)>'
I would be interested in creating a PR for this but obviously the use-case might differ. Is there an update on deep_clone? Should we go with an extra parameter to switch between locally and globally unique?
Ran into a similar issue a scratched my head for a while before figuring what caused this behaviour. 3 years later have you figured how to manage this bug for a next release ?
@florianajir there is several possible solutions, one proposed above via a custom provider can be implemented on your own as a workaround.
A more definite and in-core solution is also possible, but no one did a PR for it yet.
Hi, thanks for all the work on this awesome package!
Similar to https://github.com/nelmio/alice/issues/722 and https://github.com/nelmio/alice/issues/888, I'm getting exceptions about new entities being found via relationships.
Suppose the following set-up.
I get the following exception from Doctrine.
I believe the issue is caused by
UniqueValue
cloning its value. A copy is made of an existing entity and now Doctrine sees that there is a new entity to be persisted rather than using the existing one, which was the intended purpose.I can reproduce this with the following failing test case added to
LoaderIntegrationTest
.May I ask why is deep copying of values required (added in this PR)? Could we get by with not deep copying? I can confirm that removing these two
deep_clone
uses fromUniqueValue
eliminates the problem.My project is using the latest relevant dependencies.
Thanks, Kristen