Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend from Doctrine-Entity without Discriminator-Column

The challenge:

I want to re-use an existing entity of a third party bundle with class-inheritance in that way, that still only one table remains and no extra stuff will be necessary. That means: no discriminator-column and no JOINs. Instead only the final most inherited class should be queryable, add some properties to the base entity and just use one table containing all columns, that are added to the entity from itself and thru inheritance.

To be clear here: I am not interested in classic table inheritance. I just want to extend a base class with additional columns in that way, that the table in the database represents the sum of all needed columns.

There is no need to be able to create an instance of the base entity.

For those who are interested, i explain the reason below.

Base entity of third party library:

Sylius\UserEntity (TableName: "sylius_user")
============================================
ColA, ColB, ColC

My class:

MyProject\UserEntity : Sylius\UserEntity (TableName: "user") <---- overwrite the base table name
========================================
ColD, ColE, ColF

The model above represents the final approach: my user entity extends syslius' user entity and should be persisted in and queried from the table "user" (instead of both "user" AND "sylius_user"), which contains all columns of the final extended entity:

The Table-Structure:

Thus, only one table would exist in my szenario.

Table "user":
=============
ColA, ColB, ColC, ColD, ColE, ColF

The first 3 columns are properties in base entity, and the last 3 columns are properties in "my" user entity which gots the first three thru inheritance.

What i did:

My user class looks like so:

use \Sylius\Component\User\Model\User as BaseUser;

/**
 * User
 *
 * @ORM\Entity(repositoryClass="MyAppName\UserBundle\Repository\UserRepository")
 * @ORM\Table(name="user", uniqueConstraints= ... */
class User extends BaseUser 
{

The UserRepository:

class UserRepository extends EntityRepository
{
    /**
     * @param string $usernameOrEmail
     * @return User
     */
    public function findByUsernameOrEmail($usernameOrEmail)
    {
        $qb = $this
            ->createQueryBuilder('u')
            ->where('u.username = :search OR u.email = :search')
            ->setParameter('search', $usernameOrEmail);

        try {
            return $qb->getQuery()->getSingleResult();
        }
        catch(NoResultException $e) {
            return null;
        }
    }
}

This query results in selecting columns from the table "user", but the query tries to select the columns twice each with a separate table alias. The resulting query fails because it is broken.

What Doctrine does here, looks like so:

SELECT s0.ColA, s0.ColB, s0.ColC, 
u0.ColD, u0.ColE, u0.ColF
FROM user u0

As everyone can see, an additional table alias "s0" is used here without to reference it with a table. What i wanted doctrin to do, is:

SELECT u0.ColA, u0.ColB, u0.ColC, 
u0.ColD, u0.ColE, u0.ColF
FROM user u0

Ho to achieve this?

For those who are interested in the purpose of this task:

We want to add sylius bundles to our long existing symfony application having already an own user-bundle with user model class and existing data out there.

So we'd like to extend the user class of sylius with adding our own properties to build a "bridge" between the sylius class and our user class. The difference between both is slim on the property-side and lies in only a few columns to rename and add some special properties and methods. But we have lots of relations from our user class to other entities, which wouldn't be an issue if we could doctrine get to ignore the table inheritance staff and just act as a plain class-re-using-thing here.

like image 910
itinance Avatar asked Mar 21 '16 13:03

itinance


1 Answers

Is the base class a MappedSuperclass?

As long as the base class is defined as MappedSuperclass, you can simply extend the class and define the extending class as Entity.

Sylius defines the entities as a MappedSuperclass by default. A subscriber (LoadOrmMetadataSubscriber) passes each entity and sets MappedSuperclass to false if necessary, meaning it changes the MappedSuperclass to an Entity when the class is defined in the config.

like image 158
rvanlaarhoven Avatar answered Oct 09 '22 01:10

rvanlaarhoven