Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e43bd57

Browse filesBrowse files
committed
feature #18834 [Serializer] Add the possibility to filter attributes (dunglas)
This PR was merged into the 3.3-dev branch. Discussion ---------- [Serializer] Add the possibility to filter attributes | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | no | | Tests pass? | yes | | Fixed tickets | n/a | | License | MIT | | Doc PR | todo | This new features give the possibility to the end user to select select fields to normalize. Exemple: ``` php class Foo { public $a = '1'; public $b = '2'; public $c = '3'; } $normalizer = new \Symfony\Component\Serializer\Normalizer\ObjectNormalizer(); $normalizer->normalize(new Foo(), null, ['attributes' => ['a', 'c']]); // ['a' => '1', 'c' => '3'] ``` Denormalization is also supported. This feature works for any normalizer using the `Symfony\Component\Serializer\Normalizer\AbstractNormalizer` class. The main use case is to permit to API clients to optimize responses length by dropping unnecessary information like in the following example: ``` http://exemple.com/cars?fields=a,c {'a' => 1, 'c' => 2} ``` Thanks to this PR, support for this feature will be available out of the box in [API Platform](https://api-platform.com). Commits ------- b3826fb [Serializer] Add the possibility to filter attributes
2 parents a0f1e85 + b3826fb commit e43bd57
Copy full SHA for e43bd57

File tree

3 files changed

+123
-5
lines changed
Filter options

3 files changed

+123
-5
lines changed

‎src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+33-3Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,20 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu
236236
*/
237237
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
238238
{
239-
return !in_array($attribute, $this->ignoredAttributes);
239+
if (in_array($attribute, $this->ignoredAttributes)) {
240+
return false;
241+
}
242+
243+
if (isset($context['attributes'][$attribute])) {
244+
// Nested attributes
245+
return true;
246+
}
247+
248+
if (isset($context['attributes']) && is_array($context['attributes'])) {
249+
return in_array($attribute, $context['attributes'], true);
250+
}
251+
252+
return true;
240253
}
241254

242255
/**
@@ -324,7 +337,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
324337
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName) : $paramName;
325338

326339
$allowed = $allowedAttributes === false || in_array($paramName, $allowedAttributes);
327-
$ignored = in_array($paramName, $this->ignoredAttributes);
340+
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
328341
if (method_exists($constructorParameter, 'isVariadic') && $constructorParameter->isVariadic()) {
329342
if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) {
330343
if (!is_array($data[$paramName])) {
@@ -341,7 +354,7 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
341354
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), static::class));
342355
}
343356
$parameterClass = $constructorParameter->getClass()->getName();
344-
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $context);
357+
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $paramName));
345358
}
346359
} catch (\ReflectionException $e) {
347360
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e);
@@ -372,4 +385,21 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
372385

373386
return new $class();
374387
}
388+
389+
/**
390+
* @param array $parentContext
391+
* @param string $attribute
392+
*
393+
* @return array
394+
*
395+
* @internal
396+
*/
397+
protected function createChildContext(array $parentContext, $attribute)
398+
{
399+
if (isset($parentContext['attributes'][$attribute])) {
400+
$parentContext['attributes'] = $parentContext['attributes'][$attribute];
401+
}
402+
403+
return $parentContext;
404+
}
375405
}

‎src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function normalize($object, $format = null, array $context = array())
9595
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute));
9696
}
9797

98-
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $context));
98+
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute)));
9999
}
100100

101101
return $data;
@@ -268,7 +268,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
268268
}
269269

270270
if ($this->serializer->supportsDenormalization($data, $class, $format)) {
271-
return $this->serializer->denormalize($data, $class, $format, $context);
271+
return $this->serializer->denormalize($data, $class, $format, $this->createChildContext($context, $attribute));
272272
}
273273
}
274274

‎src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php
+88Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,70 @@ public function testExtractAttributesRespectsContext()
643643

644644
$this->assertSame(array('foo' => 'bar', 'bar' => 'foo'), $normalizer->normalize($data, null, array('include_foo_and_bar' => true)));
645645
}
646+
647+
public function testAttributesContextNormalize()
648+
{
649+
$normalizer = new ObjectNormalizer();
650+
$serializer = new Serializer(array($normalizer));
651+
652+
$objectInner = new ObjectInner();
653+
$objectInner->foo = 'innerFoo';
654+
$objectInner->bar = 'innerBar';
655+
656+
$objectDummy = new ObjectDummy();
657+
$objectDummy->setFoo('foo');
658+
$objectDummy->setBaz(true);
659+
$objectDummy->setObject($objectInner);
660+
661+
$context = array('attributes' => array('foo', 'baz', 'object' => array('foo')));
662+
$this->assertEquals(
663+
array(
664+
'foo' => 'foo',
665+
'baz' => true,
666+
'object' => array('foo' => 'innerFoo'),
667+
),
668+
$serializer->normalize($objectDummy, null, $context)
669+
);
670+
}
671+
672+
public function testAttributesContextDenormalize()
673+
{
674+
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
675+
$serializer = new Serializer(array($normalizer));
676+
677+
$objectInner = new ObjectInner();
678+
$objectInner->foo = 'innerFoo';
679+
680+
$objectOuter = new ObjectOuter();
681+
$objectOuter->bar = 'bar';
682+
$objectOuter->setInner($objectInner);
683+
684+
$context = array('attributes' => array('bar', 'inner' => array('foo')));
685+
$this->assertEquals($objectOuter, $serializer->denormalize(
686+
array(
687+
'foo' => 'foo',
688+
'bar' => 'bar',
689+
'date' => '2017-02-03',
690+
'inner' => array('foo' => 'innerFoo', 'bar' => 'innerBar'),
691+
), ObjectOuter::class, null, $context));
692+
}
693+
694+
public function testAttributesContextDenormalizeConstructor()
695+
{
696+
$normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor());
697+
$serializer = new Serializer(array($normalizer));
698+
699+
$objectInner = new ObjectInner();
700+
$objectInner->bar = 'bar';
701+
702+
$obj = new DummyWithConstructorObjectAndDefaultValue('a', $objectInner);
703+
704+
$context = array('attributes' => array('inner' => array('bar')));
705+
$this->assertEquals($obj, $serializer->denormalize(array(
706+
'foo' => 'b',
707+
'inner' => array('foo' => 'foo', 'bar' => 'bar'),
708+
), DummyWithConstructorObjectAndDefaultValue::class, null, $context));
709+
}
646710
}
647711

648712
class ObjectDummy
@@ -813,6 +877,8 @@ public function setFoo(array $f)
813877

814878
class ObjectOuter
815879
{
880+
public $foo;
881+
public $bar;
816882
private $inner;
817883
private $date;
818884

@@ -910,3 +976,25 @@ class JsonNumber
910976
*/
911977
public $number;
912978
}
979+
980+
class DummyWithConstructorObjectAndDefaultValue
981+
{
982+
private $foo;
983+
private $inner;
984+
985+
public function __construct($foo = 'a', ObjectInner $inner)
986+
{
987+
$this->foo = $foo;
988+
$this->inner = $inner;
989+
}
990+
991+
public function getFoo()
992+
{
993+
return $this->foo;
994+
}
995+
996+
public function getInner()
997+
{
998+
return $this->inner;
999+
}
1000+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.