Как изменить и тип объекта в наследовании Doctrine2 CTI

#database #doctrine-orm #class-table-inheritance

#База данных #doctrine-orm #class-table-inheritance

Вопрос:

Как (если это вообще возможно) изменить тип объекта в Doctrine2, используя наследование таблицы классов?

Допустим, у меня есть Person тип родительского класса и два унаследованных типа Employe и Client . Моя система позволяет создать Person и указать его тип — это довольно легко реализовать — но я также хотел бы иметь возможность изменять person с Employee на Client, сохраняя при этом информацию на Person уровне (его идентификатор и другие связанные записи).

Есть ли простой способ сделать это с Doctrine2?

Ответ №1:

Вчера я также искал такое поведение.

В конце концов, после разговора с людьми в #doctrine на freenode, мне сказали, что это невозможно.

Если вы хотите это сделать, то вам нужно пройти через это:

Обновление пользователя

  1. Захватите объект Person.
  2. Обновите столбец discrimator, чтобы он больше не был «person» и измените его на «employee»
  3. Создайте соответствующую строку в вашей Employee таблице для этого наследования.

Удаление наследования

Аналогично, если вы хотите удалить наследование, вы должны..

  1. Захватите объект Person.
  2. Обновите столбец discriminator, чтобы он больше не был «сотрудником», и измените его на «person».
  3. Удалите соответствующую строку в вашей Employee таблице. (Да, вы должны удалить его, просто изменить значение discrimator недостаточно).

Это может быть с опозданием на 7 месяцев, но это, по крайней мере, правильный ответ для всего, что хочет поддержать такую функцию.

Комментарии:

1. Потрясающе. Я перестал использовать CTI для этой конкретной проблемы, но это может когда-нибудь понадобиться =)

2. Как насчет обновления объекта? если вы уже загрузили объект User, значение дискриминатора вручную не будет частью уже загруженного объекта user, управляемого unit of work. Таким образом, объект user по-прежнему будет рассматриваться как старый тип. $em-> обновить($user); не работает. Итак, как справиться с этой проблемой?

Ответ №2:

PHP не поддерживает приведение объектов, поэтому Doctrine его не поддерживает. Чтобы обойти проблему, я записываю этот статический метод в родительские классы:

 public static function castToMe($obj) {

    $class = get_called_class();
    $newObj = New $class();

    foreach (get_class_vars(get_class($newObj)) as $property => $value) {
        if (method_exists($obj, 'get' . ucfirst($property)) amp;amp; method_exists($newObj, 'set' . ucfirst($property))) {
            $newObj->{'set' . ucfirst($property)}($obj->{'get' . ucfirst($property)}());
        }
    }

    return $newObj;
}
  

Вы можете создать этот метод в классе Person и использовать его для приведения от Employee к Client и наоборот:

 $employe = New Employe();
$client = Client::castToMe($employe);
  

Теперь, если вы хотите, вы можете удалить объект $employee.

Ответ №3:

Вы могли бы сделать что-то вроде этого, хотя:

Эта особенность может быть использована в вашем классе репозитория:

 namespace AppDoctrineRepository;

trait DiscriminatorTrait
{
    abstract public function getClassMetadata();

    abstract public function getEntityManager();

    private function updateDiscriminatorColumn($id, $class)
    {
        $classMetadata = $this->getClassMetadata();

        if (!in_array($class, $classMetadata->discriminatorMap)) {
            throw new Exception("invalid discriminator class: " . $class);
        }

        $identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];

        $column = $classMetadata->discriminatorColumn["fieldName"];
        $value = array_search($class, $classMetadata->discriminatorMap);

        $connection = $this->getEntityManager()->getConnection();

        $connection->update(
            $classMetadata->table["name"],
            [$column => $value],
            [$identifier => $id]
        );
    }
}
  

Вам все еще может потребоваться некоторая дополнительная работа, например, очистка значений в полях, которые присутствуют только в одном из ваших подклассов

Ответ №4:

В Doctrine2, когда у вас есть родительский класс сущности, Person задайте как:

 /**
 * @Entity
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="discr", type="string")
 * @DiscriminatorMap({"person" = "Person", "employee" = "Employee", , "client" = "Client"})
 */
class Person
{
    // ...
}
  

и подклассы, такие как Client установленные как:

 /** @Entity */
class Client extends Person
{
    // ...
}
  

при создании экземпляра Person как:

 $person = new Person();
  

Doctrine2 проверяет вашу @DiscriminatorMap инструкцию (выше) на наличие соответствующего сопоставления с Person и при обнаружении создает строковое значение в столбце таблицы, указанном в @DiscriminatorColumn выше.

Итак, когда вы решите создать экземпляр Client в качестве:

 $client = new Client();
  

Следуя этим принципам, Doctrine2 создаст для вас экземпляр, если вы объявили параметры в @DiscriminatorMap . Также в Person таблицу в столбце discr будет внесена запись, отражающая тот тип класса объектов, который только что был создан.

Надеюсь, это поможет. Хотя все это есть в документации

Комментарии:

1. Возможно, мой вопрос был недостаточно ясен, но я хотел бы преобразовать объект одного типа в другой тип (т. Е. Клиент -> Сотрудник ), сохранив при этом общие данные, относящиеся к суперклассу (здесь Person). Кроме того, я использую для этого наследование одной таблицы (не нескольких таблиц)

Ответ №5:

я использую этот метод

 trait DiscriminatorTrait
{
   // ...

    public function updateDiscriminatorColumn($id, $class)
    {
        // ... other code here
   
        $connection->update(
            "Person",  // <-- just there i put my parent class 
            [$column => $value],
            [$identifier => $id]
        );
    }
}
  

и я использую подобный вызов после :

         $this->em->getRepository(Client::class)->updateDiscriminatorColumn($cCenter->getId(), Employe::class);
        
        $this->em->close();
    // I update the data directly without going through doctrine otherwise it will create a new Person

        try {
            $query = "
            INSERT INTO Employe (id, /* ... other fields */) 
            VALUES ({$callCenter->getId()}, /* ... other fields */)
        ";
            $results = $this->connection->executeQuery($query)->execute();
        } catch (Exception $exception) {
            echo $exception->getMessage().PHP_EOL;
        }

        $this->em->close();

        // i restart the connection
        /** @var EntityManagerInterface $entityManager */
        $entityManager = $this->em;

        if ($this->em->isOpen() === false) {
            $this->em = $entityManager->create(
                $this->em->getConnection(),
                $this->em->getConfiguration(),
                $this->em->getEventManager()
            );
        }

        // and there a get Employer en update him
       
        $employe = $this->em->getRepository(Employe::class)->find($id);

        $employe->setFirstname($callCenter->getFirstName());

       // other code

  

И это работа для меня