Skip to content

Commit

Permalink
Merge pull request #1246 from schmittjoh/doctrine-groups
Browse files Browse the repository at this point in the history
Do not use excluded fields when fetching entities
  • Loading branch information
goetas authored Aug 22, 2020
2 parents 9685e94 + 36f2f44 commit f29178d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 6 deletions.
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

0 comments on commit f29178d

Please sign in to comment.