Doctrine Relationship Management Between Entities


 Doctrine Relationship Management Between Entities  
 

As we are using Doctrine ORM for easing our database layer programming, it’s very important to understand how to establish the doctrine relationship between entities and do it in proper way to build a robust relational database design. Here by the term ‘relationship’, I meant the x-to-x relations, which can take form of one to one,one to many (many to one) or many to many relationship etc. I am assuming you have started using doctrine and have some basic idea how to declare and perform Doctrine CRUD operations at least.

Ways To Establish Relationship:

Well, It is always mandatory that, you need to have a complete synchronization between your entities’ annotation/property declaration and your database structure. You can though can do that manually, its time-consuming and have possibility of buggy implementation. Sometimes, you will may find yourself messing around, but can’t exactly catch the reason of a raised issue. waste of time, mental torture.
doctrine relational table
So, it is strongly recommended that you implement on only one side and use php script or CLI tool for transformation between doctrine entities and database.

I usually like to establish the doctrine relationship in entities and then update the database schema and will show this way here. There is some specific benefits as well, that you will know later on this article. Keep reading on.

One To One Relationship:

Lets assume, we have two entity named ‘Users’ And ‘Profiles’. It can be a good example of one to one relationship. As every user has only one profile and each profile can be associated with only one user. Now, so said, as a rule of thumb, we will have to add an identity foreign key(as per database language) to one table that linked to the another table.

Now which table need to contain and link to another? To decide this, first think, which entity comes first and which is optional. The secondary entity need to have a link to its primary entity. In this example scenario, from general knowledge, we know, first we need to have a user in our system who can later create a profile. So, user is primary here and profile is secondary. So, in profile entity, we will have property/database column definition like as follows:

/**
 * UserProfile
 *
 * @Table(name="user_profile")
 * @Entity
 */
class UserProfile
{
    /**
     * @var integer $id
     *
     * @Column(name="id", type="integer", nullable=false)
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    //other declarations.....

    /**
     * @var Users
     *
     * @OneToOne(targetEntity="Users")
     * @JoinColumns({
     *   @JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
     * })
     */
    private $user;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    //other getter/setter declarations.....

    /**
     * Set user
     *
     * @param Users $user
     * @return UserProfile
     */
    public function setUser(\Users $user = null)
    {
        $this->user = $user;
        return $this;
    }

    /**
     * Get user
     *
     * @return Users 
     */
    public function getUser()
    {
        return $this->user;
    }
}

As you can see above, this declaration will create a foreign key on ‘profiles’ and link to ‘users’ table’s ‘id’ column, Now when we create a new profile, we can set the corresponding user with use of setter property and we should be fine. Also, if you check your database, you will find that, a new unique key is also generated on ‘user_id’ column, which is because of the ‘OneToOne’ relation declaration. See the below example of saving a Profile entity:

$id = 1;
$user = $em->getRepository("Users")->find($id);

$profile = new UserProfile();
$profile->setFirstName("test first name");
$profile->setLastName("test last name");
$profile->setUser($user);
$em->persist($profile);
$em->flush();

One To Many Doctrine Relationship:

Lets say, your web application is providing specific access level permission to its users. a single role can be assigned to as many users, but each user are associated to only a single role at a time. This is very good example of one to many(role to user) or many to one(users to role). And here its easy to guess that, we will need to add a property in ‘user’ entity which link to ‘role’ entity’s ‘id’ property as below:

/**
 * Users
 *
 * @Table(name="users")
 * @Entity
 */
class Users
{
    /**
     * @var integer $id
     *
     * @Column(name="id", type="integer", nullable=false)
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    //other declarations.....

    /**
     * @var Roles
     *
     * @ManyToOne(targetEntity="Roles")
     * @JoinColumns({
     *   @JoinColumn(name="role_id", referencedColumnName="id")
     * })
     */
    private $role;

    //other getter/setter declarations.....

    /**
     * Set role
     *
     * @param Roles $role
     * @return Users
     */
    public function setRole(\Roles $role = null)
    {
        $this->role = $role;
        return $this;
    }

    /**
     * Get role
     *
     * @return Roles 
     */
    public function getRole()
    {
        return $this->role;
    }

after you update the database schema with the above annotated definition, you should be able to see the foreign key on ‘users’ table. But you will notice, there is no unique key generated. to establish the relationship properly while creating new user, you can follow the code example below:

$role = $em->getRepository("Roles")->findOneBy(array("name"=>"admin"));

$user = new Users();
$user->setUserName("testusername");
$user->setEmail("testemail@domain.com");
$user->setRole($role);

$em->persist($user);
$em->flush();

Many To Many Relationship:

New story for new relationship. Suppose, our users do have some skills. A single user can have multiple skills and in the similar way, a single skill can be acquired by multiple users. So, here we are talking about a Many to Many relationship between users and skills.

From relational database design basics, we know, in such scenario, in database we will have to establish a new table which connects to users table and expertise table. And a user/skill pair will be unique on that table. Luckily, as we are modifying entities instead of database, its became very much easy for us for doctrines strong abstraction support.

Lets see the doctrine relationship establishment in users entity to contains multiple skills.

use Doctrine\ORM\Mapping as ORM;

/**
 * Users
 *
 * @Table(name="users")
 * @Entity
 */
class Users
{
     //Other declarations

     /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * @ManyToMany(targetEntity="Expertises", inversedBy="users")
     * @JoinTable(name="users_expertises")
     **/
    private $expertises;

    public function __construct() {
        $this->expertises = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get expertises
     *
     * @return PdExpertises
     */
    public function getExpertises()
    {
        return $this->expertises;
    }
}

//expertise entity

use Doctrine\ORM\Mapping as ORM;

/**
 * Expertises
 *
 * @Table(name="expertises")
 * @Entity
 */
class Expertises
{
     //other declarations.....
     /**
     * @ManyToMany(targetEntity="Users", mappedBy="expertises")
     **/
    private $users;

    public function __construct() {
        $this->users = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get users
     *
     * @return Users
     */
    public function getUsers()
    {
        return $this->users;
    }
}

From the above code, you will see, we haven’t declared any setter method for the properties. Because, we won’t need it. we will be able to perform our task by operating on ArrayCollection very fine. Lets see a basic example usages:

        $expertise = $this->em->getRepository("Expertises")->find(1);
                
        $user = new Users();
        //other properties set        
        $user->getExpertises()->add($expertise);
        
        $this->em->persist($user);
        $this->em->flush();

Final Words:

So, do some exercises, see how surprisingly doctrine relationship establishment making our life easier to do several operations. If you are having any issue to understand, ask me by commenting. Happy coding :)

Leave a Reply