Skip to content

Commit c7317cd

Browse files
committed
Merge pull request #160 from MLKiiwy/attribute-loader-groups-bug
attribute-loader-groups-bug : fix : normalization by groups; add : TF for the bug
2 parents a775f10 + 3aa6cec commit c7317cd

File tree

6 files changed

+369
-17
lines changed

6 files changed

+369
-17
lines changed

Mapping/ClassMetadata.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ public function addAttribute(AttributeMetadata $attributeMetadata)
127127
$this->attributes[$attributeMetadata->getName()] = $attributeMetadata;
128128
}
129129

130+
/**
131+
* @param AttributeMetadata $attributeMetadata
132+
*/
133+
public function removeAttribute(AttributeMetadata $attributeMetadata)
134+
{
135+
if (isset($this->attributes[$attributeMetadata->getName()])) {
136+
unset($this->attributes[$attributeMetadata->getName()]);
137+
}
138+
}
139+
130140
/**
131141
* {@inheritdoc}
132142
*/

Mapping/Loader/AttributesLoader.php

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,30 +132,36 @@ public function loadClassMetadata(
132132

133133
$serializerClassMetadata = $this->serializerClassMetadataFactory ? $this->serializerClassMetadataFactory->getMetadataFor($classMetadata->getName()) : null;
134134

135-
// Use Serializer metadata if applicable
136135
if ($serializerClassMetadata) {
136+
$normalizationAttributes = [];
137137
foreach ($serializerClassMetadata->getAttributesMetadata() as $normalizationAttribute) {
138-
$name = $normalizationAttribute->getName();
139-
if (null !== $normalizationGroups) {
140-
$attribute = $this->getOrCreateAttribute(
141-
$classMetadata,
142-
$name,
143-
$normalizationGroups,
144-
$denormalizationGroups
145-
);
146-
$attribute->setReadable(count(array_intersect($normalizationAttribute->getGroups(), $normalizationGroups)) > 0);
138+
$normalizationAttributes[$normalizationAttribute->getName()] = $normalizationAttribute;
139+
}
140+
141+
foreach ($classMetadata->getAttributes() as $attribute) {
142+
if ($attribute->isIdentifier()) {
143+
continue;
147144
}
145+
$attributeGroups = isset($normalizationAttributes[$attribute->getName()]) ? $normalizationAttributes[$attribute->getName()]->getGroups() : array();
146+
$readable = true;
147+
$writable = true;
148148

149+
if (null !== $normalizationGroups) {
150+
$readable = count(array_intersect($attributeGroups, $normalizationGroups)) > 0;
151+
}
149152
if (null !== $denormalizationGroups) {
150-
$attribute = $this->getOrCreateAttribute(
151-
$classMetadata,
152-
$name,
153-
$normalizationGroups,
154-
$denormalizationGroups
155-
);
156-
$attribute->setWritable(count(array_intersect($normalizationAttribute->getGroups(), $denormalizationGroups)) > 0);
153+
$writable = count(array_intersect($attributeGroups, $denormalizationGroups)) > 0;
154+
}
155+
156+
if (!$readable && !$writable) {
157+
$classMetadata->removeAttribute($attribute);
158+
} else {
159+
$attribute->setReadable($readable);
160+
$attribute->setWritable($writable);
157161
}
158162
}
163+
} else {
164+
throw new \Exception('Cannot apply normalization settings, serializer class metadata factory not found');
159165
}
160166

161167
return true;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the DunglasApiBundle package.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Dunglas\ApiBundle\Tests\Behat\TestBundle\Entity;
13+
14+
use Doctrine\ORM\Mapping as ORM;
15+
use Dunglas\ApiBundle\Annotation\Iri;
16+
use Symfony\Component\Serializer\Annotation\Groups;
17+
use Symfony\Component\Validator\Constraints as Assert;
18+
19+
/**
20+
* Custom normalized dummy.
21+
*
22+
* @author Mikaël Labrut <[email protected]>
23+
*
24+
* @ORM\Entity
25+
*/
26+
class CustomNormalizedDummy
27+
{
28+
/**
29+
* @var int The id.
30+
*
31+
* @ORM\Column(type="integer")
32+
* @ORM\Id
33+
* @ORM\GeneratedValue(strategy="AUTO")
34+
*/
35+
private $id;
36+
/**
37+
* @var string The dummy name.
38+
*
39+
* @ORM\Column
40+
* @Assert\NotBlank
41+
* @Iri("http://schema.org/name")
42+
* @Groups({"input", "output"})
43+
*/
44+
private $name;
45+
/**
46+
* @var string The dummy name alias.
47+
*
48+
* @ORM\Column(nullable=true)
49+
* @Iri("https://schema.org/alternateName")
50+
* @Groups({"input", "output"})
51+
*/
52+
private $alias;
53+
54+
/**
55+
* @return int
56+
*/
57+
public function getId()
58+
{
59+
return $this->id;
60+
}
61+
62+
/**
63+
* @param string $name
64+
*/
65+
public function setName($name)
66+
{
67+
$this->name = $name;
68+
}
69+
70+
/**
71+
* @return string
72+
*/
73+
public function getName()
74+
{
75+
return $this->name;
76+
}
77+
78+
/**
79+
* @return string
80+
*/
81+
public function getAlias()
82+
{
83+
return $this->alias;
84+
}
85+
86+
/**n
87+
* @param string $alias
88+
*/
89+
public function setAlias($alias)
90+
{
91+
$this->alias = $alias;
92+
}
93+
94+
/**
95+
* @return string
96+
*/
97+
public function getPersonalizedAlias()
98+
{
99+
return $this->alias;
100+
}
101+
102+
/**
103+
* @param string $value
104+
*/
105+
public function setPersonalizedAlias($value)
106+
{
107+
$this->alias = $value;
108+
}
109+
}

features/custom_normalized.feature

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
Feature: Using custom normalized entity
2+
In order to use an hypermedia API
3+
As a client software developer
4+
I need to be able to filter correctly attribute of my entities
5+
6+
@createSchema
7+
Scenario: Create a resource
8+
When I send a "POST" request to "/custom_normalized_dummies" with body:
9+
"""
10+
{
11+
"name": "My Dummy",
12+
"alias": "My alias"
13+
}
14+
"""
15+
Then the response status code should be 201
16+
And the response should be in JSON
17+
And the header "Content-Type" should be equal to "application/ld+json"
18+
And the JSON should be equal to:
19+
"""
20+
{
21+
"@context": "/contexts/CustomNormalizedDummy",
22+
"@id": "/custom_normalized_dummies/1",
23+
"@type": "CustomNormalizedDummy",
24+
"name": "My Dummy",
25+
"alias": "My alias"
26+
}
27+
"""
28+
29+
Scenario: Get a resource
30+
When I send a "GET" request to "/custom_normalized_dummies/1"
31+
Then the response status code should be 200
32+
And the response should be in JSON
33+
And the header "Content-Type" should be equal to "application/ld+json"
34+
And the JSON should be equal to:
35+
"""
36+
{
37+
"@context": "/contexts/CustomNormalizedDummy",
38+
"@id": "/custom_normalized_dummies/1",
39+
"@type": "CustomNormalizedDummy",
40+
"name": "My Dummy",
41+
"alias": "My alias"
42+
}
43+
"""
44+
45+
Scenario: Get a collection
46+
When I send a "GET" request to "/custom_normalized_dummies"
47+
Then the response status code should be 200
48+
And the response should be in JSON
49+
And the header "Content-Type" should be equal to "application/ld+json"
50+
And the JSON should be equal to:
51+
"""
52+
{
53+
"@context": "/contexts/CustomNormalizedDummy",
54+
"@id": "/custom_normalized_dummies",
55+
"@type": "hydra:PagedCollection",
56+
"hydra:totalItems": 1,
57+
"hydra:itemsPerPage": 3,
58+
"hydra:firstPage": "/custom_normalized_dummies",
59+
"hydra:lastPage": "/custom_normalized_dummies",
60+
"hydra:member": [
61+
{
62+
"@id": "/custom_normalized_dummies/1",
63+
"@type": "CustomNormalizedDummy",
64+
"name": "My Dummy",
65+
"alias": "My alias"
66+
}
67+
]
68+
}
69+
"""
70+
71+
Scenario: Update a resource
72+
When I send a "PUT" request to "/custom_normalized_dummies/1" with body:
73+
"""
74+
{
75+
"name": "My Dummy modified"
76+
}
77+
"""
78+
Then the response status code should be 200
79+
And the response should be in JSON
80+
And the header "Content-Type" should be equal to "application/ld+json"
81+
And the JSON should be equal to:
82+
"""
83+
{
84+
"@context": "/contexts/CustomNormalizedDummy",
85+
"@id": "/custom_normalized_dummies/1",
86+
"@type": "CustomNormalizedDummy",
87+
"name": "My Dummy modified",
88+
"alias": "My alias"
89+
}
90+
"""
91+
92+
Scenario: Vocab is correctly generated
93+
When I send a "GET" request to "/vocab"
94+
Then the response status code should be 200
95+
And the response should be in JSON
96+
And the hydra class "CustomNormalizedDummy" exist
97+
And 3 operations are availables for hydra class "CustomNormalizedDummy"
98+
And 2 properties are availables for hydra class "CustomNormalizedDummy"
99+
And "name" property is readable for hydra class "CustomNormalizedDummy"
100+
And "name" property is writable for hydra class "CustomNormalizedDummy"
101+
And "alias" property is readable for hydra class "CustomNormalizedDummy"
102+
And "alias" property is writable for hydra class "CustomNormalizedDummy"
103+
104+
@dropSchema
105+
Scenario: Delete a resource
106+
When I send a "DELETE" request to "/custom_normalized_dummies/1"
107+
Then the response status code should be 204
108+
And the response should be empty

features/fixtures/TestApp/config/config.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,15 @@ services:
140140
parent: "api.resource"
141141
arguments: [ "Dunglas\ApiBundle\Tests\Behat\TestBundle\Entity\CustomWritableIdentifierDummy" ]
142142
tags: [ { name: "api.resource" } ]
143+
144+
custom_normalized_resource:
145+
parent: "api.resource"
146+
arguments: [ "Dunglas\ApiBundle\Tests\Behat\TestBundle\Entity\CustomNormalizedDummy" ]
147+
calls:
148+
- method: "initNormalizationContext"
149+
arguments:
150+
- { groups: [ "output" ] }
151+
- method: "initDenormalizationContext"
152+
arguments:
153+
- { groups: [ "input" ] }
154+
tags: [ { name: "api.resource" } ]

0 commit comments

Comments
 (0)