Skip to content

[LiveComponent] Fix (de)hydration of composite and/or foreign ID entities #1957

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
Jul 5, 2024
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 @@ -48,8 +48,8 @@ public function hydrate(mixed $value, string $className): ?object
return null;
}

// $data is the single identifier or array of identifiers
if (\is_scalar($value) || (\is_array($value) && isset($value[0]))) {
// $data is a single identifier or array of identifiers
if (\is_scalar($value) || \is_array($value)) {
return $this->objectManagerFor($className)->find($className, $value);
}

Expand All @@ -64,6 +64,9 @@ public function dehydrate(object $object): mixed
->getIdentifierValues($object)
;

// Dehydrate ID values in case they are other entities
$id = array_map(fn ($id) => \is_object($id) && $this->supports($id::class) ? $this->dehydrate($id) : $id, $id);

switch (\count($id)) {
case 0:
// a non-persisted entity
Expand Down
29 changes: 29 additions & 0 deletions src/LiveComponent/tests/Fixtures/Entity/CompositeIdEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?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\UX\LiveComponent\Tests\Fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class CompositeIdEntity
{
public function __construct(
#[ORM\Id]
#[ORM\Column(type: 'integer')]
private int $firstIdPart,

#[ORM\Id]
#[ORM\Column(type: 'integer')]
private int $secondIdPart,
) {
}
}
25 changes: 25 additions & 0 deletions src/LiveComponent/tests/Fixtures/Entity/ForeignKeyIdEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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\UX\LiveComponent\Tests\Fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class ForeignKeyIdEntity
{
public function __construct(
#[ORM\Id]
#[ORM\ManyToOne(cascade: ['persist'])]
public Entity1 $id,
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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\UX\LiveComponent\Tests\Fixtures\Factory;

use Doctrine\ORM\EntityRepository;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\CompositeIdEntity;
use Zenstruck\Foundry\ModelFactory;
use Zenstruck\Foundry\Proxy;
use Zenstruck\Foundry\RepositoryProxy;

/**
* @extends ModelFactory<CompositeIdEntity>
*
* @method static CompositeIdEntity|Proxy createOne(array $attributes = [])
* @method static CompositeIdEntity[]|Proxy[] createMany(int $number, array|callable $attributes = [])
* @method static CompositeIdEntity|Proxy find(object|array|mixed $criteria)
* @method static CompositeIdEntity|Proxy findOrCreate(array $attributes)
* @method static CompositeIdEntity|Proxy first(string $sortedField = 'id')
* @method static CompositeIdEntity|Proxy last(string $sortedField = 'id')
* @method static CompositeIdEntity|Proxy random(array $attributes = [])
* @method static CompositeIdEntity|Proxy randomOrCreate(array $attributes = []))
* @method static CompositeIdEntity[]|Proxy[] all()
* @method static CompositeIdEntity[]|Proxy[] findBy(array $attributes)
* @method static CompositeIdEntity[]|Proxy[] randomSet(int $number, array $attributes = []))
* @method static CompositeIdEntity[]|Proxy[] randomRange(int $min, int $max, array $attributes = []))
* @method static EntityRepository|RepositoryProxy repository()
* @method CompositeIdEntity|Proxy create(array|callable $attributes = [])
*/
class CompositeIdEntityFactory extends ModelFactory
{
protected static function getClass(): string
{
return CompositeIdEntity::class;
}

protected function getDefaults(): array
{
return [
'firstIdPart' => rand(1, \PHP_INT_MAX),
'secondIdPart' => rand(1, \PHP_INT_MAX),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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\UX\LiveComponent\Tests\Fixtures\Factory;

use Doctrine\ORM\EntityRepository;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Entity1;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\ForeignKeyIdEntity;
use Zenstruck\Foundry\ModelFactory;
use Zenstruck\Foundry\Proxy;
use Zenstruck\Foundry\RepositoryProxy;

use function Zenstruck\Foundry\lazy;

/**
* @extends ModelFactory<ForeignKeyIdEntity>
*
* @method static ForeignKeyIdEntity|Proxy createOne(array $attributes = [])
* @method static ForeignKeyIdEntity[]|Proxy[] createMany(int $number, array|callable $attributes = [])
* @method static ForeignKeyIdEntity|Proxy find(object|array|mixed $criteria)
* @method static ForeignKeyIdEntity|Proxy findOrCreate(array $attributes)
* @method static ForeignKeyIdEntity|Proxy first(string $sortedField = 'id')
* @method static ForeignKeyIdEntity|Proxy last(string $sortedField = 'id')
* @method static ForeignKeyIdEntity|Proxy random(array $attributes = [])
* @method static ForeignKeyIdEntity|Proxy randomOrCreate(array $attributes = []))
* @method static ForeignKeyIdEntity[]|Proxy[] all()
* @method static ForeignKeyIdEntity[]|Proxy[] findBy(array $attributes)
* @method static ForeignKeyIdEntity[]|Proxy[] randomSet(int $number, array $attributes = []))
* @method static ForeignKeyIdEntity[]|Proxy[] randomRange(int $min, int $max, array $attributes = []))
* @method static EntityRepository|RepositoryProxy repository()
* @method ForeignKeyIdEntity|Proxy create(array|callable $attributes = [])
*/
class ForeignKeyIdEntityFactory extends ModelFactory
{
protected static function getClass(): string
{
return ForeignKeyIdEntity::class;
}

protected function getDefaults(): array
{
return ['id' => lazy(static fn () => new Entity1())];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?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\UX\LiveComponent\Tests\Unit\Hydration;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\UX\LiveComponent\Hydration\DoctrineEntityHydrationExtension;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\CompositeIdEntity;
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\ForeignKeyIdEntity;
use Symfony\UX\LiveComponent\Tests\Fixtures\Factory\CompositeIdEntityFactory;
use Symfony\UX\LiveComponent\Tests\Fixtures\Factory\ForeignKeyIdEntityFactory;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;

class DoctrineEntityHydrationExtensionTest extends KernelTestCase
{
use Factories;
use ResetDatabase;

public function testCompositeId(): void
{
$compositeIdEntity = CompositeIdEntityFactory::createOne()->save()->object();

/** @var DoctrineEntityHydrationExtension $extension */
$extension = self::getContainer()->get('ux.live_component.doctrine_entity_hydration_extension');

self::assertSame(
$compositeIdEntity,
$extension->hydrate($extension->dehydrate($compositeIdEntity), CompositeIdEntity::class)
);
}

public function testForeignKeyId(): void
{
$foreignKeyIdEntity = ForeignKeyIdEntityFactory::createOne()->save()->object();

/** @var DoctrineEntityHydrationExtension $extension */
$extension = self::getContainer()->get('ux.live_component.doctrine_entity_hydration_extension');

$dehydrated = $extension->dehydrate($foreignKeyIdEntity);

self::assertSame($foreignKeyIdEntity->id->id, $dehydrated);
self::assertSame($foreignKeyIdEntity, $extension->hydrate($dehydrated, ForeignKeyIdEntity::class));
}
}