For the Application module, entities are (by convention) stored inside the Entity
directory under the module's source directory. Entity classes live inside the Application\Entity
namespace.
We start with creating the Post
entity. Create the Post.php file under module's Entity
directory. (If you haven't created the Entity directory yet, its the right time to do that.)
Put the following code into the file:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* This class represents a single post in a blog.
* @ORM\Entity
* @ORM\Table(name="post")
*/
class Post
{
// Post status constants.
const STATUS_DRAFT = 1; // Draft.
const STATUS_PUBLISHED = 2; // Published.
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(name="id")
*/
protected $id;
/**
* @ORM\Column(name="title")
*/
protected $title;
/**
* @ORM\Column(name="content")
*/
protected $content;
/**
* @ORM\Column(name="status")
*/
protected $status;
/**
* @ORM\Column(name="date_created")
*/
protected $dateCreated;
// Returns ID of this post.
public function getId()
{
return $this->id;
}
// Sets ID of this post.
public function setId($id)
{
$this->id = $id;
}
// Returns title.
public function getTitle()
{
return $this->title;
}
// Sets title.
public function setTitle($title)
{
$this->title = $title;
}
// Returns status.
public function getStatus()
{
return $this->status;
}
// Sets status.
public function setStatus($status)
{
$this->status = $status;
}
// Returns post content.
public function getContent()
{
return $this->content;
}
// Sets post content.
public function setContent($content)
{
$this->content = $content;
}
// Returns the date when this post was created.
public function getDateCreated()
{
return $this->dateCreated;
}
// Sets the date when this post was created.
public function setDateCreated($dateCreated)
{
$this->dateCreated = $dateCreated;
}
}
In the code above, we have the following things:
Status constants (lines 14 and 15). These constants conveniently represent possible values the
$status
property may receive (1 for Draft, 2 for Published).
Protected properties ($title
, $content
, $dateCreated
, etc.). These are data
that a typical blog post has (see table 12.2 below for reference of properties together with
their brief descriptions).
Please note that for properties we (by convention) use camel-case names (like
$dateCreated
), while for database columns we use "canonicalized" names (in lower-case and with underscores separating words in a name, likedate_created
).
Property | Mapped on Column | Description |
---|---|---|
$id |
id |
Unique ID of this post. |
$title |
title |
Title of this post. |
$content |
content |
Content of this post. |
$status |
status |
Status (draft/published) of this post. |
$dateCreated |
date_created |
Date when this post was created. |
Entity class and its properties are marked with Docblock annotations read by Doctrine ORM at run-time allowing it to know how to map this entity and its properties on the database table and its columns.
Entity class has getter and setter methods (lines 45-102) allowing to access/modify the protected properties (see the table 12.3 for reference of methods and their brief descriptions).
Method | Description |
---|---|
getId() | Returns ID of this post. |
setId($id) | Sets ID of this post. |
getTitle() | Returns title. |
setTitle($title) | Sets title. |
getStatus() | Returns status (draft/published). |
setStatus($status) | Sets status. |
getContent() | Returns post content. |
setContent($content) | Sets post content. |
getDateCreated() | Returns the date when this post was created. |
setDateCreated() | Sets the date when this post was created. |
Note that we do not mark entity class methods with Doctrine annotations. There is just no need to do that. However, you may mark methods with usual comments and non-Doctrine Docblock annotations, if you strongly wish.
By analogy with the Post
entity, we next create the Comment
and the Tag
entity classes in
the Entity directory. To do that, first, create Comment.php file and put the
following code inside of it:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* This class represents a comment related to a blog post.
* @ORM\Entity
* @ORM\Table(name="comment")
*/
class Comment
{
/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;
/**
* @ORM\Column(name="content")
*/
protected $content;
/**
* @ORM\Column(name="author")
*/
protected $author;
/**
* @ORM\Column(name="date_created")
*/
protected $dateCreated;
// Returns ID of this comment.
public function getId()
{
return $this->id;
}
// Sets ID of this comment.
public function setId($id)
{
$this->id = $id;
}
// Returns comment text.
public function getContent()
{
return $this->content;
}
// Sets status.
public function setContent($content)
{
$this->content = $content;
}
// Returns author's name.
public function getAuthor()
{
return $this->author;
}
// Sets author's name.
public function setAuthor($author)
{
$this->author = $author;
}
// Returns the date when this comment was created.
public function getDateCreated()
{
return $this->dateCreated;
}
// Sets the date when this comment was created.
public function setDateCreated($dateCreated)
{
$this->dateCreated = $dateCreated;
}
}
Next, create Tag.php file and put the following code inside of it:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* This class represents a tag.
* @ORM\Entity
* @ORM\Table(name="tag")
*/
class Tag
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(name="id")
*/
protected $id;
/**
* @ORM\Column(name="name")
*/
protected $name;
// Returns ID of this tag.
public function getId()
{
return $this->id;
}
// Sets ID of this tag.
public function setId($id)
{
$this->id = $id;
}
// Returns name.
public function getName()
{
return $this->name;
}
// Sets name.
public function setName($name)
{
$this->name = $name;
}
}
Since the Comment
and Tag
entities are analogous to the Post
entity, we don't provide detailed
description of the code above.
Please note that we do not create an entity for the fourth auxiliary table
post_tag
. That table will be indirectly used further in this chapter when defining relationships between entities.
Now it is time to use annotations to define relationships between entities. If you remember, we have two relationships between our entities:
Post
and Comment
entities are related as "one-to-many";Post
and Tag
entities are related as "many-to-many".In Doctrine, to express a relationship between two entities, you add a private property paired with Docblock annotation.
For detailed information about relationships between entities in Doctrine, please read this page of Doctrine documentation.
First, let's define one-to-many relationship between the Post
and Comment
entities. Modify the Post.php
file and add the following lines:
<?php
// ...
use Doctrine\Common\Collections\ArrayCollection;
use Application\Entity\Comment;
/**
* This class represents a single post in a blog.
* @ORM\Entity
* @ORM\Table(name="post")
*/
class Post
{
// ...
/**
* @ORM\OneToMany(targetEntity="\Application\Entity\Comment", mappedBy="post")
* @ORM\JoinColumn(name="id", referencedColumnName="post_id")
*/
protected $comments;
/**
* Constructor.
*/
public function __construct()
{
$this->comments = new ArrayCollection();
}
/**
* Returns comments for this post.
* @return array
*/
public function getComments()
{
return $this->comments;
}
/**
* Adds a new comment to this post.
* @param $comment
*/
public function addComment($comment)
{
$this->comments[] = $comment;
}
}
As you can see from the code above, we added the $comments
property (line 19). This
property will be the collection of comments related to certain post.
We initialize the $comments
property in class constructor (lines 24-27). By assigning it with
a new instance of Doctrine\Common\Collections\ArrayCollection
class.
A Doctrine
ArrayCollection
is an array of objects, like usual PHParray
, but with additional features required by Doctrine. It is implemented in Doctrine\Common component.
In lines 15-18, we add Doctrine annotations to the $comments
property, so Doctrine knows how to
get all comments associated with the post:
@ORM\OneToMany
tag defines that this is the one-to-many relationship between the Post
entity and
the (target) Comment
entity.@ORM\JoinColumn
tag specifies which column to use for joining the tables associated with the
entities.The getComments()
method (lines 33-36) allows getting all comments associated with the post.
We also added the addComment()
method (lines 42-45) for adding new comment to post. You can notice that
we use the []
operator, just like we do with a typical PHP array.
Vice versa, we define the other side of this relationship by modifying the Comment
entity as follows:
<?php
// ...
use Doctrine\Common\Collections\ArrayCollection;
// ...
class Comment
{
/**
* @ORM\ManyToOne(targetEntity="\Application\Entity\Post", inversedBy="comments")
* @ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
protected $post;
/*
* Returns associated post.
* @return \Application\Entity\Post
*/
public function getPost()
{
return $this->post;
}
/**
* Sets associated post.
* @param \Application\Entity\Post $post
*/
public function setPost($post)
{
$this->post = $post;
$post->addComment($this);
}
}
In the code above, we added the $post
private property to the entity class. This is not a collection,
but a single instance of Post
class, because single comment always belongs to single post.
The annotation tags @ORM\ManyToOne
and @ORM\JoinColumn
are analogous to those we covered before.
Let's now express the many-to-many relationship between the Post
and Tag
entities. For this relationship,
we indirectly use the auxiliary post_tag
table.
Modify the Post
entity as follows:
<?php
//...
use Application\Entity\Tag;
//...
class Post
{
//...
/**
* @ORM\ManyToMany(targetEntity="\Application\Entity\Tag", inversedBy="posts")
* @ORM\JoinTable(name="post_tag",
* joinColumns={@ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
protected $tags;
// Constructor.
public function __construct()
{
//...
$this->tags = new ArrayCollection();
}
// Returns tags for this post.
public function getTags()
{
return $this->tags;
}
// Adds a new tag to this post.
public function addTag($tag)
{
$this->tags[] = $tag;
}
// Removes association between this post and the given tag.
public function removeTagAssociation($tag)
{
$this->tags->removeElement($tag);
}
}
In the code above, we do the following:
$tags
private property$tags
property with Docblock annotations with @ORM\ManyToMany
and @ORM\JoinTable
annotation tagsgetTags()
, addTag()
and removeTagAssociation()
allowing to get/modify the property's value.Finally, modify the Tag
entity as follows:
<?php
//...
use Doctrine\Common\Collections\ArrayCollection;
class Tag
{
// ...
/**
* @ORM\ManyToMany(targetEntity="\Application\Entity\Post", mappedBy="tags")
*/
protected $posts;
// Constructor.
public function __construct()
{
$this->posts = new ArrayCollection();
}
// Returns posts associated with this tag.
public function getPosts()
{
return $this->posts;
}
// Adds a post into collection of posts related to this tag.
public function addPost($post)
{
$this->posts[] = $post;
}
}
In the code above, we by analogy define the other side of the relationship and getter/setter methods for retrieving the collection of posts associated with the tag, and adding posts associated with the given tag.
To let Doctrine know where to find entities for your Application module (or for another module you have), you add the following lines into your module.config.php file:
<?php
namespace Application;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
return [
// ...
'doctrine' => [
'driver' => [
__NAMESPACE__ . '_driver' => [
'class' => AnnotationDriver::class,
'cache' => 'array',
'paths' => [__DIR__ . '/../src/Entity']
],
'orm_default' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
]
]
]
]
];
Above, in line 2, we specify the namespace Application
. This should be the name of the current
module.
Note that usually we do not specify namespace in config files, but in this particular case it is convenient to do. When we have namespace defined, we can use the
__NAMESPACE__
placeholder which expands into that namespace.
In line 8, we have doctrine
key, under which we have the driver
subkey.
In line 13, we tell Doctrine ORM that our entities are stored inside of Entity
directory under the module's src directory.