16
16
use ApiPlatform \HttpCache \PurgerInterface ;
17
17
use ApiPlatform \Metadata \Exception \InvalidArgumentException ;
18
18
use ApiPlatform \Metadata \Exception \OperationNotFoundException ;
19
- use ApiPlatform \Metadata \Exception \RuntimeException ;
20
19
use ApiPlatform \Metadata \GetCollection ;
21
20
use ApiPlatform \Metadata \IriConverterInterface ;
22
21
use ApiPlatform \Metadata \ResourceClassResolverInterface ;
27
26
use Doctrine \ORM \Event \PreUpdateEventArgs ;
28
27
use Doctrine \ORM \Mapping \AssociationMapping ;
29
28
use Doctrine \ORM \PersistentCollection ;
29
+ use Symfony \Component \ObjectMapper \Attribute \Map ;
30
+ use Symfony \Component \ObjectMapper \ObjectMapperInterface ;
30
31
use Symfony \Component \PropertyAccess \PropertyAccess ;
31
32
use Symfony \Component \PropertyAccess \PropertyAccessorInterface ;
32
33
@@ -41,7 +42,11 @@ final class PurgeHttpCacheListener
41
42
private readonly PropertyAccessorInterface $ propertyAccessor ;
42
43
private array $ tags = [];
43
44
44
- public function __construct (private readonly PurgerInterface $ purger , private readonly IriConverterInterface $ iriConverter , private readonly ResourceClassResolverInterface $ resourceClassResolver , ?PropertyAccessorInterface $ propertyAccessor = null )
45
+ public function __construct (private readonly PurgerInterface $ purger ,
46
+ private readonly IriConverterInterface $ iriConverter ,
47
+ private readonly ResourceClassResolverInterface $ resourceClassResolver ,
48
+ ?PropertyAccessorInterface $ propertyAccessor = null ,
49
+ private readonly ?ObjectMapperInterface $ objectMapper = null )
45
50
{
46
51
$ this ->propertyAccessor = $ propertyAccessor ?? PropertyAccess::createPropertyAccessor ();
47
52
}
@@ -110,36 +115,47 @@ public function postFlush(): void
110
115
111
116
private function gatherResourceAndItemTags (object $ entity , bool $ purgeItem ): void
112
117
{
113
- try {
114
- $ iri = $ this ->iriConverter ->getIriFromResource ($ entity , UrlGeneratorInterface::ABS_PATH , new GetCollection ());
115
- $ this ->tags [$ iri ] = $ iri ;
118
+ $ resources = $ this ->getResourcesForEntity ($ entity );
116
119
117
- if ($ purgeItem ) {
118
- $ this ->addTagForItem ($ entity );
120
+ foreach ($ resources as $ resource ) {
121
+ try {
122
+ $ iri = $ this ->iriConverter ->getIriFromResource ($ resource , UrlGeneratorInterface::ABS_PATH , new GetCollection ());
123
+ $ this ->tags [$ iri ] = $ iri ;
124
+
125
+ if ($ purgeItem ) {
126
+ $ this ->addTagForItem ($ entity );
127
+ }
128
+ } catch (OperationNotFoundException |InvalidArgumentException ) {
119
129
}
120
- } catch (OperationNotFoundException |InvalidArgumentException ) {
121
130
}
122
131
}
123
132
124
133
private function gatherRelationTags (EntityManagerInterface $ em , object $ entity ): void
125
134
{
126
135
$ associationMappings = $ em ->getClassMetadata ($ entity ::class)->getAssociationMappings ();
136
+
127
137
/** @var array|AssociationMapping $associationMapping according to the version of doctrine orm */
128
138
foreach ($ associationMappings as $ property => $ associationMapping ) {
129
139
if ($ associationMapping instanceof AssociationMapping && ($ associationMapping ->targetEntity ?? null ) && !$ this ->resourceClassResolver ->isResourceClass ($ associationMapping ->targetEntity )) {
130
140
return ;
131
141
}
142
+ if (!$ this ->propertyAccessor ->isReadable ($ entity , $ property )) {
143
+ return ;
144
+ }
132
145
133
146
if (
134
147
\is_array ($ associationMapping )
135
148
&& \array_key_exists ('targetEntity ' , $ associationMapping )
136
- && !$ this ->resourceClassResolver ->isResourceClass ($ associationMapping ['targetEntity ' ])) {
149
+ && !$ this ->resourceClassResolver ->isResourceClass ($ associationMapping ['targetEntity ' ])
150
+ && (
151
+ !$ this ->objectMapper
152
+ || !(new \ReflectionClass ($ associationMapping ['targetEntity ' ]))->getAttributes (Map::class)
153
+ )
154
+ ) {
137
155
return ;
138
156
}
139
157
140
- if ($ this ->propertyAccessor ->isReadable ($ entity , $ property )) {
141
- $ this ->addTagsFor ($ this ->propertyAccessor ->getValue ($ entity , $ property ));
142
- }
158
+ $ this ->addTagsFor ($ this ->propertyAccessor ->getValue ($ entity , $ property ));
143
159
}
144
160
}
145
161
@@ -166,14 +182,42 @@ private function addTagsFor(mixed $value): void
166
182
167
183
private function addTagForItem (mixed $ value ): void
168
184
{
169
- if (!$ this ->resourceClassResolver ->isResourceClass ($ this ->getObjectClass ($ value ))) {
170
- return ;
185
+ $ resources = $ this ->getResourcesForEntity ($ value );
186
+
187
+ foreach ($ resources as $ resource ) {
188
+ try {
189
+ $ iri = $ this ->iriConverter ->getIriFromResource ($ resource );
190
+ $ this ->tags [$ iri ] = $ iri ;
191
+ } catch (OperationNotFoundException |InvalidArgumentException ) {
192
+ }
171
193
}
194
+ }
172
195
173
- try {
174
- $ iri = $ this ->iriConverter ->getIriFromResource ($ value );
175
- $ this ->tags [$ iri ] = $ iri ;
176
- } catch (RuntimeException |InvalidArgumentException ) {
196
+ private function getResourcesForEntity (object $ entity ): array
197
+ {
198
+ $ resources = [];
199
+
200
+ if (!$ this ->resourceClassResolver ->isResourceClass ($ class = $ this ->getObjectClass ($ entity ))) {
201
+ // is the entity mapped to resource(s)?
202
+ if (!$ this ->objectMapper ) {
203
+ return [];
204
+ }
205
+
206
+ $ mapAttributes = (new \ReflectionClass ($ class ))->getAttributes (Map::class);
207
+
208
+ if (!$ mapAttributes ) {
209
+ return [];
210
+ }
211
+
212
+ // loop over all mappings to fetch all resources mapped to this entity
213
+ $ resources = array_map (
214
+ fn ($ mapAttribute ) => $ this ->objectMapper ->map ($ entity , $ mapAttribute ->newInstance ()->target ),
215
+ $ mapAttributes
216
+ );
217
+ } else {
218
+ $ resources [] = $ entity ;
177
219
}
220
+
221
+ return $ resources ;
178
222
}
179
223
}
0 commit comments