Skip to content
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

Do not use excluded fields when fetching entities #1246

Merged
merged 1 commit into from
Aug 22, 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
35 changes: 32 additions & 3 deletions src/Construction/DoctrineObjectConstructor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Exception\InvalidArgumentException;
use JMS\Serializer\Exception\ObjectConstructionException;
use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;

/**
Expand All @@ -34,15 +36,25 @@ final class DoctrineObjectConstructor implements ObjectConstructorInterface
*/
private $fallbackConstructor;

/**
* @var ExpressionLanguageExclusionStrategy|null
*/
private $expressionLanguageExclusionStrategy;

/**
* @param ManagerRegistry $managerRegistry Manager registry
* @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
*/
public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorInterface $fallbackConstructor, string $fallbackStrategy = self::ON_MISSING_NULL)
{
public function __construct(
ManagerRegistry $managerRegistry,
ObjectConstructorInterface $fallbackConstructor,
string $fallbackStrategy = self::ON_MISSING_NULL,
?ExpressionLanguageExclusionStrategy $expressionLanguageExclusionStrategy = null
) {
$this->managerRegistry = $managerRegistry;
$this->fallbackConstructor = $fallbackConstructor;
$this->fallbackStrategy = $fallbackStrategy;
$this->expressionLanguageExclusionStrategy = $expressionLanguageExclusionStrategy;
}

/**
Expand Down Expand Up @@ -78,7 +90,14 @@ public function construct(DeserializationVisitorInterface $visitor, ClassMetadat

foreach ($classMetadata->getIdentifierFieldNames() as $name) {
if (isset($metadata->propertyMetadata[$name])) {
$dataName = $metadata->propertyMetadata[$name]->serializedName;
$propertyMetadata = $metadata->propertyMetadata[$name];

// Avoid calling objectManager->find if some identification properties are excluded
if ($this->isIdentifierFieldExcluded($propertyMetadata, $context)) {
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}

$dataName = $propertyMetadata->serializedName;
} else {
$dataName = $name;
}
Expand Down Expand Up @@ -115,4 +134,14 @@ public function construct(DeserializationVisitorInterface $visitor, ClassMetadat

return $object;
}

private function isIdentifierFieldExcluded(PropertyMetadata $propertyMetadata, DeserializationContext $context): bool
{
$exclusionStrategy = $context->getExclusionStrategy();
if (null !== $exclusionStrategy && $exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
return true;
}

return null !== $this->expressionLanguageExclusionStrategy && $this->expressionLanguageExclusionStrategy->shouldSkipProperty($propertyMetadata, $context);
}
}
3 changes: 3 additions & 0 deletions tests/Fixtures/Doctrine/Entity/Author.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
namespace JMS\Serializer\Tests\Fixtures\Doctrine\Entity;

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\SerializedName;

/** @ORM\Entity */
class Author
{
/**
* @ORM\Id @ORM\Column(type="integer")
*
* @Groups({"id_group"})
*/
protected $id;

Expand Down
33 changes: 30 additions & 3 deletions tests/Serializer/Doctrine/ObjectConstructorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Exception\InvalidArgumentException;
use JMS\Serializer\Exception\ObjectConstructionException;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\Driver\DoctrineTypeDriver;
use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy;
Expand All @@ -38,6 +39,8 @@
use JMS\Serializer\Tests\Fixtures\Doctrine\IdentityFields\Server;
use JMS\Serializer\Tests\Fixtures\DoctrinePHPCR\Author as DoctrinePHPCRAuthor;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
use Metadata\Driver\AdvancedDriverInterface;
use Metadata\MetadataFactoryInterface;
use PHPUnit\Framework\TestCase;

class ObjectConstructorTest extends TestCase
Expand All @@ -54,6 +57,9 @@ class ObjectConstructorTest extends TestCase
/** @var DeserializationContext */
private $context;

/** @var AdvancedDriverInterface */
private $driver;

public function testFindEntity()
{
$em = $this->registry->getManager();
Expand All @@ -74,6 +80,26 @@ public function testFindEntity()
self::assertEquals($author, $authorFetched);
}

public function testFindEntityExcludedUsesFallback()
{
$graph = $this->createMock(GraphNavigatorInterface::class);
$metadata = $this->createMock(MetadataFactoryInterface::class);

$author = new Author('John');
$fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
$fallback->expects($this->once())->method('construct')->willReturn($author);

$type = ['name' => Author::class, 'params' => []];
$class = $this->driver->loadMetadataForClass(new \ReflectionClass(Author::class));

$context = DeserializationContext::create()->setGroups('foo');
$context->initialize('json', $this->visitor, $graph, $metadata);
$constructor = new DoctrineObjectConstructor($this->registry, $fallback);
$authorFetched = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $context);

self::assertSame($author, $authorFetched);
}

public function testFindManagedEntity()
{
$em = $this->registry->getManager();
Expand Down Expand Up @@ -308,13 +334,14 @@ static function ($id) use ($connection, $entityManager) {
}
}
);

$driver = null;
$this->driver = &$driver;
$this->serializer = SerializerBuilder::create()
->setMetadataDriverFactory(new CallbackDriverFactory(
static function (array $metadataDirs, Reader $annotationReader) use ($registry) {
static function (array $metadataDirs, Reader $annotationReader) use ($registry, &$driver) {
$defaultFactory = new DefaultDriverFactory(new IdenticalPropertyNamingStrategy());

return new DoctrineTypeDriver($defaultFactory->createDriver($metadataDirs, $annotationReader), $registry);
return $driver = new DoctrineTypeDriver($defaultFactory->createDriver($metadataDirs, $annotationReader), $registry);
}
))
->build();
Expand Down