Skip to content
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
144 changes: 144 additions & 0 deletions src/Metadata/Property/Factory/AbstractFilePropertyMetadataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Metadata\Property\Factory;

use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Exception\PropertyNotFoundException;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;

/**
* Common base class to load properties's metadata from files.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @internal
*/
abstract class AbstractFilePropertyMetadataFactory implements PropertyMetadataFactoryInterface
{
protected $paths;
protected $decorated;

/**
* @param string[] $paths
* @param PropertyMetadataFactoryInterface|null $decorated
*/
public function __construct(array $paths, PropertyMetadataFactoryInterface $decorated = null)
{
$this->paths = $paths;
$this->decorated = $decorated;
}

/**
* {@inheritdoc}
*/
public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata
{
$parentPropertyMetadata = null;
if ($this->decorated) {
try {
$parentPropertyMetadata = $this->decorated->create($resourceClass, $property, $options);
} catch (PropertyNotFoundException $propertyNotFoundException) {
// Ignore not found exception from decorated factories
}
}

if (
!property_exists($resourceClass, $property) ||
empty($propertyMetadata = $this->getMetadata($resourceClass, $property))
) {
return $this->handleNotFound($parentPropertyMetadata, $resourceClass, $property);
}

if ($parentPropertyMetadata) {
return $this->update($parentPropertyMetadata, $propertyMetadata);
}

return new PropertyMetadata(
null,
$propertyMetadata['description'],
$propertyMetadata['readable'],
$propertyMetadata['writable'],
$propertyMetadata['readableLink'],
$propertyMetadata['writableLink'],
$propertyMetadata['required'],
$propertyMetadata['identifier'],
$propertyMetadata['iri'],
null,
$propertyMetadata['attributes']
);
}

/**
* Extracts metadata from the XML tree.
Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessarily XML...

*
* @param string $resourceClass
* @param string $propertyName
*
* @throws InvalidArgumentException
*
* @return array
*/
abstract protected function getMetadata(string $resourceClass, string $propertyName): array;

/**
* Returns the metadata from the decorated factory if available or throws an exception.
*
* @param PropertyMetadata|null $parentPropertyMetadata
* @param string $resourceClass
* @param string $property
*
* @throws PropertyNotFoundException
*
* @return PropertyMetadata
*/
private function handleNotFound(PropertyMetadata $parentPropertyMetadata = null, string $resourceClass, string $property): PropertyMetadata
{
if ($parentPropertyMetadata) {
return $parentPropertyMetadata;
}

throw new PropertyNotFoundException(sprintf('Property "%s" of the resource class "%s" not found.', $property, $resourceClass));
}

/**
* Creates a new instance of metadata if the property is not already set.
*
* @param PropertyMetadata $propertyMetadata
* @param array $metadata
*
* @return PropertyMetadata
*/
private function update(PropertyMetadata $propertyMetadata, array $metadata): PropertyMetadata
{
$metadataAccessors = [
'description' => 'get',
'readable' => 'is',
'writable' => 'is',
'writableLink' => 'is',
'readableLink' => 'is',
'required' => 'is',
'identifier' => 'is',
'iri' => 'get',
'attributes' => 'get',
];

foreach ($metadataAccessors as $metadataKey => $accessorPrefix) {
if (null === $metadata[$metadataKey] || null !== $propertyMetadata->{$accessorPrefix.ucfirst($metadataKey)}()) {
continue;
}

$propertyMetadata = $propertyMetadata->{'with'.ucfirst($metadataKey)}($metadata[$metadataKey]);
}

return $propertyMetadata;
}
}
119 changes: 2 additions & 117 deletions src/Metadata/Property/Factory/XmlPropertyMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,103 +12,21 @@
namespace ApiPlatform\Core\Metadata\Property\Factory;

use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Exception\PropertyNotFoundException;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use Symfony\Component\Config\Util\XmlUtils;

/**
* Creates a property metadata from XML {@see Property} configuration.
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
class XmlPropertyMetadataFactory implements PropertyMetadataFactoryInterface
final class XmlPropertyMetadataFactory extends AbstractFilePropertyMetadataFactory
{
const RESOURCE_SCHEMA = __DIR__.'/../../schema/metadata.xsd';

private $paths;
private $decorated;

/**
* @param string[] $paths
* @param PropertyMetadataFactoryInterface|null $decorated
*/
public function __construct(array $paths, PropertyMetadataFactoryInterface $decorated = null)
{
$this->paths = $paths;
$this->decorated = $decorated;
}

/**
* {@inheritdoc}
*/
public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata
{
$parentPropertyMetadata = null;
if ($this->decorated) {
try {
$parentPropertyMetadata = $this->decorated->create($resourceClass, $property, $options);
} catch (PropertyNotFoundException $propertyNotFoundException) {
// Ignore not found exception from decorated factories
}
}

if (
!property_exists($resourceClass, $property) ||
empty($propertyMetadata = $this->getMetadata($resourceClass, $property))
) {
return $this->handleNotFound($parentPropertyMetadata, $resourceClass, $property);
}

if ($parentPropertyMetadata) {
return $this->update($parentPropertyMetadata, $propertyMetadata);
}

return new PropertyMetadata(
null,
$propertyMetadata['description'],
$propertyMetadata['readable'],
$propertyMetadata['writable'],
$propertyMetadata['readableLink'],
$propertyMetadata['writableLink'],
$propertyMetadata['required'],
$propertyMetadata['identifier'],
$propertyMetadata['iri'],
null,
$propertyMetadata['attributes']
);
}

/**
* Returns the metadata from the decorated factory if available or throws an exception.
*
* @param PropertyMetadata|null $parentPropertyMetadata
* @param string $resourceClass
* @param string $property
*
* @throws PropertyNotFoundException
*
* @return PropertyMetadata
*/
private function handleNotFound(PropertyMetadata $parentPropertyMetadata = null, string $resourceClass, string $property): PropertyMetadata
{
if ($parentPropertyMetadata) {
return $parentPropertyMetadata;
}

throw new PropertyNotFoundException(sprintf('Property "%s" of the resource class "%s" not found.', $property, $resourceClass));
}

/**
* Extracts metadata from the XML tree.
*
* @param string $resourceClass
* @param string $propertyName
*
* @throws InvalidArgumentException
*
* @return array
*/
private function getMetadata(string $resourceClass, string $propertyName): array
protected function getMetadata(string $resourceClass, string $propertyName): array
{
foreach ($this->paths as $path) {
try {
Expand Down Expand Up @@ -166,37 +84,4 @@ private function getAttributes(\SimpleXMLElement $element): array

return $attributes;
}

/**
* Creates a new instance of metadata if the property is not already set.
*
* @param PropertyMetadata $propertyMetadata
* @param array $metadata
*
* @return PropertyMetadata
*/
private function update(PropertyMetadata $propertyMetadata, array $metadata): PropertyMetadata
{
$metadataAccessors = [
'description' => 'get',
'readable' => 'is',
'writable' => 'is',
'writableLink' => 'is',
'readableLink' => 'is',
'required' => 'is',
'identifier' => 'is',
'iri' => 'get',
'attributes' => 'get',
];

foreach ($metadataAccessors as $metadataKey => $accessorPrefix) {
if (null === $metadata[$metadataKey] || null !== $propertyMetadata->{$accessorPrefix.ucfirst($metadataKey)}()) {
continue;
}

$propertyMetadata = $propertyMetadata->{'with'.ucfirst($metadataKey)}($metadata[$metadataKey]);
}

return $propertyMetadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
class XmlPropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface
final class XmlPropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface
{
const RESOURCE_SCHEMA = __DIR__.'/../../schema/metadata.xsd';

Expand Down
Loading