开发者

How to change and entity type in Doctrine2 CTI Inheritance

How (if possible at all) do you change the entity type with Doctrine2, using it's Class Table Inheritance?

Let's say I have a Person parent class type and two inherited types Employe and Client. My system allows to create a Person and specify it开发者_运维知识库's type - that's fairly easy to implement - but I'd also like to be able to change the person from an Employe to a Client, while maintaining the Person-level information (it's id and other associated records).

Is there a simple way to do this with Doctrine2?


I was looking for this behaviour yesterday also.

In the end, after speaking with people in #doctrine on freenode, I was told that it is not possible.

If you want to do this, then you have to go through this:

Upgrading a User

  1. Grab the Person Entity.
  2. Update the discrimator column so that it is no longer a 'person' and change it to 'employee'
  3. Create a corresponding row inyour Employee table for this inheritance.

Removing Inheritance

Likewise if you want to remove inheritance, you have to..

  1. Grab the Person Entity.
  2. Update the discrimnator column so that it is no longer an 'employee' and change it to a 'person'.
  3. Delete the corresponding row in your Employee table. (Yes you have to delete it, just change the discrimator coumn is not sufficient).

This might be 7 months late, but it is at least the correct answer for anything else looking to suport such a feature.


PHP doesn't have support for object casting, so Doctrine doesn't support it. To workaround the problem I write this static method into parent classes:

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)) && method_exists($newObj, 'set' . ucfirst($property))) {
            $newObj->{'set' . ucfirst($property)}($obj->{'get' . ucfirst($property)}());
        }
    }

    return $newObj;
}

You can create this method in class Person and use it to cast from Employe to Client and viceversa:

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

Now, if you want, you can remove the $employe entity.


You could do something like this though:

This Trait can be used on your Repository class:

namespace App\Doctrine\Repository;

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]
        );
    }
}

There still might be some extra work you need to put in, like clearing values in fields that are only present on one of your sub-classes


In Doctrine2, when you have your parent entity class, Person set as:

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

and sub classes such as Client set as:

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

when you instantiate Person as:

$person = new Person();

Doctrine2 checks your @DiscriminatorMap statement (above) for a corresponding mapping to Person and when found, creates a string value in the table column set in @DiscriminatorColumn above.

So when you decide to have an instance of Client as:

$client = new Client();

Following these principles, Doctrine2 will create an instance for you as long as you have declared the parameters in the @DiscriminatorMap. Also an entry will be made on the Person table, in the discr column to reflect that type of entity class that has just been instantiated.

Hope that helps. It's all in the documentation though


i use this method

trait DiscriminatorTrait
{
   // ...

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

and i use call like this after :

        $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

And it is work for me

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜