Anteriormente, criamos a entidade Post, vamos dar continuidade ao nosso simples projeto, criando uma entidade Author, faremos o relacionamento para que cada autor fique ligado ao post que criou.
Criando a entidade Author
Vamos criar a entidade Author, entre no terminal e digite:
$ php app/console generate:doctrine:entity
Vamos digitar o nome da entidade como : TablelessModelBundle:Author.
$ The Entity shortcut name: TablelessModelBundle:Author
Vamos mapeá-la usando annotation. Apenas damos enter.
$ Configuration format (yml, xml, php, or annotation) [annotation]:
O assistente nos pergunta: Qual será o nome do nosso campo?
Digitamos “name” e damos enter.
$ New field name (press to stop adding fields): name
Será do tipo string.
$ Field type [string]:
Com o tamanho de 100.
$ Field length [255]: 100
Quando o assistente nos perguntar novamente: Qual será o novo campo? Damos enter para entrarmos no processo de finalização. E nos pergunta, se queremos criar uma classe de repositório, ele nos indica não, vamos apenas dar um enter.
Do you want to generate an empty repository class [no]?
E para finalizar, o assistente pergunta se realmente queremos gerar a entidade. Como queremos, digitamos apenas enter.
$ Do you confirm generation [yes]?
Nossa entidade Author está pronta.
Ao entrarmos na pasta src/Tableless/ModelBundle/Entity/ vamos encontrá-la.
Agora devemos adicionar a annotations, @ORM\Table(name=”author”) para o nome da nossa tabela, veja na linha 10:
Veja toda a entidade Author:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Author * * @ORM\Table(name="author") * @ORM\Entity */ class Author { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=100) */ private $name; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * @return Author */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * * @return string */ public function getName() { return $this->name; } }
Configurando o projeto
Nesse momento vamos criar uma classe abstrata com o nome Timestampable, para que não fiquemos repetindo código, pois entidade Author também receberá uma data de criação e data de atualização.
Vamos lá!
Entre na pasta src/Tableless/ModelBundle/Entity/, e vamos criar uma classe abstrata com o nome Timestampable, para que possamos mapeá lá vamos usar a classe do Doctrine Mapping e vamos dar um apelido de ORM, e fazer as annotations correspondentes.
Nesse momento, ficar explicando detalhe por detalhe levará muito tempo, e o tutorial ficará extenso, veja a classe Timestampable pronta abaixo:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * Timestampable abstract class * @ORM\MappedSuperclass */ abstract class Timestampable { /** * @var \DateTime * * @ORM\Column(name="created_at", type="datetime") * @Assert\NotBlank */ private $createdAt; /** * @var \DateTime * * @ORM\Column(name="updated_at", type="datetime") * @Assert\NotBlank */ private $updatedAt; /** * Construct */ public function __construct() { $this->createdAt = new \DateTime(); $this->updatedAt = new \DateTime(); } /** * Set createdAt * * @param $createdAt */ public function setCreatedAt($createdAt) { $this->createdAt = $createdAt; } /** * Get CreatedAt * * @return \DateTime */ public function getCreatedAt() { return $this->createdAt; } /** * Set UpdatedAt * * @param \DateTime $updatedAt */ public function setUpdatedAt($updatedAt) { $this->updatedAt = $updatedAt; } /** * Get UpdateAt * * @return \DateTime */ public function getUpdatedAt() { return $this->updatedAt; } }
Vamos estender essa classe na entidade Author, veja abaixo na linha 13:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Author * * @ORM\Table(name="author") * @ORM\Entity */ class Author extends Timestampable { …
Temos que validar os campos da entidade Author, vamos dar um use em Constraints e apelidá-la como Assert:
use Symfony\Component\Validator\Constraints as Assert;
vamos validar o campo name com @Assert\NotBlank, veja abaixo:
/** * @var string * * @ORM\Column(name="name", type="string", length=100) * @Assert\NotBlank */ private $name;
Veja a entidade Author depois da configuração:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * Author * * @ORM\Table(name="author") * @ORM\Entity */ class Author extends Timestampable { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=100) * @Assert\NotBlank */ private $name; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * @return Author */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * * @return string */ public function getName() { return $this->name; } }
Configurando a entidade Post
Como criamos um classe abstrata, vamos alterar a entidade Post para que ela estenda a entidade Timestampable.
Exclua os atributos $createdAt e $updatedAt e os métodos setCreatedAt(), getCreatedAt(), setUpdatedAt(), getUpdatedAt() e o __contruct(), e vamos estender a classe Timestampable, depois de configurada, a entidade Post ficará assim:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * Post * * @ORM\Table(name="post") * @ORM\Entity */ class Post extends Timestampable { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) * @Assert\NotBlank */ private $title; /** * @var string * * @ORM\Column(name="content", type="text") * @Assert\NotBlank */ private $content; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set title * * @param string $title * @return Post */ public function setTitle($title) { $this->title = $title; return $this; } /** * Get title * * @return string */ public function getTitle() { return $this->title; } /** * Set content * * @param string $content * @return Post */ public function setContent($content) { $this->content = $content; return $this; } /** * Get content * * @return string */ public function getContent() { return $this->content; } }
Atualizando o banco de dados
Geramos a entidade Author e alteramos a entidade Post, dessa forma devemos atualizar nosso banco, para que o mesmo fique configurado de acordo com as entidades.
Vamos ao terminal, e para atualizar o banco de dados vamos digitar o código:
$ php app/console doctrine:schema:update --force
Teremos o resultado:
Updating database schema... Database schema updated successfully! "1" queries were execute
Entrando no banco de dados vamos perceber que a tabela author criada:
Criando o CRUD da entidade Author
Depois da configuração das nossa entidade, vamos gerar o CRUD da entidade Author. Vamos digitar no console:
$ php app/console generate:doctrine:crud
Digitamos TablelessModelBundle:Author:
$ The Entity shortcut name: TablelessModelBundle:Author
O assistente nos pergunta se queremos gerar as ações de gravação, digitamos: yes.
$ Do you want to generate the "write" actions [no]? Yes
Como vamos configurar? Vamos deixar como está, annotation, e damos enter
$ Configuration format (yml, xml, php, or annotation) [annotation]:
Como será a rota? Vamos deixar como ele nos indica, damos enter.
$ Routes prefix [/author]:
Vamos confirmar a geração desse CRUD dando enter.
$ Do you confirm generation [yes]?
Prontinho nosso CRUD da entidade Author está pronto, vamos testar.
Inicie o servidor:
$ php app/console server:run
entre na url:
https://127.0.0.1:8000/author/
Vamos criar um autor com o nome Tableless
Relacionamento com Doctrine
Vamos fazer um relacionamento no banco de dados, pois queremos que, ao criarmos um post, o mesmo esteja relacionado com o autor que o criou. Não entraremos em detalhes sobre relacionamento, caso tenha dúvidas, consulte a documentação.
Vamos configurar novamente as entidades para que o relacionamento possa acontecer.
Entre na entidade Post e acrescente o atributo $author com as seguintes annotations:
/** * @var Author * * @ORM\ManyToOne(targetEntity="Author", inversedBy="posts") * @ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false) * @Assert\NotBlank */ private $author;
Entre na entidade Author e acrescente o atributo $post, com as seguintes annotations:
/** * @var ArrayCollection * * @ORM\OneToMany(targetEntity="Post", mappedBy="author", cascade={"remove"}) */ private $post;
Precisamos também dar um use na classe ArrayCollection, do Doctrine, pois um autor terá vários posts, e os posts serão buscados como array, insira o código:
use Doctrine\Common\Collections\ArrayCollection;
Vamos criar um construtor, porém a entidade Timestampable já tem um construtor, para resolver esse problema, vamos adicionar um parent::__construct(), veja abaixo:
/** * Constructor */ public function __construct() { parent::__construct(); $this->post = new ArrayCollection(); }
Agora vamos gerar os métodos necessários da entidade Author, entre no console e digite:
$ php app/console generate:doctrine:entities TablelessModelBundle:Author
Temos que gerar também para a entidade Post:
$ php app/console generate:doctrine:entities TablelessModelBundle:Post
Entre novamente na entidade Author e acrescente o método abaixo no final da entidade:
/** * @return string */ public function __toString() { return $this->getName(); }
Veja como ficou a entidade Author depois de configurarmos:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; use Doctrine\Common\Collections\ArrayCollection; /** * Author * * @ORM\Table(name="author") * @ORM\Entity */ class Author extends Timestampable { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=100) * @Assert\NotBlank */ private $name; /** * @var ArrayCollection * * @ORM\OneToMany(targetEntity="Post", mappedBy="author", cascade={"remove"}) */ private $post; /** * Constructor */ public function __construct() { parent::__construct(); $this->post = new ArrayCollection(); } /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * @return Author */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * * @return string */ public function getName() { return $this->name; } /** * Add post * * @param \Tableless\ModelBundle\Entity\Post $post * @return Author */ public function addPost(\Tableless\ModelBundle\Entity\Post $post) { $this->post[] = $post; return $this; } /** * Remove post * * @param \Tableless\ModelBundle\Entity\Post $post */ public function removePost(\Tableless\ModelBundle\Entity\Post $post) { $this->post->removeElement($post); } /** * Get post * * @return \Doctrine\Common\Collections\Collection */ public function getPost() { return $this->post; } /** * @return string */ public function __toString() { return $this->getName(); } }
Veja a entidade Post, após a configuração:
<?php namespace Tableless\ModelBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * Post * * @ORM\Table(name="post") * @ORM\Entity */ class Post extends Timestampable { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) * @Assert\NotBlank */ private $title; /** * @var string * * @ORM\Column(name="content", type="text") * @Assert\NotBlank */ private $content; /** * @var Author * * @ORM\ManyToOne(targetEntity="Author", inversedBy="posts") * @ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false) * @Assert\NotBlank */ private $author; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set title * * @param string $title * @return Post */ public function setTitle($title) { $this->title = $title; return $this; } /** * Get title * * @return string */ public function getTitle() { return $this->title; } /** * Set content * * @param string $content * @return Post */ public function setContent($content) { $this->content = $content; return $this; } /** * Get content * * @return string */ public function getContent() { return $this->content; } /** * Set author * * @param \Tableless\ModelBundle\Entity\Author $author * @return Post */ public function setAuthor(\Tableless\ModelBundle\Entity\Author $author) { $this->author = $author; return $this; } /** * Get author * * @return \Tableless\ModelBundle\Entity\Author */ public function getAuthor() { return $this->author; } }
Agora vamos atualizar o banco de dados para gerar o relacionamento, para isso apague todo o conteúdo das tabelas do banco de dados, caso não o faça, ocorrerá erro,
Após apagar o conteúdo do banco, rode o comando no console:
$ php app/console doctrine:schema:update --force
Corrigindo os formulários
Entre na classe PostType, caminho: src/Tableless/ModelBundle/Form/PostType, e acrescente a linha abaixo, no método buildForm:
->add('author')
Também vamos apagar as linhas:
->add('createdAt') ->add('updatedAt')
Pois não precisamos inserir as datas, em que o post foi criado, ou alterado, isso acontecerá automaticamente!
Depois das modificações, o método formBuilder ficará como abaixo:
/** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('title') ->add('content') ->add('author') ; }
Tudo configurado para que possamos criar nossos posts.
Para verificarmos se está tudo correto, precisamos criar primeiramente um autor, depois criamos um post, onde teremos que selecionar um autor, para o mesmo.
Veja abaixo:
Lembrando que a url de autor é:
https://127.0.0.1:8000/author/
e a url de post é:
Conclusão
Vamos terminar este tutorial, pois seu conteúdo está muito extenso, no próximo, vamos fazer as configurações necessárias em nossa simples aplicação, e vamos criar um index, para mostrar nossos posts, que configuraremos com o Bootstrap, e com o template engine twig. O projeto encontra-se no GitHub!