Mapping Doctrine2 Entities in Symfony2

28 December 2011

If you’re reading this, you’re probably at the point where you’ve got your Symfony project setup and your routing / controllers / views are ready to go. Now you’re looking to add your first form, and you want it to interact with tables in your database.

This is part one of a two-part walkthrough, where we will be adding Users to our database and sorting them into Groups, using a form in our application. If you’d like to follow along, please use the following file to create the database tables used in this example: db_users_and_groups.sql

Let’s get started!

We’ll begin with how to map our entities. First off, it is important to think of Entities in the singular form. An Entity represents a single row in your table. If you have a table named “Groups”, your Entity used to represent it would be named “Group”.

This example will not be walking through the specifics of mapping each property, but rather showing the result of a mapped table and pointing out important information. The annotations are very straight forward, so let’s jump in…

Here is our Users table structure:

CREATE TABLE  `Users` (
	`id` int NOT NULL AUTO_INCREMENT,
	`last_name` varchar(255) NOT NULL,
	`first_name` varchar(255) NOT NULL,
	`middle_name` varchar(255) DEFAULT NULL,
	`date_added` DATETIME,
	`fk_group_id` int NOT NULL,
	PRIMARY KEY (`id`),
	FOREIGN KEY (`fk_group_id`) references Groups(`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Here is how the User Entity looks when finished.
Located in WalkthroughBundle/Entity/User.php

<?php

namespace kurtfunai\WalkthroughBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="Users")
 */
class User {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

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

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

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

	/**
     * @ORM\Column(name="date_added", type="datetime")
     */
    protected $dateAdded;

    /**
    * @ORM\ManyToOne(targetEntity="kurtfunai\WalkthroughBundle\Entity\Group", inversedBy="members")
    * @ORM\JoinColumn(name="fk_group_id", referencedColumnName="id")
    */
    protected $group;

    // Bunch of Setters/Getters for our properties...
}

View file on [Github][entity_user] [entity_user]: http://github.com/kurtfunai/WalkthroughBundle/blob/master/Entity/User.php

######Things to note:

  • That Doctrine2 Entities are mapped through Docblock Annotations.

  • We declare that it is an entity, and the associated table name, before the class.

/**
 * @ORM\Entity
 * @ORM\Table(name="Users")
 */
class User {
  • How we declare the id with Auto Increment.
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;
  • How we specify a column name, type, and other attributes
/**
 * @ORM\Column(name="first_name", type="string", length="255")
 */
protected $firstName;

If you’re looking for information on how to map different data types, it can be found here

  • That we allow our $middleName property to be nullable.

  • That we’re using $group to represent our Foreign Key relationship to the Groups table.

/**
 * @ORM\ManyToOne(targetEntity="kurtfunai\WalkthroughBundle\Entity\Group", inversedBy="members")
 * @ORM\JoinColumn(name="fk_group_id", referencedColumnName="id")
 */
protected $group;

Now, you’ve probably got some questions about how this is setup, and how we decided to use the ManyToOne annotation. For the sake of simplicity, we are mapping a OneToMany relationship - one group to many users. Because of this, our User Entity is on the Many side of the relationship. With Doctrine2, in a OneToMany relationship, the “Many” is always the owning side. If you’re looking for more information on how this is configured, or about ManyToMany relationships, it can be found here.

In this case, $group will represent an entire Group Entity. We are specifying the targeted entity kurtfunai\WalkthroughBundle\Entity\Group, and the property that will be the inverse side of the relationship - $members.

We still haven’t actually created our Group Entity yet so…

Let’s get mapping our Groups table

This is how our Groups table is defined in the database:

CREATE TABLE  `Groups` (
	`id` int NOT NULL AUTO_INCREMENT,
	`name` varchar(255) NOT NULL,
	PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Now let’s take a look at how the Group Entity looks when finished.
Located in WalkthroughBundle/Entity/Group.php

<?php

namespace kurtfunai\WalkthroughBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="Groups")
 */
class Group {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="name", type="string", length="255")
     */
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity="kurtfunai\WalkthroughBundle\Entity\User", mappedBy="group")
     */
    protected $members;

    /**
     * Creates a Doctrine Collection for members.
     */
    public function __construct()
    {
        $this->members = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Override toString() method to return the name of the group
     * @return string name
     */
    public function __toString()
    {
        return $this->name;
    }

	// Bunch of Setters/Getters for our properties...
}

View file on Github

######Things to note about how this is mapped:

  • In our __construct() for this class, we’re assigning $members to be a Doctrine Collection. This will be a collection of the Users in the Group.
/**
 * Creates a Doctrine Collection for members.
 */
public function __construct()
{
    $this->members = new \Doctrine\Common\Collections\ArrayCollection();
}
  • We override __toString() to return the $this->name;. We’ll get to see the reason why in ‘Part 2 - Forms and Validation’.
  • That we use $members to map the inverse side of our relationship with the User Entity.
/**
 * @ORM\OneToMany(targetEntity="kurtfunai\WalkthroughBundle\Entity\User", mappedBy="group")
 */
protected $members;

In our User Entity, we mapped the ManyToOne side. Now we’ve mapped our OneToMany side, specifying the targeted entity, kurtfunai\WalkthroughBundle\Entity\User and the property on the User Entity that maps the relationship.

You may be wondering why we have added the $members property when we’ve already mapped the Foreign Key relationship on our User Entity. With Doctrine2, you can map both sides of the relationship, allowing us to grab all Users belonging to a Group. While this is not necessary, it is useful to know.

That’s it, our Entities for this example are mapped! Now we’ve got to use them to build our Forms, and place information into the database.

On to Part 2 - Forms and Validation

blog comments powered by Disqus