Skip to content
Open
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
75 changes: 75 additions & 0 deletions tests/Tests/Models/WrappedIntegerPrimaryKey/Category.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\WrappedIntegerPrimaryKey;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;

use function random_int;

#[Entity]
class Category
{
#[Id]
#[Column(type: IntegerIdType::NAME, nullable: false)]
private IntegerId $id;

#[Column]
private string $name;

#[ManyToOne(targetEntity: self::class, inversedBy: 'children')]
private self|null $parent;

/** @var Collection<int, Category> */
#[OneToMany(targetEntity: self::class, mappedBy: 'parent')]
private Collection $children;

public function __construct(
string $name,
self|null $parent = null,
) {
$this->id = new IntegerId(random_int(0, (1 << 10) - 1));
$this->name = $name;
$this->parent = $parent;
$this->children = new ArrayCollection();

$parent?->addChild($this);
}

public function getId(): IntegerId
{
return $this->id;
}

public function getName(): string
{
return $this->name;
}

public function getParent(): self|null
{
return $this->parent;
}

/** @return ReadableCollection<int, Category> */
public function getChildren(): ReadableCollection
{
return $this->children;
}

/** @internal */
public function addChild(self $category): void
{
if (! $this->children->contains($category)) {
$this->children->add($category);
}
}
}
22 changes: 22 additions & 0 deletions tests/Tests/Models/WrappedIntegerPrimaryKey/IntegerId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\WrappedIntegerPrimaryKey;

class IntegerId
{
public function __construct(private int $value)
{
}

public function getValue(): int
{
return $this->value;
}

public function __toString(): string
{
return (string) $this->value;
}
}
63 changes: 63 additions & 0 deletions tests/Tests/Models/WrappedIntegerPrimaryKey/IntegerIdType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\WrappedIntegerPrimaryKey;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Doctrine\Tests\Mocks\CompatibilityType;
use LogicException;

final class IntegerIdType extends Type
{
use CompatibilityType;

public const NAME = 'integer_id';

public function convertToPHPValue(
mixed $value,
AbstractPlatform $platform,
): IntegerId|null {
if ($value === null) {
return null;
}

if ($value instanceof IntegerId) {
return $value;
}

return new IntegerId($value);
}

public function convertToDatabaseValue(
mixed $value,
AbstractPlatform $platform,
): int|null {
if ($value === null) {
return null;
} elseif ($value instanceof IntegerId) {
return $value->getValue();
} else {
throw new LogicException('Unexpected value: ' . $value);
}
}

public function getSQLDeclaration(
array $column,
AbstractPlatform $platform,
): string {
return $platform->getIntegerTypeDeclarationSQL(['unsigned' => true]);
}

private function doGetBindingType(): ParameterType|int
{
return ParameterType::INTEGER;
}

public function getName(): string
{
return self::NAME;
}
}
107 changes: 107 additions & 0 deletions tests/Tests/ORM/Persisters/IntegerIdPersisterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Persisters;

use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Types\Type as DbalType;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMSetup;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Tools\SchemaValidator;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Models\WrappedIntegerPrimaryKey\Category;
use Doctrine\Tests\Models\WrappedIntegerPrimaryKey\IntegerIdType;
use Doctrine\Tests\OrmTestCase;

final class IntegerIdPersisterTest extends OrmTestCase
{
private EntityManager|null $entityManager = null;

public function testEagerFetchMode(): void
{
$entityManager = $this->createEntityManager();

$this->createDummyBlogData($entityManager, 1, 1);

/** @var Category[] $topCategories */
$topCategories = $entityManager->createQueryBuilder()
->select('category')
->from(Category::class, 'category')
->andWhere('category.parent IS NULL')
->getQuery()
->setFetchMode(Category::class, 'children', ClassMetadata::FETCH_EAGER)
->getResult();

self::assertCount(1, $topCategories);

foreach ($topCategories as $topCategory) {
// the real count of children in db is 1
self::assertCount(
1,
$entityManager->createQueryBuilder()
->select('category')
->from(Category::class, 'category')
->andWhere('category.parent = :parent')
->setParameter('parent', $topCategory->getId()->getValue())
->getQuery()
->getResult(),
);

// our collection is initialized
self::assertTrue($topCategory->getChildren()->isInitialized());

// but fails to load the children
self::assertSame(1, $topCategory->getChildren()->count());
}
}

private function createDummyBlogData(
EntityManager $entityManager,
int $categoryCount = 1,
int $categoryParentsCount = 0,
): void {
for ($h = 0; $h < $categoryCount; $h++) {
$categoryParent = null;

for ($i = 0; $i < $categoryParentsCount; $i++) {
$categoryParent = new Category('CategoryParent#' . $i, $categoryParent);
$entityManager->persist($categoryParent);
}

$category = new Category('Category#' . $h, $categoryParent);
$entityManager->persist($category);
}

$entityManager->flush();
$entityManager->clear();
}

private function createEntityManager(): EntityManager
{
if ($this->entityManager !== null) {
return $this->entityManager;
}

$config = ORMSetup::createAttributeMetadataConfiguration([__DIR__ . '/../../Models/WrappedIntegerPrimaryKey'], isDevMode: true);

if (! DbalType::hasType(IntegerIdType::NAME)) {
DbalType::addType(IntegerIdType::NAME, IntegerIdType::class);
}

$connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true], $config);
$entityManager = new EntityManagerMock($connection, $config);

$schemaTool = new SchemaTool($entityManager);
$schemaTool->createSchema($entityManager->getMetadataFactory()->getAllMetadata());

$schemaValidator = new SchemaValidator($entityManager);
$schemaValidator->validateMapping();

$this->entityManager = $entityManager;

return $entityManager;
}
}
Loading