-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
DQL Query: process ArrayCollection values to ease development #590
DQL Query: process ArrayCollection values to ease development #590
Conversation
Hello, thank you for positing this Pull Request. I have automatically opened an issue on our Jira Bug Tracker for you with the details of this Pull-Request. See the Link: |
$values = array(); | ||
|
||
foreach ($value as $valueEntry) { | ||
$singleValue = $this->_em->getUnitOfWork()->getSingleIdentifierValue($valueEntry); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if it does not have a single identifier ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stof It's tested on the line below and an Exception is thrown, this is the same behavior for the object case (see line 283 of the same file)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is that entities using a composite identifier would throw an exception here because getSingleIdentifierValue
is not supported for them (it cannot be a single one)
@stof I updated the code according to the comments you made. If you think the feature is worth being merged, I'll write tests associated to it. |
@stof Sorry for the misunderstanding. You're right, an exception ( $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value); When binding object parameters, developers will know (and already know with the current code) that only object having a single identifier can be used thanks to this raised exception. I added by the way more control on the collection entities and added tests as well for this enhancement. |
@@ -284,6 +285,26 @@ public function processParameterValue($value) | |||
} | |||
} | |||
|
|||
if ($value instanceof Collection) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this might be easier to read :
if ($value instanceof Collection) {
$values = array();
foreach ($value as $valueEntry) {
if ( ! is_object($valueEntry) || ! $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($valueEntry))) {
return $value;
}
$singleValue = $this->_em->getUnitOfWork()->getSingleIdentifierValue($valueEntry);
if ($singleValue === null) {
throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
}
$values[] = $singleValue;
}
return $values;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please move the block inside if()
into a method processCollectionParameterValue()
? This would avoid the method to become that long.
After that its good to merge from my POV.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@beberlei This is now done!
@FabioBatSilva Done (small condition refactoring) |
Rebased commits (no more conflicts with current master branch) |
|
||
foreach ($value as $valueEntry) { | ||
if ( ! is_object($valueEntry) || ! $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($valueEntry))) { | ||
return $value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure? I think it must be:
foreach ($value as $valueEntry) {
if ( ! is_object($valueEntry) || ! $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($valueEntry))) {
$values[] = $value;
continue;
@Baachi Hum, you're right, that's better adding the value itself in case the value in the collection parameter is not an object. That's fixed now. |
|
||
foreach ($value as $valueEntry) { | ||
if ( ! is_object($valueEntry) || ! $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($valueEntry))) { | ||
$values[] = $value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong,
You should add a continue;
here..
Otherwise you are adding the value and then trying to extract the identifier which will probably fail ..
Please add a test for this case as well..
Is compatible with #522 ? |
@@ -288,10 +289,45 @@ public function processParameterValue($value) | |||
return $value->name; | |||
} | |||
|
|||
if ($value instanceof Collection) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not always passing around objects in collections, and collections musn't have only objects. I am not sure if this is a helpful addition for users and as you show in the test, the API actually requires quite some code to instantiate and add elements to the collection, whereas i could just do:
$query->setParameter('foo', array_map(function ($object) {
return $object->getId();
}, array($a, $b));
This is much simpler and generic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@beberlei The case where objects and values can be mixed in the same collection is now handled and unit-tested. And while it requires some code if the collection is built manually, it's not the case when the collection has been generated by Doctrine (like when retrieving a one to many value on an object) and can later directly be used as a query parameter with the solution I propose. What do you think?
@FabioBatSilva Aw, my mistake, sorry. This is now fixed, and unit tested. |
Just checked the code again, please remove the processcollectionParamterValue() and just add a check for instanceof Collection in the line |
@beberlei This is indeed a more clever way to implement it indeed and this is now done with the last commit. I first convert the collection to an array as the Anyway, this solution works fine! Thanks for your help! |
Hello, Do you have any news on the merge of this PR? |
@beberlei I rebased my commits and resolved conflicts so that it can be merged to master. |
@michaelperrin Can you squash your PR into 1 commit? |
@asm89 Done! |
Waiting for Travis to respond. It failed temporarily and manually triggered a new build. |
…-value DQL Query: process ArrayCollection values to ease development
Thanks @guilhermeblanco ! |
I added some code to ease "where in" parameter binding.
As you know, when a where condition is added, the object itself can be passed as a parameter and it's id is automatically retrieved:
Where
$category
is an object.But it doesn't work for collections:
Where categories is an
ArrayCollection
object (retrieved from a many to one relation for instance).This doesn't work in the current version of Doctrine, and my PR solves that.
Note that I didn't add any unit test for this feature. Can you explain me where I should add the test?This enhancement is now unit-tested in this same PR.
So far, the only solution was to do the following, which is pretty borring: