Skip to content

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

Merged
merged 1 commit into from
May 23, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyOffer;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProduct;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProperty;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddableDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddedDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FileConfigDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Node;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Question;
Expand Down Expand Up @@ -170,6 +172,25 @@ public function thereIsDummyPropertyObjects($nb)
$this->manager->flush();
}

/**
* @Given there are :nb embedded dummy objects
*/
public function thereIsEmbeddedDummyObjects($nb)
{
for ($i = 1; $i <= $nb; ++$i) {
$dummy = new EmbeddedDummy();
$dummy->setName('Dummy #'.$i);

$embeddableDummy = new EmbeddableDummy();
$embeddableDummy->setDummyName('Dummy #'.$i);
$dummy->setEmbeddedDummy($embeddableDummy);

$this->manager->persist($dummy);
}

$this->manager->flush();
}

/**
* @Given there is :nb dummy objects with relatedDummy
*/
Expand All @@ -191,6 +212,25 @@ public function thereIsDummyObjectsWithRelatedDummy($nb)
$this->manager->flush();
}

/**
* @Given there is :nb dummy objects with embeddedDummy
*/
public function thereIsDummyObjectsWithEmbeddedDummy($nb)
{
for ($i = 1; $i <= $nb; ++$i) {
$embeddableDummy = new EmbeddableDummy();
$embeddableDummy->setDummyName('EmbeddedDummy #'.$i);

$dummy = new EmbeddedDummy();
$dummy->setName('Dummy #'.$i);
$dummy->setEmbeddedDummy($embeddableDummy);

$this->manager->persist($dummy);
}

$this->manager->flush();
}

/**
* @Given there is :nb dummy objects having each :nbrelated relatedDummies
*/
Expand Down Expand Up @@ -305,6 +345,32 @@ public function thereIsDummyObjectsWithDummyDateAndRelatedDummy($nb)
$this->manager->flush();
}

/**
* @Given there is :nb embedded dummy objects with dummyDate and embeddedDummy
*/
public function thereIsDummyObjectsWithDummyDateAndEmbeddedDummy($nb)
{
for ($i = 1; $i <= $nb; ++$i) {
$date = new \DateTime(sprintf('2015-04-%d', $i), new \DateTimeZone('UTC'));

$embeddableDummy = new EmbeddableDummy();
$embeddableDummy->setDummyName('Embeddable #'.$i);
$embeddableDummy->setDummyDate($date);

$dummy = new EmbeddedDummy();
$dummy->setName('Dummy #'.$i);
$dummy->setEmbeddedDummy($embeddableDummy);
// Last Dummy has a null date
if ($nb !== $i) {
$dummy->setDummyDate($date);
}

$this->manager->persist($dummy);
}

$this->manager->flush();
}

/**
* @Given there is :nb dummy objects with dummyPrice
*/
Expand Down Expand Up @@ -354,6 +420,66 @@ public function thereIsDummyObjectsWithDummyBoolean($nb, $bool)
$this->manager->flush();
}

/**
* @Given there is :nb embedded dummy objects with embeddedDummy.dummyBoolean :bool
*/
public function thereIsDummyObjectsWithEmbeddedDummyBoolean($nb, $bool)
{
if (in_array($bool, ['true', '1', 1], true)) {
Copy link
Member

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 ...;
}

Copy link
Member

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.

Copy link
Member

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 😛

Copy link
Member

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.

Copy link
Contributor

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

Copy link
Member

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.

Copy link
Member

@meyerbaptiste meyerbaptiste May 23, 2017

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?🤔

Copy link
Member

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.

Copy link
Contributor

@teohhanhui teohhanhui May 24, 2017

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!

Copy link
Member

Choose a reason for hiding this comment

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

Maybe with some explanations! 😛

$bool = true;
} elseif (in_array($bool, ['false', '0', 0], true)) {
$bool = false;
} else {
$expected = ['true', 'false', '1', '0'];
throw new InvalidArgumentException(sprintf('Invalid boolean value for "%s" property, expected one of ( "%s" )', $bool, implode('" | "', $expected)));
}

for ($i = 1; $i <= $nb; ++$i) {
$dummy = new EmbeddedDummy();
$dummy->setName('Embedded Dummy #'.$i);
$embeddableDummy = new EmbeddableDummy();
$embeddableDummy->setDummyName('Embedded Dummy #'.$i);
$embeddableDummy->setDummyBoolean($bool);
$dummy->setEmbeddedDummy($embeddableDummy);
$this->manager->persist($dummy);
}

$this->manager->flush();
}

/**
* @Given there is :nb embedded dummy objects with relatedDummy.embeddedDummy.dummyBoolean :bool
*/
public function thereIsDummyObjectsWithRelationEmbeddedDummyBoolean($nb, $bool)
{
if (in_array($bool, ['true', '1', 1], true)) {
$bool = true;
} elseif (in_array($bool, ['false', '0', 0], true)) {
$bool = false;
} else {
$expected = ['true', 'false', '1', '0'];
throw new InvalidArgumentException(sprintf('Invalid boolean value for "%s" property, expected one of ( "%s" )', $bool, implode('" | "', $expected)));
}

for ($i = 1; $i <= $nb; ++$i) {
$dummy = new EmbeddedDummy();
$dummy->setName('Embedded Dummy #'.$i);
$embeddableDummy = new EmbeddableDummy();
$embeddableDummy->setDummyName('Embedded Dummy #'.$i);
$embeddableDummy->setDummyBoolean($bool);

$relationDummy = new RelatedDummy();
$relationDummy->setEmbeddedDummy($embeddableDummy);

$dummy->setRelatedDummy($relationDummy);

$this->manager->persist($relationDummy);
$this->manager->persist($dummy);
}

$this->manager->flush();
}

/**
* @Given there is a RelationEmbedder object
*/
Expand Down
204 changes: 204 additions & 0 deletions features/doctrine/boolean_filter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,210 @@ Feature: Boolean filter on collections
"""
And the JSON node "hydra:totalItems" should be equal to 10

Scenario: Get collection by embeddedDummy.dummyBoolean true
Given there is "15" embedded dummy objects with embeddedDummy.dummyBoolean true
And there is "10" embedded dummy objects with embeddedDummy.dummyBoolean false
When I send a "GET" request to "/embedded_dummies?embeddedDummy.dummyBoolean=true"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/EmbeddedDummy$"},
"@id": {"pattern": "^/embedded_dummies$"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/embedded_dummies/1$"},
{"pattern": "^/embedded_dummies/2$"},
{"pattern": "^/embedded_dummies/3$"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/embedded_dummies\\?embeddedDummy\\.dummyBoolean=true"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""
And the JSON node "hydra:totalItems" should be equal to 15

Scenario: Get collection by embeddedDummy.dummyBoolean true
When I send a "GET" request to "/embedded_dummies?embeddedDummy.dummyBoolean=1"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/EmbeddedDummy$"},
"@id": {"pattern": "^/embedded_dummies$"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/embedded_dummies/1$"},
{"pattern": "^/embedded_dummies/2$"},
{"pattern": "^/embedded_dummies/3$"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/embedded_dummies\\?embeddedDummy\\.dummyBoolean=1"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""
And the JSON node "hydra:totalItems" should be equal to 15

Scenario: Get collection by embeddedDummy.dummyBoolean false
When I send a "GET" request to "/embedded_dummies?embeddedDummy.dummyBoolean=false"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/EmbeddedDummy$"},
"@id": {"pattern": "^/embedded_dummies$"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/embedded_dummies/16$"},
{"pattern": "^/embedded_dummies/17$"},
{"pattern": "^/embedded_dummies/18$"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/embedded_dummies\\?embeddedDummy\\.dummyBoolean=false"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""
And the JSON node "hydra:totalItems" should be equal to 10

Scenario: Get collection by embeddedDummy.dummyBoolean false
When I send a "GET" request to "/embedded_dummies?embeddedDummy.dummyBoolean=0"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/EmbeddedDummy$"},
"@id": {"pattern": "^/embedded_dummies$"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/embedded_dummies/16$"},
{"pattern": "^/embedded_dummies/17$"},
{"pattern": "^/embedded_dummies/18$"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/embedded_dummies\\?embeddedDummy\\.dummyBoolean=0"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""
And the JSON node "hydra:totalItems" should be equal to 10

Scenario: Get collection by association with embed relatedDummy.embeddedDummy.dummyBoolean true
Given there is "15" embedded dummy objects with relatedDummy.embeddedDummy.dummyBoolean true
And there is "10" embedded dummy objects with relatedDummy.embeddedDummy.dummyBoolean false
When I send a "GET" request to "/embedded_dummies?relatedDummy.embeddedDummy.dummyBoolean=true"
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 valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/EmbeddedDummy$"},
"@id": {"pattern": "^/embedded_dummies$"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/embedded_dummies/26$"},
{"pattern": "^/embedded_dummies/27$"},
{"pattern": "^/embedded_dummies/28$"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/embedded_dummies\\?relatedDummy.embeddedDummy\\.dummyBoolean=true"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""
And the JSON node "hydra:totalItems" should be equal to 15

@dropSchema
Scenario: Get collection ordered by a non valid properties
When I send a "GET" request to "/dummies?unknown=0"
Expand Down
Loading