-
-
Notifications
You must be signed in to change notification settings - Fork 918
Manage embedded fields for API Platform Filter #984
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
Manage embedded fields for API Platform Filter #984
Conversation
@@ -116,7 +116,7 @@ protected function isPropertyEnabled(string $property): bool | |||
*/ | |||
protected function isPropertyMapped(string $property, string $resourceClass, bool $allowAssociation = false): bool | |||
{ | |||
if ($this->isPropertyNested($property)) { | |||
if ($this->isPropertyNested($property) && !$this->isPropertyNestedEmbedded($property, $resourceClass)) { |
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.
Is this condition valid everytime? I mean, will it break current implementations if we put this inside isPropertyNested
so that other filters also benefits from this? Maybe it's just the boolean filter that's impacted, but I doubt that.
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.
Yes it will breaks 👍
If we put the code in isPropertyNested, it disturbs for differents reasons:
- 1/ it means that a embed field is not considered as a nested field. In fact it is by definition :-)
- 2/ the function extractProperties in AbstractFilter use isPropertyNested and for an Embed field, it has to be needsFixing to true. Otherwise, it fails for the request.
This code is in AbstractFilter, so any Filter will benefit the code.
Normally the only thing to change for each filter is isBooleanField, isDateField, isNumericField...
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.
Okay got it. I'd add a method that ends up doing $this->isPropertyNested($property) && !$this->isPropertyNestedEmbedded($property, $resourceClass)
though. Feel free to add tests.
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.
I do not agree that an Embedded
field should be considered a "nested property". isNestedProperty
is to help us find out when we need to join.
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.
Agreed ofc. Smth like isPropertyNestedFilterable
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.
@teohhanhui , you're right for that. So We confirm that a nested property concerns only join ?
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.
@teohhanhui @soyuka , what about extractProperties method in the class AbstracFilter ? The property $needsFixing to true. Otherwise the embed field embed_b.field is transformed to embed_b_field and generates an error in the request
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.
Yes, that's the approach that I'd take.
Embedded fields are inlined to the field mapping, apparently (see https://github.com/doctrine/doctrine2/blob/v2.5.6/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php#L3230-L3264), so there should be no further changes necessary.
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.
Yes, we also need to fix extractProperties
. $needsFixing = true
for embedded fields. 😄
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.
So you may still find it valuable to extract the logic into, say, isPropertyEmbedded
.
To explain a little bit:
|
Is this issue limited to the |
@soyuka , for the moment yes. I'm waiting your feedback before to apply it in any filter and write STRONG tests about this |
572a083
to
08f0acb
Compare
@teohhanhui @soyuka , second commit for discussion (apply the code in all filters). No unit and functional tests are developped for the moment. But existing tests success |
Better, I'd need @teohhanhui to confirm but I think that we break backward changes by adding @jordscream would be nice to have a functional test involving those embedded properties. For unit tests, coverage looks fine not sure you'd be able to add much. |
@soyuka , no problem for functionnal test, writing in progress |
In unit test, I might be wrong but I wanted to make sure that the filter description worked ok. |
@soyuka @teohhanhui Why we call dropSchema to execute an insert just after ? |
Doing this there ensures that the next scenario has a clean database |
@soyuka My apologize.... |
08f0acb
to
2d95b70
Compare
@soyuka @teohhanhui I had functionnal tests. Tell me if it is enough. The most important to me is to test of at least on filter a model like this entity.relation.embed.field. |
@soyuka @teohhanhui any news ? :-) |
@@ -394,7 +393,7 @@ Feature: Date filter on collections | |||
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" | |||
And the JSON should be equal to: | |||
""" | |||
{ | |||
{ |
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.
Indentation looks wrong here but it's maybe github failing
Then the response status code should be 200 | ||
And the response should be in JSON | ||
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" | ||
And the JSON should be equal to: |
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.
Would it be possible to use a schema here instead? Or you can play with JSON node should equal
. It's a pain to maintain when we add more filters.
features/main/crud.feature
Outdated
} | ||
} |
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.
Same indentation looks weird here from github POV. This is the maintain mess I'm talking about above :p.
Shit failed the review thing \o/. Anyway great job there! However, this has a BC break and should target |
@soyuka @teohhanhui , thanks for your feedback. I gonna check indentation and I will rebase to master. Just 2 questions:
Thanks API platform Team ! |
Because it alters methods that custom filters might use by extending our AbstractFilter. For example, I use it with
which one? |
@soyuka Ok , thanks for the explanation This comment: |
Oh yes, just like you did the other tests, you're using When I speak about
|
2d95b70
to
9b4ef0f
Compare
@soyuka , not sure of my modification according your comment but I did it in boolean_filter.feature. Can you tell me if it is ok for you before I modify my other functionnal test ? thanks |
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.
The first test is correct, the other ones should match a schema instead of comparing the full json. Nice work!
Then the response status code should be 200 | ||
And the response should be in JSON | ||
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" | ||
And the JSON should be equal to: |
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.
And the JSON should be valid according to this schema:
Then the response status code should be 200 | ||
And the response should be in JSON | ||
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" | ||
And the JSON should be equal to: |
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.
And the JSON should be valid according to this schema:
@soyuka
So I need to replace all my the JSON should be equal to by And the JSON should be valid according to this schema ? |
Oh sorry about this, rephrasing : use schema instead of full json equality for the sake of maintainability. Really sorry that you misunderstood this I was just doing too many things at once yesterday and speaking 3 languages at the same time makes it hard to write comprehensible things sometimes :|. |
7f5a2d7
to
4090a85
Compare
@soyuka , I revert my change about functional test to not alter the existing functional test |
tests/Fixtures/app/config/config.yml
Outdated
tags: [ { name: 'api_platform.filter', id: 'my_dummy.search' } ] | ||
|
||
# Tests if the id default to the service name, do not add id attributes here | ||
my_dummy.order: | ||
parent: 'api_platform.doctrine.orm.order_filter' | ||
arguments: [ { 'id': ~, 'name': 'desc', 'relatedDummy.symfony': ~ } ] | ||
tags: [ { name: 'api_platform.filter' } ] | ||
arguments: [ { 'id': ~, 'name': 'desc', 'relatedDummy.name': ~, 'embeddedDummy.dummyName': 'desc' } ] |
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.
you removed relatedDummy.symfony
order, I assume this is wanted? As long as tests pass it's ok to me 👍
php-cs-fixer failed, you can check the diff on travis |
462ee9d
to
40cf084
Compare
@soyuka done (normally) |
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.
Fix my comments and phpstan should be happy!
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); |
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.
Your sprintf()
has too many args.
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); |
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.
Your sprintf()
has too many args.
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); |
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.
Your sprintf()
has too many args.
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); |
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.
Your sprintf()
has too many args.
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); |
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.
Your sprintf()
has too many args.
02370a1
to
61ec985
Compare
@soyuka travis/appveyor failed is really linked to what i did ? |
@jordscream yeah you added an {
"@id": "/related_dummies/1",
"@type": "https://schema.org/Product",
"id": 1,
"name": "Hello",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": "/third_levels/1",
"relatedToDummyFriend": [],
"dummyBoolean": null,
"age": null
}, {
"@id": "\/related_dummies\/1",
"@type": "https:\/\/schema.org\/Product",
"id": 1,
"name": "Hello",
"symfony": "symfony",
"dummyDate": null,
"thirdLevel": "\/third_levels\/1",
"relatedToDummyFriend": [],
"dummyBoolean": null,
"embeddedDummy": [],
"age": null
}, |
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED); |
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.
will have a two second argument
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED); |
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.
will have a two second argument
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED); |
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.
will have a two second argument
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED); |
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.
will have a two second argument
if (__CLASS__ !== get_class($this)) { | ||
$r = new \ReflectionMethod($this, __FUNCTION__); | ||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) { | ||
@trigger_error(sprintf('Method %s() will have a two `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), E_USER_DEPRECATED); |
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.
will have a two second argument
<?php | ||
|
||
declare(strict_types=1); | ||
/* |
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.
There should be a blank line here.
@@ -0,0 +1,144 @@ | |||
<?php | |||
|
|||
declare(strict_types=1); |
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.
There should be a blank line here.
Just FYI: Pointed a project I worked on for over 4 months to this pull request and everything (including the filters on embedded entities) worked like a charm without any error at all! |
@jordscream do you think you can handle the last few comments? |
@dunglas , yes will do this week :-) |
b7d48bf
to
c29a322
Compare
@jordscream just so you know (and you don't force push 😛) I fixed the last comments and rebased against master :). |
c29a322
to
5dc1b08
Compare
Thanks @jordscream and @soyuka! |
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.
Arf too late!
*/ | ||
public function thereIsDummyObjectsWithEmbeddedDummyBoolean($nb, $bool) | ||
{ | ||
if (in_array($bool, ['true', '1', 1], true)) { |
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 about filter_var($bool, FILTER_VALIDATE_BOOLEAN)
?
if (null === $bool = filter_var($bool, FILTER_VALIDATE_BOOLEAN) {
throw new ...;
}
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 wasn't part of this PR or was it? I think there was a reason we didn't use that. I'll look into this.
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.
It was and I don't see any reason 😛
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.
Okay so did some digging. This actually comes from #1010 and I used the same code as we already use in the BooleanFilter
for consistency.
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.
"on" / "off" support in boolean filter was removed. That's where this modification is comming from IIRC
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.
Note that I'm sure we talked about FILTER_VALIDATE_BOOLEAN
when working on the boolean filter, IIRC it was with @teohhanhui.
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.
Hmm, I don't see what can block here. We're not talking about a filter but a setter. It's just a step definition with an argument which is converted to a pure boolean in all cases! So why couldn't we use FILTER_VALIDATE_BOOLEAN
?🤔
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, can add it to my todolist.
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.
Can we all stop using filter_var
, please? 😞 Burn it with fire!
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.
Maybe with some explanations! 😛
@@ -331,7 +331,7 @@ Feature: Order filter on collections | |||
"properties": { | |||
"@id": { | |||
"type": "string", | |||
"pattern": "^/dummies/2$" | |||
"pattern": "^/dummies/2" |
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.
Why?
@@ -92,7 +92,7 @@ Feature: Create-Retrieve-Update-Delete | |||
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" | |||
And the JSON should be equal to: | |||
""" | |||
{ | |||
{ |
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.
Indentation!
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.
Thanks, rebasing behat features is a mess. Fixed in #1137
…d-filter Manage embedded fields for API Platform Filter
This PR is to aliment a discussion about how to manage embedded fields in API Platform Filter
This PR works form Boolean Filter Only. It works recursively Entity->Embed Fields -> Embed Fields..
No Tests is developped for the moment