Para el módulo Application, las entidades se guardan (por convención) en la carpeta
Entity dentro de la carpeta fuente del módulo. Las clases tipo entidad están en
el namespace Application\Entity
.
Comenzamos con la creación de la entidad Post
. Creamos el archivo Post.php dentro
de la carpeta Entity del módulo. (Si aún no hemos creado la carpeta Entity es
momento de hacerlo ahora). Colocamos este código dentro del archivo:
<?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;
}
}
En el código de arriba tenemos los siguientes elementos:
Las constantes de estado (líneas 14 y 15). Estas constantes representan convenientemente
los posibles valores que la propiedad $status
puede recibir (1 para borrador
y 2 para publicado).
Propiedades protegidas ($title
, $content
, $dateCreated
, etc.). Estas son
datos que generalmente tiene una publicación de un blog (en la tabla 12.2 de abajo
se pueden ver las propiedades junto con una breve descripción).
Es importante señalar que para las propiedades usamos (por convención) nombres en camel-case (como
$dateCreated
), mientras que para las columnas usamos nombres "canónicos" (en minúsculas con un guión bajo como separador de palabras, comodate_created
).
Propiedad | Columna Asociada | Descripción |
---|---|---|
$id |
id |
ID únicp de la publicación. |
$title |
title |
Título de la publicación. |
$content |
content |
Contenido de la publicación. |
$status |
status |
Estado (borrador/publicado) de la publicación. |
$dateCreated |
date_created |
Fecha de creación de la publicación. |
La clase de tipo entidad y sus propiedades se señalan con anotaciones de Docblock que lee Doctrine ORM en tiempo de ejecución permitiendo asociar la entidad y sus propiedades a la tabla de la base de datos y sus columnas.
Las clases de tipo entidad tiene métodos getter y setter (líneas 45-102) que permite acceder/modificar las propiedades protegidas (ver la tabla 12.3 para consultar los métodos y una breve descripción).
Método | Descripción |
---|---|
getId() | Regresa el ID de la publicación. |
setId($id) | Coloca el ID de la publicación. |
getTitle() | Regresa el título. |
setTitle($title) | Coloca el título. |
getStatus() | Regresa el estado (borrador/publicado). |
setStatus($status) | Coloca el estado. |
getContent() | Regresa el contenido de la publicación. |
setContent($content) | Coloca el contenido de la publicación. |
getDateCreated() | Regresa la fecha de creación de la publicación. |
setDateCreated() | Coloca la fecha de creación de la publicación. |
Es importante señalar que los métodos de la clase de tipo entidad no tienen anotaciones. No es necesario que las tenga. Sin embargo, podemos colocar comentarios a los métodos y usar anotaciones de Docblock (pero no las de Doctrine), lo que es altamente recomendable.
Por analogía con la entidad Post
creamos las entidades Comment
y Tag
en la
carpeta Entity. Para hacer esto, primero, creamos el archivo Comment.php y colocamos
el siguiente código dentro de él:
<?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;
}
}
Luego, creamos el archivo Tag.php y colocamos el siguiente código dentro de él:
<?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;
}
}
Como las entidades Comment
y Tag
son análogas a la entidad Post
no proveemos una
descripción detallada del código de arriba.
Es importante notar que no se creo una entidad para la tabla auxiliar
post_tag
. Esta tabla se usará indirectamente luego en este capítulo cuando se definan las relaciones entre entidades.
Ahora es tiempo de usar las anotaciones para definir las relaciones entre entidades. Si recordamos, nosotros tenemos dos relaciones entre nuestras entidades:
Post
y Comment
se relacionan: "uno-a-muchos".Post
y Tag
se relacionan: "mucho-a-mucho".En Doctrine para expresar relaciones entre dos entidades agregamos una propiedad privada junto con una anotación de Docblock.
Para conocer más detalles sobre las relaciones entre entidades en Doctrine podemos leer esta página de la documentación de Doctrine.
Primero vamos a definir la relación uno-a-muchos entre las entidades Post
y Comment
.
Modificamos el archivo Post.php y agregamos las siguientes líneas:
<?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;
}
}
Como podemos ver en el código de arriba se agregó la propiedad $comments
(línea 19).
Esta propiedad será la colección de comentarios de una determinada publicación.
Se inicializa la propiedad $comments
en el constructor de la clase (línea 24-27) y
se le asigna una instancia de la clase Doctrine\Common\Collections\ArrayCollection
.
Un
ArrayCollection
de Doctrine es un arreglo de objetos, como unarray
PHP común, pero con características adicionales de Doctrine. Está implementada en el componente Doctrine\Common.
En las líneas 15-18 agregamos las anotaciones de Doctrine para la propiedad $comments
,
así Doctrine conoce como conseguir todos los comentarios asociados a la publicación:
@ORM\OneToMany
define que esta es una relación uno-a-muchos entre
la entidad Post
y la entidad (objetivo) Comment
.@ORM\JoinColumn
especifica que columnas usar para unir las tablas
asociadas con las entidades.El método getComments()
(líneas 33-36) permite traer todos los comentarios asociados
con la publicación.
Además, agregamos el método addComment()
(líneas 42-45) para agregar un nuevo
comentario a la publicación. Se puede ver que usamos el operador []
exactamente
como un típico arreglo de PHP.
Y vice versa, definimos el otro lado de la relación para esto modificamos la entidad
Comment
de la siguiente manera:
<?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);
}
}
En el código de arriba agregamos la propiedad privada $post
a la clase de tipo
entidad. Esta no es una colección sino una instancia de la clase Post
, por que
un solo comentario pertenece a una sola publicación. Las etiquetas de anotación
@ORM\ManyToOne
y @ORM\\JoinColumn
son análogas a aquellas que explicamos
antes.
Ahora vamos a expresar la relación mucho-a-muchos entre las entidades Post
y Tag
.
Para esta relación usamos indirectamente la tabla auxiliar post_tag
.
Modificamos la entidad Post
de la siguiente manera:
<?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);
}
}
En el código de arriba se hace lo siguiente:
$tags
.$tags
con las etiquetas de anotaciones de Docblock.
@ORM\ManyToMany
y @ORM\JoinTable
.getTags()
, addTag()
y removeTagAssociation()
.Finalmente, modificamos la entidad Tag
de la siguiente manera:
<?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;
}
}
En el código de arriba por analogía definimos el otro lado de la relación y los métodos getter/setter para recuperar la colección de publicaciones asociadas con la etiqueta y agregamos las publicaciones asociadas con una etiqueta dada.
Para permitir que Doctrine conozca donde encontrar las entidades para nuestra módulo Application (o para otro módulo) agregamos las siguientes líneas dentro de nuestro archivo module.config.php:
<?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'
]
]
]
]
];
Arriba en la línea 2 especificamos el namespace Application
. Este debe ser el nombre
del módulo actual.
Nótese que usualmente no especificamos namespace en los archivos de configuración, pero en este caso en particular es conveniente hacer esto. Como tenemos un namespace definido podemos usar el comodín
__NAMESPACE__
.
En la línea 8 tenemos la llave doctrine
dentro de la que tenemos la subllave driver
.
En la línea 13 decimos a Doctrine ORM que nuestras entidades se guardan dentro del
directorio Entity del directorio src del módulo.