Skip to content

Commit 02c7676

Browse files
committed
Refactor XML factories
1 parent 510801f commit 02c7676

File tree

13 files changed

+198
-262
lines changed

13 files changed

+198
-262
lines changed

src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,8 @@ private function registerLoaders(ContainerBuilder $container, array $bundles)
255255
}
256256

257257
$container->getDefinition('api_platform.metadata.resource.name_collection_factory.annotation')->addArgument($annotationPaths);
258-
259-
$container->getDefinition('api_platform.metadata.extractor.yaml')->replaceArgument(0, $yamlResources);
260-
261-
$container->getDefinition('api_platform.metadata.resource.name_collection_factory.xml')->replaceArgument(0, $xmlResources);
262-
$container->getDefinition('api_platform.metadata.resource.metadata_factory.xml')->replaceArgument(0, $xmlResources);
263-
264-
$container->getDefinition('api_platform.metadata.property.name_collection_factory.xml')->replaceArgument(0, $xmlResources);
265-
$container->getDefinition('api_platform.metadata.property.metadata_factory.xml')->replaceArgument(0, $xmlResources);
258+
$container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
259+
$container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
266260
}
267261

268262
/**

src/Bridge/Symfony/Bundle/Resources/config/metadata.xml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
66

77
<services>
8+
89
<!-- Extractor -->
9-
<service id="api_platform.metadata.extractor.yaml" class="ApiPlatform\Core\Metadata\YamlExtractor" public="false">
10-
<argument type="collection" />
11-
</service>
10+
11+
<service id="api_platform.metadata.extractor.yaml" class="ApiPlatform\Core\Metadata\YamlExtractor" public="false" />
12+
<service id="api_platform.metadata.extractor.xml" class="ApiPlatform\Core\Metadata\XmlExtractor" public="false" />
1213

1314
<!-- Resource name collection -->
1415

@@ -29,7 +30,7 @@
2930
</service>
3031

3132
<service id="api_platform.metadata.resource.name_collection_factory.xml" decorates="api_platform.metadata.resource.name_collection_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\XmlResourceNameCollectionFactory" public="false">
32-
<argument type="collection" />
33+
<argument type="service" id="api_platform.metadata.extractor.xml" />
3334
<argument type="service" id="api_platform.metadata.resource.name_collection_factory.xml.inner" />
3435
</service>
3536

@@ -51,7 +52,7 @@
5152
</service>
5253

5354
<service id="api_platform.metadata.resource.metadata_factory.xml" decorates="api_platform.metadata.resource.metadata_factory" class="ApiPlatform\Core\Metadata\Resource\Factory\XmlResourceMetadataFactory" decoration-priority="40" public="false">
54-
<argument type="collection" />
55+
<argument type="service" id="api_platform.metadata.extractor.xml" />
5556
<argument type="service" id="api_platform.metadata.resource.metadata_factory.xml.inner" />
5657
</service>
5758

@@ -100,7 +101,7 @@
100101
</service>
101102

102103
<service id="api_platform.metadata.property.name_collection_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\XmlPropertyNameCollectionFactory" decorates="api_platform.metadata.property.name_collection_factory" public="false">
103-
<argument type="collection" />
104+
<argument type="service" id="api_platform.metadata.extractor.xml" />
104105
<argument type="service" id="api_platform.metadata.property.name_collection_factory.xml.inner" />
105106
</service>
106107

@@ -129,7 +130,7 @@
129130
</service>
130131

131132
<service id="api_platform.metadata.property.metadata_factory.xml" class="ApiPlatform\Core\Metadata\Property\Factory\XmlPropertyMetadataFactory" decorates="api_platform.metadata.property.metadata_factory" decoration-priority="40" public="false">
132-
<argument type="collection" />
133+
<argument type="service" id="api_platform.metadata.extractor.xml" />
133134
<argument type="service" id="api_platform.metadata.property.metadata_factory.xml.inner" />
134135
</service>
135136

src/Metadata/Property/Factory/XmlPropertyMetadataFactory.php

Lines changed: 5 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111

1212
namespace ApiPlatform\Core\Metadata\Property\Factory;
1313

14-
use ApiPlatform\Core\Exception\InvalidArgumentException;
15-
use Symfony\Component\Config\Util\XmlUtils;
14+
use ApiPlatform\Core\Metadata\XmlExtractor;
1615

1716
/**
1817
* Creates a property metadata from XML {@see Property} configuration.
@@ -21,80 +20,20 @@
2120
*/
2221
final class XmlPropertyMetadataFactory extends AbstractFilePropertyMetadataFactory
2322
{
24-
const RESOURCE_SCHEMA = __DIR__.'/../../schema/metadata.xsd';
23+
private $extractor;
2524

26-
private $paths;
27-
28-
/**
29-
* @param string[] $paths
30-
* @param PropertyMetadataFactoryInterface|null $decorated
31-
*/
32-
public function __construct(array $paths, PropertyMetadataFactoryInterface $decorated = null)
25+
public function __construct(XmlExtractor $extractor, PropertyMetadataFactoryInterface $decorated = null)
3326
{
3427
parent::__construct($decorated);
3528

36-
$this->paths = $paths;
29+
$this->extractor = $extractor;
3730
}
3831

3932
/**
4033
* {@inheritdoc}
4134
*/
4235
protected function getMetadata(string $resourceClass, string $propertyName): array
4336
{
44-
foreach ($this->paths as $path) {
45-
try {
46-
$domDocument = XmlUtils::loadFile($path, self::RESOURCE_SCHEMA);
47-
} catch (\InvalidArgumentException $e) {
48-
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
49-
}
50-
51-
$properties = (new \DOMXPath($domDocument))->query(sprintf('//resources/resource[@class="%s"]/property[@name="%s"]', $resourceClass, $propertyName));
52-
53-
if (
54-
false === $properties ||
55-
0 >= $properties->length ||
56-
null === $properties->item(0) ||
57-
false === $property = simplexml_import_dom($properties->item(0))
58-
) {
59-
continue;
60-
}
61-
62-
return [
63-
'description' => (string) $property['description'] ?: null,
64-
'readable' => $property['readable'] ? (bool) XmlUtils::phpize($property['readable']) : null,
65-
'writable' => $property['writable'] ? (bool) XmlUtils::phpize($property['writable']) : null,
66-
'readableLink' => $property['readableLink'] ? (bool) XmlUtils::phpize($property['readableLink']) : null,
67-
'writableLink' => $property['writableLink'] ? (bool) XmlUtils::phpize($property['writableLink']) : null,
68-
'required' => $property['required'] ? (bool) XmlUtils::phpize($property['required']) : null,
69-
'identifier' => $property['identifier'] ? (bool) XmlUtils::phpize($property['identifier']) : null,
70-
'iri' => (string) $property['iri'] ?: null,
71-
'attributes' => $this->getAttributes($property),
72-
];
73-
}
74-
75-
return [];
76-
}
77-
78-
/**
79-
* Recursively transforms an attribute structure into an associative array.
80-
*
81-
* @param \SimpleXMLElement $element
82-
*
83-
* @return array
84-
*/
85-
private function getAttributes(\SimpleXMLElement $element): array
86-
{
87-
$attributes = [];
88-
foreach ($element->attribute as $attribute) {
89-
$value = isset($attribute->attribute[0]) ? $this->getAttributes($attribute) : (string) $attribute;
90-
91-
if (isset($attribute['name'])) {
92-
$attributes[(string) $attribute['name']] = $value;
93-
} else {
94-
$attributes[] = $value;
95-
}
96-
}
97-
98-
return $attributes;
37+
return $this->extractor->getResources()[$resourceClass]['properties'][$propertyName] ?? [];
9938
}
10039
}

src/Metadata/Property/Factory/XmlPropertyNameCollectionFactory.php

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use ApiPlatform\Core\Exception\InvalidArgumentException;
1515
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
1616
use ApiPlatform\Core\Metadata\Property\PropertyNameCollection;
17-
use Symfony\Component\Config\Util\XmlUtils;
17+
use ApiPlatform\Core\Metadata\XmlExtractor;
1818

1919
/**
2020
* Creates a property name collection from XML {@see Property} configuration files.
@@ -23,18 +23,12 @@
2323
*/
2424
final class XmlPropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface
2525
{
26-
const RESOURCE_SCHEMA = __DIR__.'/../../schema/metadata.xsd';
27-
28-
private $paths;
26+
private $extractor;
2927
private $decorated;
3028

31-
/**
32-
* @param array $paths
33-
* @param PropertyNameCollectionFactoryInterface|null $decorated
34-
*/
35-
public function __construct(array $paths, PropertyNameCollectionFactoryInterface $decorated = null)
29+
public function __construct(XmlExtractor $extractor, PropertyNameCollectionFactoryInterface $decorated = null)
3630
{
37-
$this->paths = $paths;
31+
$this->extractor = $extractor;
3832
$this->decorated = $decorated;
3933
}
4034

@@ -62,32 +56,15 @@ public function create(string $resourceClass, array $options = []): PropertyName
6256
}
6357

6458
$propertyNames = [];
65-
66-
foreach ($this->paths as $path) {
67-
try {
68-
$domDocument = XmlUtils::loadFile($path, self::RESOURCE_SCHEMA);
69-
} catch (\InvalidArgumentException $e) {
70-
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
71-
}
72-
73-
$properties = (new \DOMXPath($domDocument))->query(sprintf('//resources/resource[@class="%s"]/property', $resourceClass));
74-
75-
if (false === $properties || 0 >= $properties->length) {
76-
continue;
77-
}
78-
79-
foreach ($properties as $property) {
80-
if ('' === $propertyName = $property->getAttribute('name')) {
81-
continue;
82-
}
83-
59+
if (isset($propertyNameCollection)) {
60+
foreach ($propertyNameCollection as $propertyName) {
8461
$propertyNames[$propertyName] = true;
8562
}
8663
}
8764

88-
if (isset($propertyNameCollection)) {
89-
foreach ($propertyNameCollection as $propertyName) {
90-
$propertyNames[$propertyName] = true;
65+
if ($properties = $this->extractor->getResources()[$resourceClass]['properties'] ?? null) {
66+
foreach ($properties as $key => $value) {
67+
$propertyNames[$key] = true;
9168
}
9269
}
9370

src/Metadata/Resource/Factory/XmlResourceMetadataFactory.php

Lines changed: 9 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111

1212
namespace ApiPlatform\Core\Metadata\Resource\Factory;
1313

14-
use ApiPlatform\Core\Exception\InvalidArgumentException;
1514
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
1615
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
17-
use Symfony\Component\Config\Util\XmlUtils;
16+
use ApiPlatform\Core\Metadata\XmlExtractor;
1817

1918
/**
2019
* Creates a resource metadata from XML {@see Resource} configuration.
@@ -24,18 +23,12 @@
2423
*/
2524
final class XmlResourceMetadataFactory implements ResourceMetadataFactoryInterface
2625
{
27-
const RESOURCE_SCHEMA = __DIR__.'/../../schema/metadata.xsd';
28-
29-
private $paths;
26+
private $extractor;
3027
private $decorated;
3128

32-
/**
33-
* @param string[] $paths
34-
* @param ResourceMetadataFactoryInterface|null $decorated
35-
*/
36-
public function __construct(array $paths, ResourceMetadataFactoryInterface $decorated = null)
29+
public function __construct(XmlExtractor $extractor, ResourceMetadataFactoryInterface $decorated = null)
3730
{
38-
$this->paths = $paths;
31+
$this->extractor = $extractor;
3932
$this->decorated = $decorated;
4033
}
4134

@@ -53,68 +46,11 @@ public function create(string $resourceClass): ResourceMetadata
5346
}
5447
}
5548

56-
if (!class_exists($resourceClass) || empty($metadata = $this->getMetadata($resourceClass))) {
49+
if (!class_exists($resourceClass) || !($resource = $this->extractor->getResources()[$resourceClass] ?? null)) {
5750
return $this->handleNotFound($parentResourceMetadata, $resourceClass);
5851
}
5952

60-
return null === $parentResourceMetadata ? new ResourceMetadata(...$metadata) : $this->update($parentResourceMetadata, $metadata);
61-
}
62-
63-
/**
64-
* Extracts metadata from the XML tree.
65-
*
66-
* @param string $resourceClass
67-
*
68-
* @throws InvalidArgumentException
69-
*
70-
* @return array
71-
*/
72-
private function getMetadata(string $resourceClass): array
73-
{
74-
foreach ($this->paths as $path) {
75-
try {
76-
$domDocument = XmlUtils::loadFile($path, self::RESOURCE_SCHEMA);
77-
} catch (\InvalidArgumentException $e) {
78-
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
79-
}
80-
81-
$xml = simplexml_import_dom($domDocument);
82-
foreach ($xml->resource as $resource) {
83-
if ($resourceClass !== (string) $resource['class']) {
84-
continue;
85-
}
86-
87-
return [
88-
(string) $resource['shortName'] ?: null,
89-
(string) $resource['description'] ?: null,
90-
(string) $resource['iri'] ?: null,
91-
$this->getAttributes($resource, 'itemOperation') ?: null,
92-
$this->getAttributes($resource, 'collectionOperation') ?: null,
93-
$this->getAttributes($resource, 'attribute') ?: null,
94-
];
95-
}
96-
}
97-
98-
return [];
99-
}
100-
101-
/**
102-
* Recursively transforms an attribute structure into an associative array.
103-
*
104-
* @param \SimpleXMLElement $resource
105-
* @param string $elementName
106-
*
107-
* @return array
108-
*/
109-
private function getAttributes(\SimpleXMLElement $resource, string $elementName): array
110-
{
111-
$attributes = [];
112-
foreach ($resource->$elementName as $attribute) {
113-
$value = isset($attribute->attribute[0]) ? $this->getAttributes($attribute, 'attribute') : (string) $attribute;
114-
isset($attribute['name']) ? $attributes[(string) $attribute['name']] = $value : $attributes[] = $value;
115-
}
116-
117-
return $attributes;
53+
return $this->update($parentResourceMetadata ?: new ResourceMetadata(), $resource);
11854
}
11955

12056
/**
@@ -146,12 +82,12 @@ private function handleNotFound(ResourceMetadata $parentPropertyMetadata = null,
14682
*/
14783
private function update(ResourceMetadata $resourceMetadata, array $metadata): ResourceMetadata
14884
{
149-
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'attributes'] as $key => $property) {
150-
if (null === $metadata[$key] || null !== $resourceMetadata->{'get'.ucfirst($property)}()) {
85+
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'attributes'] as $property) {
86+
if (null === $metadata[$property] || null !== $resourceMetadata->{'get'.ucfirst($property)}()) {
15187
continue;
15288
}
15389

154-
$resourceMetadata = $resourceMetadata->{'with'.ucfirst($property)}($metadata[$key]);
90+
$resourceMetadata = $resourceMetadata->{'with'.ucfirst($property)}($metadata[$property]);
15591
}
15692

15793
return $resourceMetadata;

0 commit comments

Comments
 (0)