Doctrine ORM — Relacionamento entre entidades

Rafael Mello
PHPRio
Published in
4 min readNov 2, 2016

Nas partes 1 e 2, expliquei como instalar e configurar o Doctrine ORM no Zend Framework(no caso do Symfony, por padrão já vem instalado) e a criar entidades e sincronizar com o banco de dados. Para quem não acompanhou, poderá acessar os posts anteriores pelos links abaixo.

Hoje estarei dando continuidade e explicando como validar e criar relacionamentos entre as entidades.

Então vamos la! Na parte dois, foi criada uma entidade com nome de estado, onde respectivamente representa minha tabela “estado” no meu banco de dados. De inicio, criarei uma entidade com nome de cidade e criarei seus devidos relacionamentos one-to-many e many-to-one.

Minha entidade de cidade seguirá a seguinte estrutura:

<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;/**
* @ORM\Table(name="cidade")
* @ORM\Entity
*/
class Cidade {
/**
* @var integer Identificador único do registro.
*
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string Nome da cidade.
*
* @ORM\Column(type="string")
*/
private $nome;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getNome()
{
return $this->nome;
}
public function setNome($nome)
{
$this->nome = $nome;
return $this;
}
public function __toString()
{
return $this->nome;
}
}

Lembrando, o método __toString() é usado para definir o que será impresso caso seja feito um “echo” diretamente no objeto. Por padrão, quando trabalhamos com entidades costumamos manter sempre a existencia desse método com a informação necessária para mostrar na tela.

Com a entidade criada, inicialmente criarei o relacionamento many-to-one de cidade com estado. Para isso estarei adicionando o código abaixo em minha classe de cidade.

// .../**
* @var Estado Entidade de estado
*
* @ORM\ManyToOne(targetEntity="Estado")
* @ORM\JoinColumn(nullable=false)
*/
private $estado;
// ...public function getEstado()
{
return $this->estado;
}
public function setEstado(Estado $estado)
{
$this->estado = $estado;
return $this;
}
// ...

O Doctrine nesse caso se encarregará de criar na tabela de cidade, uma foreign key(chave estrangeira) apontando para a tabela de estado.

Por padrão, foreign keys no Doctrine aceita valores nulos(diferente dos campos convencionais), para obrigar o preenchimento(“NOT NULL” no caso do banco de dados) é necessário adicionar a anotação @ORM\JoinColumn, alterando valor de “nullable”. No caso de colunas comuns, esse valor poderá ser alterado diretamente na anotação @ORM\Column.

Caso queira pesquisar(ou registrar) uma coleção de cidades pela entidade de estado, é necessário criar o relacionamento one-to-many na entidade de estado, além de adicionar o parametro inversedBy na anotação @ORM\ManyToOne do respectivo relacionamento na entidade de cidade.

Exemplo de campos adicionados na entidade de estado:

// ...use Doctrine\Common\Collections\ArrayCollection;// .../**
* @var ArrayCollection Coleção de cidades
*
* @ORM\OneToMany(targetEntity="Cidade", mappedBy="estado")
*/
private $cidades;
// ...public function __construct()
{
$this->cidade = new ArrayCollection();
}
public function getCidades()
{
return $this->cidades;
}
public function setCidades(ArrayCollection $cidades)
{
foreach ($cidades as $cidade) {
$cidade->setEstado($this);
}
$this->cidades = $cidades;
return $this;
}
// ...

Nome que no setCidades, precisei criar um loop no ArrayCollection cidades e forçar para que cada cidade presente na coleção de objetos tenha seu devido relacionamento com o estado em questão. Sem isso, na tabela cidade não será registrado valor na foreign key de estado.

Na minha entidade de cidade, estarei editando o atributo “private $estado” para inserir o parametros “inversedBy”, conforme mencionamento acima. Segue as alterações abaixo.

// .../**
* @var Estado Entidade de estado
*
* @ORM\ManyToOne(targetEntity="Estado", inversedBy="cidades")
* @ORM\JoinColumn(nullable=false)
*/
private $estado;
// ...

Com isso concluimos nossos relacionamento one-to-many e many-to-one! Note que a existencia do mappedBy e inversedBy nas entidades faz com que o Doctrine entenda qual atributo está fazendo referencia ao relacionamento em questão na entidade informada.

O one-to-one segue a mesma filosofia, com a diferença que usamos a anotação @ORM\OneToOne em ambas as entidades, no lugas das anotações @ORM\ManyToOne e @ORM\OneToMany usados anteriormente. Vale lembrar que como a relação one-to-one condiz a apenas um registro relacionado, então não é necessário ArrayCollection. Ambos os lados são tratados na mesma forma que a anotação @ORM\ManyToOne.

No caso do many-to-many ambas as entidades mantem a anotação @ORM\ManyToMany e ambas as entidades possuem coleções de objetos(ArrayCollection), ou seja, ambos os lados são tratados da mesma forma que é tratado com a anotação @ORM\OneToMany, com a leve diferença de que não é necessário fazer o loop nas coleções para adicionar a foreign key. O próprio Doctrine resolverá os ids neste caso.

ATENÇÃO! O many-to-many do Doctrine não permite a inserção de campos adicionais. Por padrão, a tabela criada para esse relacionamento contem apenas as 2 foreign keys do relacionamento em questão. Caso necessite de campos adicionais, você precisará criar uma entidade intermediaria, que deverá representar a tabela em questão. No caso de usar uma entidade intermediaria, você usará as anotações @ORM\OneToMany e @ORM\ManyToOne no lugar da anotação @ORM\ManyToMany.

Pronto! Agora que sabemos criar os relacionamentos entre entidades, vamos aprender a validar as entidades e o banco de dados.

O Doctrine tanto no Symfony, quanto no Zend Framework, possui seu comando especifico para validar a estabilidade das entidades e verificar se seu banco de dados está sincronizado ou não com as entidades existentes. Segue abaixo o comando abaixo de cada framework.

# Symfony
$ php app/console doctrine:schema:validate
# Zend Framework
$ php public/index.php orm:validate-schema

Caso suas entidades estejam sem erros e seu banco de dados esteja sincronizado, deverá retornar a mensagem abaixo.

[Mapping]  OK - The mapping files are correct.
[Database] OK - The database schema is in sync with the mapping files.

Pronto! Com isso terminamos o conteúdo da parte 3. Não deixem de comentar dúvidas, criticas ou sujestões, estarei respondendo todos os comentários!

Na próxima parte estarei explicando como criar “triggers” no Doctrine, onde uma entidade só deverá executar um método antes e/ou após o persist, update e/ou remove.

Até a próxima pessoal!

--

--

Rafael Mello
PHPRio
Writer for

“Do or do not. There is no try.” — Yoda(Star Wars)