doctrine-extensions/DoctrineExtensions

[Translateable] + Doctrine Table Inheritance: wrong object class in table "ext_translations"

klemens-u opened this issue · 0 comments

The environment is the same as in #2849.

When using [Translateable] + Doctrine Table Inheritance the wrong class name is saved in the translation database table.

Example: (Sorry Gedmo attributes are missing, we use a trait for that)

#[ORM\InheritanceType('SINGLE_TABLE')] // 'JOINED' does not make much of a difference as most fields are in custom_translations anyways
#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
// Automatically generating the discriminator map is very expensive computation-wise
#[ORM\DiscriminatorMap([
    AbstractPage::class => AbstractPage::class,
    Page::class => Page::class,
])]
class AbstractPage
{
    ...
#[ORM\Entity(repositoryClass: PageRepository::class)]
class Page extends AbstractPage
{
    ...

Now when creating a new "Page" object, the "object_class" field in "ext_translations" reads "App\Entity\AbstractPage" instead of "App\Entity\Page" .

The problem lies somewhere in TranslateableListener::handleTranslatableObjectUpdate() in this section:

            if ($persistNewTranslation) {
                $translation = $translationMetadata->newInstance();
                $translation->setLocale($locale);
                $translation->setField($field);
                if ($ea->usesPersonalTranslation($translationClass)) {
                    $translation->setObject($object);
                } else {
                    $translation->setObjectClass($config['useObjectClass']);
                    $translation->setForeignKey($objectId);
                }
            }

This traces further back to loadMetadataForObjectClass() where the wrong (base class name) is used.

Workaround: overwrite loadMetadataForObjectClass() in a custom TranslatableListener.

config/services.yaml:

  App\EventListener\CustomTranslatableListener:
    decorates: 'stof_doctrine_extensions.listener.translatable'

src/EventListener/CustomTranslatableListener.php

<?php

namespace App\EventListener;

use Doctrine\Persistence\ObjectManager;
use Gedmo\Translatable\TranslatableListener;

class CustomTranslatableListener extends TranslatableListener
{
    public function loadMetadataForObjectClass(ObjectManager $objectManager, $metadata)
    {
        parent::loadMetadataForObjectClass($objectManager, $metadata);

        $className = $metadata->getName();
        if (isset(self::$configurations[$this->name][$className])) {
            // Always use the actual class name for useObjectClass
            self::$configurations[$this->name][$className]['useObjectClass'] = $className;
        }
    }
}

Anyone knows why the actual class name is not used? This looks like an easy fix.

Thanks & have a nice day.