Skip to content

[Serializer] Add an @Ignore annotation #28744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public function getProperties(string $class, array $context = []): ?array
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);

foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
if (array_intersect($context['serializer_groups'], $serializerAttributeMetadata->getGroups())) {
$ignored = method_exists($serializerClassMetadata, 'isIgnored') && $serializerAttributeMetadata->isIgnored();
if (!$ignored && array_intersect($context['serializer_groups'], $serializerAttributeMetadata->getGroups())) {
$properties[] = $serializerAttributeMetadata->getName();
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/Symfony/Component/Serializer/Annotation/Ignore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Annotation;

/**
* Annotation class for @Ignore().
*
* @Annotation
* @Target({"PROPERTY", "METHOD"})
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
final class Ignore
{
}
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CHANGELOG

* added support for scalar values denormalization
* added support for `\stdClass` to `ObjectNormalizer`
* added the ability to ignore properties using metadata (e.g. `@Symfony\Component\Serializer\Annotation\Ignore`)

5.0.0
-----
Expand Down
31 changes: 30 additions & 1 deletion src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ class AttributeMetadata implements AttributeMetadataInterface
*/
public $serializedName;

/**
* @var bool
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link isIgnored()} instead.
*/
public $ignore = false;

public function __construct(string $name)
{
$this->name = $name;
Expand Down Expand Up @@ -113,6 +122,22 @@ public function getSerializedName(): ?string
return $this->serializedName;
}

/**
* {@inheritdoc}
*/
public function setIgnore(bool $ignore)
{
$this->ignore = $ignore;
}

/**
* {@inheritdoc}
*/
public function isIgnored(): bool
{
return $this->ignore;
}

/**
* {@inheritdoc}
*/
Expand All @@ -131,6 +156,10 @@ public function merge(AttributeMetadataInterface $attributeMetadata)
if (null === $this->serializedName) {
$this->serializedName = $attributeMetadata->getSerializedName();
}

if ($ignore = $attributeMetadata->isIgnored()) {
$this->ignore = $ignore;
}
}

/**
Expand All @@ -140,6 +169,6 @@ public function merge(AttributeMetadataInterface $attributeMetadata)
*/
public function __sleep()
{
return ['name', 'groups', 'maxDepth', 'serializedName'];
return ['name', 'groups', 'maxDepth', 'serializedName', 'ignore'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ public function setSerializedName(string $serializedName = null);
*/
public function getSerializedName(): ?string;

/**
* Sets if this attribute must be ignored or not.
*/
public function setIgnore(bool $ignore);

/**
* Gets if this attribute is ignored or not.
*/
public function isIgnored(): bool;

/**
* Merges an {@see AttributeMetadataInterface} with in the current one.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\Ignore;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Serializer\Exception\MappingException;
Expand Down Expand Up @@ -71,6 +72,8 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
$attributesMetadata[$property->name]->setMaxDepth($annotation->getMaxDepth());
} elseif ($annotation instanceof SerializedName) {
$attributesMetadata[$property->name]->setSerializedName($annotation->getSerializedName());
} elseif ($annotation instanceof Ignore) {
$attributesMetadata[$property->name]->setIgnore(true);
}

$loaded = true;
Expand Down Expand Up @@ -116,6 +119,8 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
}

$attributeMetadata->setSerializedName($annotation->getSerializedName());
} elseif ($annotation instanceof Ignore) {
$attributeMetadata->setIgnore(true);
}

$loaded = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
if (isset($attribute['serialized-name'])) {
$attributeMetadata->setSerializedName((string) $attribute['serialized-name']);
}

if (isset($attribute['ignore'])) {
$attributeMetadata->setIgnore((bool) $attribute['ignore']);
}
}

if (isset($xml->{'discriminator-map'})) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)

$attributeMetadata->setSerializedName($data['serialized_name']);
}

if (isset($data['ignore'])) {
if (!\is_bool($data['ignore'])) {
throw new MappingException(sprintf('The "ignore" value must be a boolean in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()));
}

$attributeMetadata->setIgnore($data['ignore']);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
</xsd:choice>
<xsd:attribute name="type-property" type="xsd:string" use="required" />
</xsd:complexType>

<xsd:complexType name="discriminator-map-mapping">
<xsd:attribute name="type" type="xsd:string" use="required" />
<xsd:attribute name="class" type="xsd:string" use="required" />
Expand Down Expand Up @@ -78,6 +78,7 @@
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="ignore" type="xsd:boolean" />
</xsd:complexType>

</xsd:schema>
17 changes: 12 additions & 5 deletions src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,22 +239,29 @@ protected function getAllowedAttributes($classOrObject, array $context, bool $at

$tmpGroups = $context[self::GROUPS] ?? $this->defaultContext[self::GROUPS] ?? null;
$groups = (\is_array($tmpGroups) || is_scalar($tmpGroups)) ? (array) $tmpGroups : false;
if (false === $groups && $allowExtraAttributes) {
return false;
}

$allowedAttributes = [];
$ignoreUsed = false;
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
$name = $attributeMetadata->getName();
if ($ignore = $attributeMetadata->isIgnored()) {
$ignoreUsed = true;
}

// If you update this check, update accordingly the one in Symfony\Component\PropertyInfo\Extractor\SerializerExtractor::getProperties()
if (
!$ignore &&
(false === $groups || array_intersect($attributeMetadata->getGroups(), $groups)) &&
$this->isAllowedAttribute($classOrObject, $name, null, $context)
$this->isAllowedAttribute($classOrObject, $name = $attributeMetadata->getName(), null, $context)
) {
$allowedAttributes[] = $attributesAsString ? $name : $attributeMetadata;
}
}

if (!$ignoreUsed && false === $groups && $allowExtraAttributes) {
// Backward Compatibility with the code using this method written before the introduction of @Ignore
return false;
}

return $allowedAttributes;
}

Expand Down
35 changes: 35 additions & 0 deletions src/Symfony/Component/Serializer/Tests/Fixtures/IgnoreDummy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Fixtures;

use Symfony\Component\Serializer\Annotation\Ignore;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class IgnoreDummy
{
public $notIgnored;
/**
* @Ignore()
*/
public $ignored1;
private $ignored2;

/**
* @Ignore()
*/
public function getIgnored2()
{
return $this->ignored2;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy':
attributes:
ignored1:
ignore: foo
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@
<attribute name="foo" />
</class>

<class name="Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy">
<attribute name="ignored1" ignore="true" />
<attribute name="ignored2" ignore="true" />
</class>

</serializer>
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@
second: 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild'
attributes:
foo: ~
'Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy':
attributes:
ignored1:
ignore: true
ignored2:
ignore: true
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ public function testSerializedName()
$this->assertEquals('serialized_name', $attributeMetadata->getSerializedName());
}

public function testIgnore()
{
$attributeMetadata = new AttributeMetadata('ignored');
$this->assertFalse($attributeMetadata->isIgnored());
$attributeMetadata->setIgnore(true);
$this->assertTrue($attributeMetadata->isIgnored());
}

public function testMerge()
{
$attributeMetadata1 = new AttributeMetadata('a1');
Expand All @@ -69,11 +77,14 @@ public function testMerge()
$attributeMetadata2->setMaxDepth(2);
$attributeMetadata2->setSerializedName('a3');

$attributeMetadata2->setIgnore(true);

$attributeMetadata1->merge($attributeMetadata2);

$this->assertEquals(['a', 'b', 'c'], $attributeMetadata1->getGroups());
$this->assertEquals(2, $attributeMetadata1->getMaxDepth());
$this->assertEquals('a3', $attributeMetadata1->getSerializedName());
$this->assertTrue($attributeMetadata1->isIgnored());
}

public function testSerialize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;

/**
Expand Down Expand Up @@ -105,4 +106,14 @@ public function testLoadClassMetadataAndMerge()

$this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $classMetadata);
}

public function testLoadIgnore()
{
$classMetadata = new ClassMetadata(IgnoreDummy::class);
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertTrue($attributesMetadata['ignored1']->isIgnored());
$this->assertTrue($attributesMetadata['ignored2']->isIgnored());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;

/**
Expand Down Expand Up @@ -92,4 +93,14 @@ public function testLoadDiscriminatorMap()

$this->assertEquals($expected, $classMetadata);
}

public function testLoadIgnore()
{
$classMetadata = new ClassMetadata(IgnoreDummy::class);
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertTrue($attributesMetadata['ignored1']->isIgnored());
$this->assertTrue($attributesMetadata['ignored2']->isIgnored());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
namespace Symfony\Component\Serializer\Tests\Mapping\Loader;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\IgnoreDummy;
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;

/**
Expand Down Expand Up @@ -105,4 +107,22 @@ public function testLoadDiscriminatorMap()

$this->assertEquals($expected, $classMetadata);
}

public function testLoadIgnore()
{
$classMetadata = new ClassMetadata(IgnoreDummy::class);
$this->loader->loadClassMetadata($classMetadata);

$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertTrue($attributesMetadata['ignored1']->isIgnored());
$this->assertTrue($attributesMetadata['ignored2']->isIgnored());
}

public function testLoadInvalidIgnore()
{
$this->expectException(MappingException::class);
$this->expectExceptionMessage('The "ignore" value must be a boolean');

(new YamlFileLoader(__DIR__.'/../../Fixtures/invalid-ignore.yml'))->loadClassMetadata(new ClassMetadata(IgnoreDummy::class));
}
}
Loading