Soundz
Soundz

Reputation: 1300

Doctrine Entity extending another Entity

Hi I read this article http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html yet I'm not quiet sure how to accomplish the following:

I have a "user"-Table, a "man"-Table and a "woman"-table. I want my php classes Man and Woman extend the User Object.

Annotationmapping:

    namespace Core\Entity;

    use Doctrine\ORM\Mapping as ORM;

    /**
     * User
     *
     * @ORM\Table(name="user", uniqueConstraints={@ORM\UniqueConstraint(name="email_UNIQUE", columns={"email"}), @ORM\UniqueConstraint(name="username_UNIQUE", columns={"username"})})
     * @ORM\Entity
     */
    class User
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer", nullable=false)
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="IDENTITY")
         */
        private $id;

        /**
         * @var string
         *
         * @ORM\Column(name="first_name", type="string", length=255, nullable=true)
         */
        private $firstName;

        /**
         * @var string
         *
         * @ORM\Column(name="middle_name", type="string", length=255, nullable=true)
         */
        private $middleName;

        /**
         * @var string
         *
         * @ORM\Column(name="last_name", type="string", length=255, nullable=true)
         */
        private $lastName;

        /**
         * @var string
         *
         * @ORM\Column(name="username", type="string", length=255, nullable=false)
         */
        private $username;

        /**
         * @var string
         *
         * @ORM\Column(name="email", type="string", length=255, nullable=false)
         */
        private $email;

        /**
         * @var string
         *
         * @ORM\Column(name="password", type="string", length=255, nullable=false)
         */
        private $password;

        /**
         * @var \DateTime
         *
         * @ORM\Column(name="created_at", type="datetime", nullable=true)
         */
        private $createdAt;

        /**
         * @var \DateTime
         *
         * @ORM\Column(name="updated_at", type="datetime", nullable=false)
         */
        private $updatedAt;

        /**
         * @var \DateTime
         *
         * @ORM\Column(name="last_login", type="datetime", nullable=false)
         */
        private $lastLogin;

        /**
         * @var string
         *
         * @ORM\Column(name="login_hash", type="string", length=255, nullable=true)
         */
        private $loginHash;

        /**
         * @var boolean
         *
         * @ORM\Column(name="is_premium", type="boolean", nullable=false)
         */
        private $isPremium = '0';

        /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\ManyToMany(targetEntity="Core\Entity\Bill", inversedBy="user")
         * @ORM\JoinTable(name="user_has_bill",
         *   joinColumns={
         *     @ORM\JoinColumn(name="user_id", referencedColumnName="id")
         *   },
         *   inverseJoinColumns={
         *     @ORM\JoinColumn(name="bill_id", referencedColumnName="id")
         *   }
         * )
         */
        private $bill;

        /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\ManyToMany(targetEntity="Core\Entity\Picture", inversedBy="user")
         * @ORM\JoinTable(name="user_has_picture",
         *   joinColumns={
         *     @ORM\JoinColumn(name="user_id", referencedColumnName="id")
         *   },
         *   inverseJoinColumns={
         *     @ORM\JoinColumn(name="picture_id", referencedColumnName="id")
         *   }
         * )
         */
        private $picture;

        /**
         * Constructor
         */
        public function __construct()
        {
            $this->bill = new \Doctrine\Common\Collections\ArrayCollection();
            $this->picture = new \Doctrine\Common\Collections\ArrayCollection();
        }

    }

Woman:

namespace Core\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Woman
 *
 * @ORM\Table(name="woman", indexes={@ORM\Index(name="fk_woman_user1_idx", columns={"user_id"})})
 * @ORM\Entity
 */
class Woman
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $id;

    /**
     * @var \Core\Entity\User
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\OneToOne(targetEntity="Core\Entity\User")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     * })
     */
    private $user;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Core\Entity\Cart", inversedBy="woman")
     * @ORM\JoinTable(name="woman_has_cart",
     *   joinColumns={
     *     @ORM\JoinColumn(name="woman_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="cart_id", referencedColumnName="id")
     *   }
     * )
     */
    private $cart;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Core\Entity\Interest", inversedBy="woman")
     * @ORM\JoinTable(name="woman_has_interest",
     *   joinColumns={
     *     @ORM\JoinColumn(name="woman_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="interest_id", referencedColumnName="id")
     *   }
     * )
     */
    private $interest;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Core\Entity\Man", inversedBy="woman")
     * @ORM\JoinTable(name="woman_has_man",
     *   joinColumns={
     *     @ORM\JoinColumn(name="woman_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @ORM\JoinColumn(name="man_id", referencedColumnName="id")
     *   }
     * )
     */
    private $man;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->cart = new \Doctrine\Common\Collections\ArrayCollection();
        $this->interest = new \Doctrine\Common\Collections\ArrayCollection();
        $this->man = new \Doctrine\Common\Collections\ArrayCollection();
    }

}

The man mapping looks (for now) the same as the woman.

Here a little Snippet from MysqlWorkbench http://s14.directupload.net/images/131013/fbg7okyn.png

The basic idea is this:

Men and Women share some common logic and individual logic. For example take a login. Men and Women need an email and a password to log in. Since it's redundant to implement the same login logic twice I thought of creating a more abstract class, the User, which is where I want to put everything which applies to men and women, like name, email, password, login logic etc...

This is where it gets tricky with doctrine. Men and women will have individual fields to store in the database, yet they still need their "common data"(name, password etc...), so a simple class Man extends User might not work out correctly. I store the corresponding id of the User on men and women. So there is an identification.

What I had in mind is, if I do $men->getPassword() it should use the getPassword()function of the corresponding User object.

I hope I cleared up my intend.

Kind Regards and thank you for digging through.

Upvotes: 0

Views: 385

Answers (1)

Sam
Sam

Reputation: 16455

i have done this what you're looking for in one of my projects once, It's done not too good code wise, but the mapping is fine ;) Please check this link

  • Item.php.dist would be your User Entity
  • (Property|Vehicle).php.dist would be your Man / Women Entity
  • Please notice that the Property Discriminator Mapping is missing within the code examples. I do it differently in the application ;)

Ultimately you wouldn't want to have separate "Tables" on your SQL Server. It all belongs to the Superclass "User" and therefore belongs to the User-Table. You will extends the UserTable and use DiscriminatorMapping to map specific entities.

Note: A Man can not be editted to become a Woman! You'd have to kill the man and give birth to a woman :P

Imagina this Model:

User
  *id
  -name
  -surname

Man extends User
  -pc_power

Woman extends User
  -nail_color

Your DB-Schema would look like this:

Table User:
  *id (pk)
  -discriminator (not nullable) (value: man or woman)
  -name (not nullable)
  -surname (not nullable)
  -pc_power (nullable as far as DB is concerned)
  -nail_color (nullable as far as DB is concerned)

You do not need 3 tables to mod your models like this. It makes literally no sense to do this. It just slows your Queries down by quite a bit.

Now a Dataset could look like this:

A Man: (1, man, john, doe, 4ghz, null)
A Woman: (2, woman, john, doe, null, pink)

Now on Doctrines side of things you do Queries against the USER-Entity

$entity = $userRepository->find(1);
echo get_class($entity); // returns "Man"

$entity = $userRepository->find(2);
echo get_class($entity); // returns "Woman"

Does that make things more clear, because otherwise i'm simply unable to help you :P

Upvotes: 2

Related Questions