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

JoinedSubclassPersister pass identifier types on delete #7322

39 changes: 21 additions & 18 deletions lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Utility\IdentifierFlattener;
use Doctrine\ORM\Utility\PersisterHelper;
use function array_map;
use function array_merge;
use function assert;
use function reset;

/**
Expand Down Expand Up @@ -569,29 +571,12 @@ protected function deleteJoinTableRecords($identifier)
*/
public function delete($entity)
{
$self = $this;
$class = $this->class;
$identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);
$tableName = $this->quoteStrategy->getTableName($class, $this->platform);
$idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform);
$id = array_combine($idColumns, $identifier);
$types = array_map(function ($identifier) use ($class, $self) {
if (isset($class->fieldMappings[$identifier])) {
return $class->fieldMappings[$identifier]['type'];
}

$targetMapping = $self->em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']);

if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) {
return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
}

if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) {
return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
}

throw ORMException::unrecognizedField($targetMapping->identifier[0]);
}, $class->identifier);
$types = $this->getClassIdentifiersTypes($class);

$this->deleteJoinTableRecords($identifier);

Expand Down Expand Up @@ -2089,4 +2074,22 @@ protected function switchPersisterContext($offset, $limit)

$this->currentPersisterContext = $this->limitsHandlingContext;
}

/**
* @return string[]
*/
protected function getClassIdentifiersTypes(ClassMetadata $class) : array
{
$entityManager = $this->em;

return array_map(
static function ($fieldName) use ($class, $entityManager) : string {
$types = PersisterHelper::getTypeOfField($fieldName, $class, $entityManager);
assert(isset($types[0]));

return $types[0];
},
$class->identifier
);
}
}
13 changes: 8 additions & 5 deletions lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,22 +278,25 @@ public function delete($entity)
// If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest.
if ($this->platform->supportsForeignKeyConstraints()) {
$rootClass = $this->em->getClassMetadata($this->class->rootEntityName);
$rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform);
$rootClass = $this->em->getClassMetadata($this->class->rootEntityName);
$rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform);
$rootTypes = $this->getClassIdentifiersTypes($rootClass);

return (bool) $this->conn->delete($rootTable, $id);
return (bool) $this->conn->delete($rootTable, $id, $rootTypes);
}

// Delete from all tables individually, starting from this class' table up to the root table.
$rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform);
$rootTypes = $this->getClassIdentifiersTypes($this->class);

$affectedRows = $this->conn->delete($rootTable, $id);
$affectedRows = $this->conn->delete($rootTable, $id, $rootTypes);

foreach ($this->class->parentClasses as $parentClass) {
$parentMetadata = $this->em->getClassMetadata($parentClass);
$parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform);
$parentTypes = $this->getClassIdentifiersTypes($parentMetadata);

$this->conn->delete($parentTable, $id);
$this->conn->delete($parentTable, $id, $parentTypes);
}

return (bool) $affectedRows;
Expand Down
114 changes: 114 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/GH5988Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type as DBALType;
use Doctrine\Tests\DbalTypes\CustomIdObject;
use Doctrine\Tests\OrmFunctionalTestCase;
use function str_replace;

/**
* Functional tests for the Class Table Inheritance mapping strategy with custom id object types.
*
* @group GH5988
*/
final class GH5988Test extends OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();

if (! DBALType::hasType(GH5988CustomIdObjectHashType::class)) {
DBALType::addType(GH5988CustomIdObjectHashType::class, GH5988CustomIdObjectHashType::class);
}

$this->setUpEntitySchema([GH5988CustomIdObjectTypeParent::class, GH5988CustomIdObjectTypeChild::class]);
}

public function testDelete()
{
$object = new GH5988CustomIdObjectTypeChild(new CustomIdObject('foo'), 'Test');

$this->_em->persist($object);
$this->_em->flush();

$id = $object->id;

$object2 = $this->_em->find(GH5988CustomIdObjectTypeChild::class, $id);

$this->_em->remove($object2);
$this->_em->flush();

self::assertNull($this->_em->find(GH5988CustomIdObjectTypeChild::class, $id));
}
}


class GH5988CustomIdObjectHashType extends DBALType
{
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value->id . '_test';
}
/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return new CustomIdObject(str_replace('_test', '', $value));
}
/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return self::class;
}
}

/**
* @Entity
* @Table
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="type", type="string")
* @DiscriminatorMap({"child" = GH5988CustomIdObjectTypeChild::class})
*/
abstract class GH5988CustomIdObjectTypeParent
{
/**
* @Id
* @Column(type="Doctrine\Tests\ORM\Functional\GH5988CustomIdObjectHashType")
* @var CustomIdObject
*/
public $id;
}


/**
* @Entity
* @Table
*/
class GH5988CustomIdObjectTypeChild extends GH5988CustomIdObjectTypeParent
{
/** @var string */
public $name;

public function __construct(CustomIdObject $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}