Skip to content

Instantly share code, notes, and snippets.

@soyuka
Forked from oxan/Embedded.php
Created June 5, 2018 17:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save soyuka/1e35dcf182284c35200b66b4be76b816 to your computer and use it in GitHub Desktop.
Save soyuka/1e35dcf182284c35200b66b4be76b816 to your computer and use it in GitHub Desktop.
Override API platform embedding decisions
<?php
// src/Serializer/Embedding/Embedded.php
namespace App\Serializer\Embedding;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD"})
*/
class Embedded
{
}
<?php
// src/Serializer/Embedding/EmbeddingPropertyMetadataFactory.php
namespace App\Serializer\Embedding;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use Doctrine\Common\Annotations\Reader;
class EmbeddingPropertyMetadataFactory implements PropertyMetadataFactoryInterface
{
private $reader;
private $decorated;
public function __construct(Reader $reader, PropertyMetadataFactoryInterface $decorated)
{
$this->reader = $reader;
$this->decorated = $decorated;
}
public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata
{
/* This is a workaround for upstream issue #1001 and #1994. Basically, it's impossible to
* serialize a self-referencing entity as a relation instead of embedding. This is because
* API platform decides to embed if the relation class has any properties with a serializer
* group we're currently using. Thus, when using serializer groups, a self-referencing
* entity will always be embedded.
*
* The result of this decision is stored in the readableLink/writableLink properties of the
* property metadata (see #479). We can override these properties by inserting a metadata
* factory that runs later than the SerializerPropertyMetadataFactory, and changing these
* values based on our custom annotations. That's what we do here.
*/
$propertyMetadata = $this->decorated->create($resourceClass, $property, $options);
try {
$reflectionClass = new \ReflectionClass($resourceClass);
} catch (\ReflectionException $reflectionException) {
return $propertyMetadata;
}
if (!$reflectionClass->hasProperty($property))
return $propertyMetadata;
$reflectionProperty = $reflectionClass->getProperty($property);
$embedAnnotation = $this->reader->getPropertyAnnotation($reflectionProperty, Embedded::class);
if (!is_null($embedAnnotation))
$propertyMetadata = $propertyMetadata->withReadableLink(true)->withWritableLink($propertyMetadata->isWritable());
$dontEmbedAnnotation = $this->reader->getPropertyAnnotation($reflectionProperty, NotEmbedded::class);
if (!is_null($dontEmbedAnnotation))
$propertyMetadata = $propertyMetadata->withReadableLink(false)->withWritableLink(false);
return $propertyMetadata;
}
}
<?php
// src/Serializer/Embedding/NotEmbedded.php
namespace App\Serializer\Embedding;
/**
* @Annotation
* @Target({"PROPERTY", "METHOD"})
*/
class NotEmbedded
{
}
# in app/config/services.yaml
services:
# custom metadata factory
app.api.embedding_property_metadata_factory:
decorates: api_platform.metadata.property.metadata_factory
decoration_priority: 0
class: App\Serializer\Embedding\EmbeddingPropertyMetadataFactory
arguments:
- '@annotation_reader'
- '@app.api.embedding_property_metadata_factory.inner'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment