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

Inheritance does not work properly after "Fix OneToManyPersister::deleteEntityCollection missing discriminator column/value." #11551

Open
konrad-czernecki-esky opened this issue Jul 15, 2024 · 1 comment

Comments

@konrad-czernecki-esky
Copy link

konrad-czernecki-esky commented Jul 15, 2024

Bug Report

Q A
BC Break no
Version 2.19.6

Summary

After this fix #11500, I have a problem with the SINGLE_TABLE inheritance type, because when I set a new collection (in order to replace the previous one) in my one-to-many relation, the data in the old collection is not deleted.

Current behavior

The collection that is being replaced has not been replaced (old data remains + new appears)

How to reproduce

*Pseudocode

enum ComponentTypeEnum: string
{
    case EXAMPLE_COMPONENT = 'example';
    case EXAMPLE_COMPONENT2 = 'example_2';
}

#[ORM\Entity]
#[ORM\Table(name: 'components')]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap(Component::ENTITY_MAP)]
abstract class Component extends AbstractComponent
{
    public const ENTITY_MAP = [
        ComponentTypeEnum::EXAMPLE_COMPONENT->value => ExampleComponent::class,
        ComponentTypeEnum::EXAMPLE_COMPONENT2->value => ExampleComponent2::class,
        //and so on
    ];

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: Page::class, inversedBy: 'components')]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    public ?Page $page = null;

    /**
     * @var mixed[]
     */
    #[ORM\Column(type: 'json', nullable: false)]
    public array $data = [];

    public function getId(): ?int
    {
        return $this->id;
    }
}
#[Entity]
class ExampleComponent extends Component
{
    public ComponentTypeEnum $type = ComponentTypeEnum::ExampleComponent;
}
#[ORM\Entity]
#[ORM\Table(name: 'pages')]
class Page
{
    #[ORM\Id]
    #[ORM\Column(type: 'uuid')]
    private string $id;

    #[ORM\OneToMany(
        mappedBy: 'page',
        targetEntity: Component::class,
        cascade: ['persist', 'remove'],
        orphanRemoval: true
    )]
    #[ORM\OrderBy(['id' => 'ASC'])]
    private Collection $components;

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

    public function setId(string $id): self
    {
        $this->id = $id;

        return $this;
    }

    public function getComponents(): array
    {
        return $this->components->toArray();
    }

    public function addComponent(Component $component): self
    {
        if (!$this->components->contains($component)) {
            $this->components->add($component);
            $component->setPage($this);
        }

        return $this;
    }

    public function setComponents(array $components): self
    {
        $this->components = new ArrayCollection();

        foreach ($components as $component) {
            $this->addComponent($component);
        }

        return $this;
    }
}

Reproduction:

$page = new Page();
$page->setId('eab311cf-9854-4872-a2c6-73376b7b9936');
$page->addComponent(new ExampleComponent());

$entityManger->persist($page);
$entityManager->flush();

$entityManager->clear();

$page = $pageRepository->findOneById('eab311cf-9854-4872-a2c6-73376b7b9936');
$page->setComponents([new ExampleComponent()]);

$entityManger->persist($page);
$entityManager->flush();

$entityManager->clear();

$page = $pageRepository->findOneById('eab311cf-9854-4872-a2c6-73376b7b9936');

assertCount(1, $page->getComponents()) //will fail, there are two components in DB, but should be only one

The reason is that the SQL query generated in \Doctrine\ORM\Persisters\Collection\OneToManyPersister::deleteEntityCollection has a null value in the "type" column.

DELETE FROM components WHERE page_id = eab311cf-9854-4872-a2c6-73376b7b9936 AND type = null

2024-07-15_15-00

2024-07-15_15-15

Expected behavior

Old collection should be deleted and only new data should remain. Before v2.19.6 everything works well.

@gitbugr
Copy link
Contributor

gitbugr commented Aug 1, 2024

The issue is caused by your targetEntity being the abstract Component class...

I think in this scenario, the SQL should be type IN ('example', 'example_2') to continue to be safe. I'll take a look and push up a PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants