Skip to content

[Live] Lower the normalizer priority of DoctrineObjectNormalizer and fix embeddables #453

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
Sep 16, 2022
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 @@ -22,7 +22,7 @@ public function process(ContainerBuilder $container): void
if ($container->hasDefinition('doctrine')) {
$container->register('ux.live_component.doctrine_object_normalizer', DoctrineObjectNormalizer::class)
->setArguments([new IteratorArgument([new Reference('doctrine')])]) // todo add other object managers (mongo)
->addTag('serializer.normalizer', ['priority' => 100])
->addTag('serializer.normalizer', ['priority' => -100])
;
}
}
Expand Down
22 changes: 21 additions & 1 deletion src/LiveComponent/src/Normalizer/DoctrineObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\UX\LiveComponent\Normalizer;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
Expand Down Expand Up @@ -83,10 +84,29 @@ private function objectManagerFor(string $class): ?ObjectManager
// todo cache/warmup an array of classes that are "doctrine objects"
foreach ($this->managerRegistries as $registry) {
if ($om = $registry->getManagerForClass($class)) {
return $om;
return self::ensureManagedObject($om, $class);
}
}

return null;
}

/**
* Ensure the $class is not embedded or a mapped superclass.
*/
private static function ensureManagedObject(ObjectManager $om, string $class): ?ObjectManager
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #452 (comment) for why this is needed.

{
if (!$om instanceof EntityManagerInterface) {
// todo might need to add some checks once ODM support is added
return $om;
}

$metadata = $om->getClassMetadata($class);

if ($metadata->isEmbeddedClass || $metadata->isMappedSuperclass) {
return null;
}

return $om;
}
}
37 changes: 37 additions & 0 deletions src/LiveComponent/tests/Fixtures/Component/WithObjects.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Embeddable2;
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Money;
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Temperature;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Embeddable1;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Entity1;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Entity2;

#[AsLiveComponent('with_objects')]
final class WithObjects
{
use DefaultActionTrait;

#[LiveProp]
public Money $money;

#[LiveProp]
public Temperature $temperature;

#[LiveProp]
public Entity1 $entity1;

#[LiveProp]
public Entity2 $entity2;

#[LiveProp]
public Embeddable1 $embeddable1;

#[LiveProp]
public Embeddable2 $embeddable2;
}
10 changes: 10 additions & 0 deletions src/LiveComponent/tests/Fixtures/Dto/Embeddable2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Dto;

final class Embeddable2
{
public function __construct(public string $name)
{
}
}
22 changes: 22 additions & 0 deletions src/LiveComponent/tests/Fixtures/Dto/Money.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Dto;

/**
* @author Kevin Bond <[email protected]>
*/
final class Money
{
public function __construct(public int $amount, public string $currency)
{
}
}
22 changes: 22 additions & 0 deletions src/LiveComponent/tests/Fixtures/Dto/Temperature.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Dto;

/**
* @author Kevin Bond <[email protected]>
*/
final class Temperature
{
public function __construct(public int $degrees, public string $uom)
{
}
}
30 changes: 30 additions & 0 deletions src/LiveComponent/tests/Fixtures/Entity/Embeddable1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Embeddable
*/
final class Embeddable1
{
/**
* @ORM\Column
*/
public string $name;

public function __construct(string $name)
{
$this->name = $name;
}
}
38 changes: 38 additions & 0 deletions src/LiveComponent/tests/Fixtures/Entity/Entity2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Embeddable2;

/**
* @ORM\Entity
*/
class Entity2
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
public $id;

/**
* @ORM\Embedded(Embeddable1::class)
*/
public Embeddable1 $embedded1;

/**
* @ORM\Embedded(Embeddable2::class)
*/
public Embeddable2 $embedded2;
}
15 changes: 13 additions & 2 deletions src/LiveComponent/tests/Fixtures/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use Symfony\UX\LiveComponent\LiveComponentBundle;
use Symfony\UX\LiveComponent\Tests\Fixtures\Serializer\Entity2Normalizer;
use Symfony\UX\LiveComponent\Tests\Fixtures\Serializer\MoneyNormalizer;
use Symfony\UX\TwigComponent\TwigComponentBundle;
use Twig\Environment;

Expand Down Expand Up @@ -73,12 +75,19 @@ protected function configureContainer(ContainerConfigurator $c): void
'auto_generate_proxy_classes' => true,
'auto_mapping' => true,
'mappings' => [
'Test' => [
'Default' => [
'is_bundle' => false,
'type' => 'annotation',
'dir' => '%kernel.project_dir%/tests/Fixtures/Entity',
'prefix' => 'Symfony\UX\LiveComponent\Tests\Fixtures\Entity',
'alias' => 'Test',
'alias' => 'Default',
],
'XML' => [
'is_bundle' => false,
'type' => 'xml',
'dir' => '%kernel.project_dir%/tests/Fixtures/config/doctrine',
'prefix' => 'Symfony\UX\LiveComponent\Tests\Fixtures\Dto',
'alias' => 'XML',
],
],
],
Expand All @@ -90,6 +99,8 @@ protected function configureContainer(ContainerConfigurator $c): void
->autoconfigure()
// disable logging errors to the console
->set('logger', NullLogger::class)
->set(MoneyNormalizer::class)->autoconfigure()->autowire()
->set(Entity2Normalizer::class)->autoconfigure()->autowire()
->load(__NAMESPACE__.'\\Component\\', __DIR__.'/Component')
;
}
Expand Down
49 changes: 49 additions & 0 deletions src/LiveComponent/tests/Fixtures/Serializer/Entity2Normalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Serializer;

use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Entity2;

/**
* @author Kevin Bond <[email protected]>
*/
final class Entity2Normalizer implements NormalizerInterface, DenormalizerInterface
{
public function __construct(private ManagerRegistry $doctrine)
{
}

public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
{
[, $id] = \explode(':', $data);

return $this->doctrine->getRepository(Entity2::class)->find($id);
}

public function supportsDenormalization(mixed $data, string $type, string $format = null)
{
return Entity2::class === $type;
}

public function normalize(mixed $object, string $format = null, array $context = [])
{
return 'entity2:'.$object->id;
}

public function supportsNormalization(mixed $data, string $format = null)
{
return $data instanceof Entity2;
}
}
39 changes: 39 additions & 0 deletions src/LiveComponent/tests/Fixtures/Serializer/MoneyNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

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

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Serializer;

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Money;

final class MoneyNormalizer implements NormalizerInterface, DenormalizerInterface
{
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
{
return new Money(...\explode('|', $data));
}

public function supportsDenormalization(mixed $data, string $type, string $format = null)
{
return Money::class === $type;
}

public function normalize(mixed $object, string $format = null, array $context = [])
{
return \implode('|', [$object->amount, $object->currency]);
}

public function supportsNormalization(mixed $data, string $format = null)
{
return $data instanceof Money;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<doctrine-mapping>
<embeddable name="Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Embeddable2">
<field name="name" type="string" />
</embeddable>
</doctrine-mapping>
Loading