Symfony Basics


O symfony é um framework fullstack de aplicações web para as necessidades de alto desempenho, é um conjunto de componentes PHP, para grandes e avançados projetos, porém podemos instalar seus componentes separadamente em casos de projetos menores. É muito respeitado pela comunidade, não é apenas um Framework popular, mas também é uma das melhores plataformas para construir projetos Open-Source. Muitos projetos PHP estão incorporando alguns dos componentes ou estão usando o framework full-stack, projetos como DrupalLaravel, entre outros, veja a lista.

Iniciando

Vou abordar nesse simples tutorial a instalação do Symfony, para que, em tutoriais futuros possamos dar continuidade a dicas mais avançadas.

Primeiramente vamos criar uma pasta, para que nosso projeto se mantenha organizado, digitando o comando no terminal:

$ mkdir tableless

Após a criação da pasta, vamos entrar na mesma, com o comando:

$ cd tableless

Instalação

Vamos instalar o Symfony via composer, caso não o conheça, ou tenha dúvidas, leia este post (Composer para iniciantes). Para fazermos o Download do Symfony entramos no site e copiamos o comando, como na imagem abaixo:

Download do Symfony

Vamos trocar o final do código, onde está path/ vamos colocar symfony/ que será a pasta onde instalaremos o Symfony, o comando ficará assim:

$ composer create-project symfony/framework-standard-edition symfony/

Ao darmos enter, a instalação irá começar como na imagem abaixo, isso poderá demorar alguns minutos, já que o composer irá baixar a distribuição padrão do Symfony, juntamente com todas as suas bibliotecas.

Instalação do symfony

Ao baixar todos os componentes, a instalação do symfony vai nos fazer diversas perguntas.

1º – Gostaria de instalar Acme demo bundle? [y/N] digitamos “N” e damos enter.

O Acme demo bundle é apenas uma demonstração de alguns recursos que podemos trabalhar, e nesse caso não vamos instalar!

$ Would you like to install Acme demo bundle? [y/N]:N

2º – O symfony nos pergunta qual o drive de banco de dados que vamos utilizar.

Em nosso caso vamos usar o PDO, que hoje é basicamente um padrão do PHP, e o próprio symfony nos recomenda, damos apenas um enter para continuar.

$ database_driver (pdo_mysql):

3º – Nos pergunta qual o host do banco de dados, como estamos em localhost apenas damos um enter.

$ database_host (127.0.0.1):

4º – Qual a porta que vamos usar, por defult vamos deixar como está, e damos um Enter

$ database_port (null)

5º – Qual o nome do banco de dados vamos usar, nesse caso, vamos deixar como está, mas você pode utilizar o nome que quiser, e damos enter.

$ database_name (symfony):

6º – Qual o nome do nosso usuário do banco, no meu caso vou deixar como está, meu usuário é root, damos um enter.

$ database_user (root):

7º – Qual é nossa senha, no meu caso é root, deixo assim em ambiente de desenvolvimento, digito root e dou enter.

$ database_password (null): root

8º – Nos pergunta sobre nossos dados de e-mail, vamos apenas dar um enter, pois não vamos usar agora.

$ mailer_transport (smtp):

9º – Qual o host de e-mail, apenas damos um enter.

$ mailer_host (127.0.0.1):

10º – Nos pergunta sobre o usuário de e-mail, damos enter.

$ mailer_user (null):

11º – Nos pergunta sobre a senha, enter.

$ mailer_password (null):

12º – Sobre localidade, e digitamos pt_BR, e enter

$ locale (en): pt_BR

13º – Nos pergunta sobre a chave secreta de nossa aplicação, vamos deixar como está, apenas damos um enter.

$ secret (ThisTokenIsNotSoSecretChangeIt):

Segue a imagem para comparação, se tudo ocorreu bem, ficará assim:

Instalando Symfony

Rodando a aplicação

Pronto, o Symfony está instalado!

Para vê-lo rodando vamos iniciar nosso servidor.

A partir do PHP 5.4, o próprio vem com um servidor web embutido (PHP’s built-in Web Server). Ele pode ser usado para executar suas aplicações PHP localmente durante o desenvolvimento, para testar ou para demonstrações de aplicativos. Desta forma, você não tem que se preocupar em configurar um servidor full-featured web como o Apache ou Nginx.

Para iniciarmos o servidor do php digitamos no terminal:

$ php -S 127.0.0.1:8080

ou

$ php -S 127.0.0.1:8080 -t public_html/

para indicar que seu index.php está na pasta public_html.

Mas em nosso caso, não vamos utilizar os comandos citados acima, como estamos usando o Symfony, entramos em nossa pasta, que está instalado o projeto:

$ cd symfony

e digitamos:

$ php app/console server:run

e teremos a resposta:

$ Server running on http://127.0.0.1:800

Abrimos nosso navegador e digitamos a url: http://127.0.0.1:8000/

E Pronto!

Página do Symfony

Irá aparecer um erro na tela, por não termos configurado as nossas rotas no Controller, porém sabemos que nossa aplicação está rodando. O Symfony gera uma rota de teste automaticamente, e para vermos se está tudo certo sem erros, então digitamos em nosso navegador a url:

http://127.0.0.1:8000/app/example

Aparecerá uma página em branco escrita Homepage, junto com a barra de debug do Symfony (a debug toolbar) utilizada em desenvolvimento, que estará no rodapé!

O Symfony está rodando com sucesso!

Olhando rapidamente para a debug toolbar

A debug toolbar é uma barra de ferramentas do Symfony, fantástica, que nos traz informações valiosas.

Debug toolbar Symfony2

1 – No início temos o ícone do Symfony, que ao colocarmos o ponteiro do mouse em cima, a barra nos mostra a versão do Symfony juntamente com um link para a documentação.

2 – Informações sobre o PHP, como versão e extensões usadas, e ao clicarmos, nos retorna uma página com um phpinfo(), onde estão todas as configurações do PHP de nossa máquina.

3 – Informações a respeito da requisição principal de nossa aplicação, indicando, que estamos em ambiente de desenvolvimento e o token de requisição.

4 – Informações sobre o status code, o Controller e Action de nossa página, o nome da rota que estamos acessando e se temos ou não uma sessão.

5 – Requisições de AJAX.

6 – Tempo em que nossa página demorou pra carregar.

7 – Quantidade de memória que a aplicação utilizou.

8 – Informações, quantidade de formulários.

9 – Informação sobre autenticação, usuários anônimos, admins, etc…

10 – Informações sobre consultas no banco de dados, tempos de queries, …

Configurações básicas

Vamos deixar nosso projeto um pouco mais limpo, excluindo os arquivos de UPGRADEs.md, que não tem relevância em nossa aplicação nesse momento.

Podemos excluir os arquios:

UPGRADE.md

UPGRADE-2.2.md

UPGRADE-2.3.md

UPGRADE-2.4.md

Também vamos modificar o conteúdo do nosso arquivo README.md, lembrando que a linguagem usada nesse arquivo é Markdown, para ser lida pelo GitHub, nesse link você encontra algumas noções básicas.

Apague todo o conteúdo do arquivo, que pode ser feito por um simples editor de texto, como bloco de notas, pela IDE de sua preferência, ou até mesmo pelo vim no terminal!

E adicionamos o conteúdo abaixo:

Iniciando com Symfony 2
=======================

http://tableless.com.br/
-----------------------

**Tutorial do Portal Tableless**

>1º -  *Iniciando com Symfony 2*

Podemos modificar o conteúdo do arquivo composer.json somente as linhas 2 e 5.

De:

"name": "symfony/framework-standard-edition",
"description": "The \"Symfony Standard Edition\" distribution",

Para:

"name": "tableless/iniciando-com-Symfony",
"description": "Tableless: Iniciando com Framework Symfony 2",

Controlando nossa aplicação

Após essas simples configurações iniciais, podemos iniciar o Git em nosso projeto, para termos maior controle sobre nossa aplicação, não abordaremos conceitos sobre Git, mas em caso de dúvidas, Consulte!

O projeto se encontra no meu GitHub, assim ficará mais fácil para você analisar, estudar e comparar os códigos como o seu projeto! Lembrando que até o momento só instalamos o Symfony2, mas nos próximos tutoriais daremos continuidade ao nossos exemplos!

Concluindo

Para finalizarmos, recomendo a documentação do Symfony, ótima para estudos!

É claro que o Symfony é para projetos maiores, mas para efeito de didática, achei um bom começo, espero que gostem.

Bundle o coração do symfony

O Symfony trabalha com bundle (pacote), que é um conjunto de códigos que pode ser reutilizado em outros projetos. Simplificando: são componentes prontos, que facilitam nossa vida na hora de desenvolver. Podemos criar um bundle do zero, como vamos fazer aqui, ou configurar um já pronto.

Antes de criarmos de nosso primeiro bundle, vamos fazer uma pequena modificação no projeto. Não vamos precisar do bundle AppBundle, só usamos para a introdução.

Exclua a pasta AppBundle como mostrado na imagem, caminho src/AppBundle.

Imagem pastas app/AppBundle

Vamos apagar o registro desse bundle que acabamos de excluir, pois todos os bundles são registrados no AppKernel para que possamos usá-los.

Entre no arquivo app/AppKernel.php e apague a linha “new AppBundle\AppBundle()”, no meu caso a linha 19.

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
//apague    new AppBundle\AppBundle(),    caso contrário acarretará em erro...
        );

        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
    }
}

Também devemos excluir a rota desse bundle.

Entre no arquivo app/config/routing.yml e apague as linhas abaixo:

app:
    resource: @AppBundle/Controller/
    type:     annotation

Cuidado caso tenha feito alguma modificação desde a instalação, no meu caso, estou dando continuidade a partir da instalação, e apagando as linhas acima, meu arquivo routing.yml, ficou vazio.

Criando um bundle utilizando o componente console do Symfony

Vamos criar nosso bundle! Podemos fazer isso codificando, porém o Symfony 2 nos traz um ferramenta poderosa e fantástica, o componente cosole, que já vem instalado em nossa aplicação, ou podemos instalá-lo separadamente pelo packagist, via composer.

Vamos lá.

Entre na pasta do projeto pelo terminal:

$ cd symfony

Primeiramente, para vermos o que o console do symfony é capaz, vamos digitar no terminal:

$ php app/console

Com este comando seremos apresentados à uma lista de comandos que podemos efetuar em nossa aplicação pelo componente console do Symfony. Se instalarmos alguns componentes adicionais do symfony, essa lista pode aumentar, mas não é nosso caso agora, vamos dar continuidade!

Para criarmos nosso bunble, digitamos no terminal:

$ php app/console generate:bundle

Após darmos o enter aparecerá a seguinte tela, que é um assistente do Symfony para nos ajudar na geração do bundle:

Criando bundle pelo symfony console

O symfony nos pede para digitarmos nossa namespace, que é o caminho completo do nosso bundle, primeiramente, temos que dar um nome para nosso vendor, caso não entenda, sugiro a leitura da PSR-4, em meus projetos particulares, coloco a sigla do meu nome como vendor “CJSN”, porém você pode colocar o nome do seu projeto, do seu cliente, etc… Aqui vamos colocar Tableless, em seguida o nome de nosso bundle.

Então digitamos

$ Bundle namespace: Tableless/CoreBundle

Ao darmos enter, o Symfony nos pergunta como esse bundle vai ser reconhecido em nosso projeto e nos sugere um nome: TablelessCoreBundle

$ Bundle name [TablelessCoreBundle]:

Você pode simplificar digitando apenas CoreBundle, em meu caso vou deixar como está, gosto assim em meus projetos, deixando-os padrão.

E damos enter.

Agora a pergunta é: Qual o caminho que queremos instalar nosso bundle?

Por padrão vamos deixá-lo na pasta src/, somente damos enter.

$ Target directory [/home/candidosouza/tableless/symfony/src]:

Como vamos fazer a configuração de nosso bundle?

Digitamos annotation e enter:

$ Configuration format (yml, xml, php, or annotation): annotation

A próxima pergunta é: Se queremos que ele gere toda a estrutura de diretórios de um bundle? Não, apenas damos um enter.

$ Do you want to generate the whole directory structure [no]?

Nos pergunta sobre a confirmação de geração do bundle, damos enter.

$ Do you confirm generation [yes]? 

Desejamos adicionar as configurações no AppKernel? Sim queremos, somente digitamos enter.

$ Confirm automatic update of your Kernel [yes]?

E pergunta se queremos inserir rotas para esse bundle, digitamos enter.

$ Confirm automatic update of the Routing [yes]? 

E pronto, nosso bundle está criado, veja as imagems abaixo para comparação.

Finalizando criação de bundle

Imagem finalizada CoreBundle

Para vermos se está tudo certo, podemos entrar em nosso navegador e digitar a rota exemplo criada pelo Symfony: @Route(“/hello/{name}”), então digitamos a url no navegador: http://127.0.0.1:8000/hello/Tableless

Lembrando que o servidor deve estar iniciado, você pode ver isso no tutorial anterior. Aparecerá a mensagem “Hello Tableless!”, se você notar, verá que a debug toolbar, a barra de ferramentas do Symfony, não está aparecendo, mas não vamos nos preocupar com isso, pois não usaremos essa rota, ela é gerada apenas para exemplo.

Após a crianção do nosso bundle, o CoreBundle, vamos repetir a operação, e criar um novo bundle, agora com o nome ModelBundle.

Novamente digitamos

$ php app/console generate:bundle

em seguida:

$ Tableless/ModelBundle

E todo o processo anteriormente feito…

A primeira coisa a fazer depois de criarmos o ModelBundle, é excluir a rota gerada pelo Symfony, é claro que poderíamos ter feito isso na hora da criação, pelo terminal, quando ele nos faz a última pergunta: Confirmar atualização automática das Rotas[sim]?

$ Confirm automatic update of the Routing [yes]? 

Era só termos digitado “no”, mas para efeito de didática, e para não ficarmos duplicando código nesse tutorial, preferi deixar assim.

Vamos entrar no arquivo app/config/route.yml e vamos apagar a rota gerada para esse bundle.

Apague as linhas abaixo:

tableless_model:
    resource: "@TablelessModelBundle/Controller/"
    type:     annotation
    prefix:   /

Deixando somente a rota do CoreBundle criada anteriormente.

Vamos excluir também, as pastas Controller, e views, pois não vamos usar esses arquivos nesse bundle.

Caminhos das pastas a serem excluidas:

src/Tableless/ModelBundle/Controller

src/Tableless/ModelBundle/Resources/views

Excluindo pastas

Concluindo

Pronto, nosso simples projeto, está configurado e pronto para darmos inicio aos demais processos, neste momento em que se encontra o mesmo, vou comitar, e subir o projeto para o GitHub. No próximo tutorial veremos como criar entidades com o Doctrine ORM em conjunto com o Symfony, para inserirmos nossos posts no banco de dados.

Para finalizarmos, recomendo novamente a documentação do Symfony, caso queiram fazer testes, estudar, se aprofundar mais no assunto!

Banco de dados com Doctrine ORM

Quando falamos de banco de dados em projetos com Symfony, estamos falando de Doctrine ORM (Object Relational Mapper), onde criamos uma camada de persistência entre a aplicação e um banco de dados, que mapeia nossas tabelas com entidades, para que possamos acessar o banco.

Não vou me aprofundar sobre Doctrine, porém, o que você precisa saber é que o Doctrine é um projeto espetacular e grandioso, muito usado pela comunidade, para projetos robustos.

Iniciando com Doctrine ORM

O Doctrine já vem instalado e configurado no Symfony. Lembrando que você pode instalá-lo separadamente via ComposerNo primeiro post que escrevi sobre a instalação do Symfony, nós configuramos as opções de banco de dados, o host, usuário e senha. Vale lembrar que são de configuração pessoais, configuradas em sua máquina.

No meu caso, configurei de acordo com minhas configurações do MySQL. Também demos um nome para o banco de dados que vamos utilizar agora e que foi configurado com o nome “symfony”. Porém este não está criado, vamos criá-lo neste momento usando o componente console do Symfony em conjunto com o Doctrine.

Vamos digitar no terminal para criar nosso banco:

$ php app/console doctrine:database:create

E obteremos a resposta: Banco de dados criado para a conexão, com o nome ‘symfony’.

$ Created database for connection named 'symfony'

Gerando Entidades

Agora que nosso banco foi gerado, vamos iniciar criando uma entidade. No Doctrine são objetos leves que contêm propriedades persistentes, que são salvos e recuperados do banco de dados por recursos de mapeamento de dados.

Para gerarmos uma entidade vamos digitar no terminal:

$ php app/console generate:doctrine:entity

Neste momento o console nos dá a dica do que devemos fazer:

“Em primeiro lugar, você precisa dar o nome para a entidade que pretende gerar.

Você deve usar a notação de atalho como AcmeBlogBundle:Post.”

Em nosso caso vamos digitar “TablelessModelBundle:Post”, para criar a entidade Post no bundle TablelessModelBundle.

$ TablelessModelBundle:Post

Feito isso, o console nos pergunda: Qual o formato que vamos usar para obter as informações de mapeamento? Por padrão ele nos indica annotation, vamos deixar como está e damos enter.

$ Configuration format (yml, xml, php, or annotation) [annotation]:

As annotations são usadas pelo Doctrine para mapear as entidades, e obter informações por meio delas.

Após darmos o enter, o console nos indica a inserir novos campos, (Obs: automaticamente, já foi gerado o “id” como primary key), e nos pergunta qual o nome do novo campo que vamos criar:

$ New field name (press  to stop adding fields):

Você pode interromper, apenas dando um enter, em nosso caso vamos continuar, digitando o nosso primeiro campo que será “title”, o titulo de nosso post, damos enter.

$ New field name (press  to stop adding fields):title

Nos pergunta qual o tipo, que vamos usar nesse campo, e ele nos indica “string”, vamos deixar como está, apenas damos um enter.

$ Field type [string]:

A próxima pergunta é: Qual o comprimento desse campo? E por padrão nos indica “255”, você pode deixar assim, em nosso caso vamos digitar “150”, como é um título, para esse projeto não vejo necessidade de mais, damos enter.

$ Field length [255]:150

Criamos nosso primeiro campo na entidade, como é um post, vamos precisar além do titulo, um conteúdo, quando o post foi criado, e quando foi atualizado, então vamos criar nosso próximo campo, digitamos “content”.

$ New field name (press  to stop adding fields): content

O tipo deste campo será um texto, digitamos “text”.

$ Field type [string]:text

Automaticamente, o console não nos pede o tamanho, pois é um campo do tipo text. Vamos para o nosso próximo campo, por convenção digitamos createdAt.

$ New field name (press  to stop adding fields): createdAt

O tipo será uma data, então digitamos “datetime”.

$ Field type [string]: datetime

Vamos para o próximo campo. Nosso post, pode ser atualizado, e para sabermos qual foi a data de atualização vamos criar um campo para isso, digitamos “updatedAt”, também com o tipo “datetime”.

$ New field name (press  to stop adding fields): updatedAt

Tipo

$ Field type [string]: datetime

Como vamos criar um simples blog, e não vamos fazer nada complexo, deixaremos somente estes campos, você pode criar mais campos como texto de introdução, etc, nesse momento vamos deixar assim, e quando o console nos perguntar novamente sobre um novo campo, não vamos digitar nada, apena vamos dar um enter, para entrarmos no processo de finalização.

$ New field name (press  to stop adding fields):

Após o enter, ele nos pergunta se queremos criar uma classe de repositório vazia para a nossa entidade Post, e por padrão, nos indica [não], no momento não vamos usar repositórios, abordaremos isso mais pra frente, novamente damos enter.

$ Do you want to generate an empty repository class [no]?

Nesse momento o console nos pergunta, se confirmamos a geração da nossa entidade, por padrão ele nos indica [Sim], como queremos, damos enter.

$ Do you confirm generation [yes]? 

Pronto, nossa primeira entidade está criada!

Ao entrarmos em nosso projeto, notaremos que uma pasta “Entity” foi criada, e nela teremos nossa entidade “Post”.

Criando entidades no symfony

Nossa entidade Post criada:

<?php

namespace Tableless\ModelBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post
{
    /**
     * @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)
     */
    private $title;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     */
    private $content;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="createdAt", type="datetime")
     */
    private $createdAt;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="updatedAt", type="datetime")
     */
    private $updatedAt;


    /**
     * 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 createdAt
     *
     * @param \DateTime $createdAt
     * @return Post
     */
    public function setCreatedAt($createdAt)
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    /**
     * Get createdAt
     *
     * @return \DateTime 
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * Set updatedAt
     *
     * @param \DateTime $updatedAt
     * @return Post
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * Get updatedAt
     *
     * @return \DateTime 
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }
}

Configurando a entidade

Vamos fazer algumas modificações.

Daremos o nome “post” pra nossa tabela quando ela for criada, para isso temos que configurar via annotation para que o Doctrine saiba. Para fazer isso vamos adicionar na linha 10 a annotation correspondente:

Obs: As annotations do Doctrine, começam com “@ORM”, o restante é documentação…

Verifique a alteração na linha 10, vamos inserir: (name=”post”).

Caso não façamos essa alteração, não ocorrerá erro, porém por padrão o Doctrine criará uma tabela com o nome da classe “Post”, com a letra “P” maiúscula.

/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity
 */
class Post
{
...

Mudaremos também o nome da coluna “createdAt” para “creadet_at” na linha 41 de nossa entidade, no caso do código abaixo, a linha 4.

/**
 * @var \DateTime
 *
 * @ORM\Column(name="created_at", type="datetime")
 */
 private $createdAt;
...

E a linha 48, de “updatedAt” para “updated_at”, no código abaixo a linha 4.

/**
 * @var \DateTime
 *
 * @ORM\Column(name="updated_at", type="datetime")
 */
 private $updatedAt;
...

Precisamos que, ao criarmos nosso post, seja inserido automaticamente a data de criação, e a data de atualização, para isso vamos criar um método construtor em nossa entidade, veja abaixo:

/**
 * Construct
 */
 public function __construct()
 {
    $this->createdAt = new \DateTime();
    $this->updatedAt = new \DateTime();
 }

Validando

Agora vamos fazer uma validação, para que nossos campos “title” e “content”, não aceitem conteúdos nulos, em brancos. Para isso chamaremos a classe Constraints do Symfony, dando um “use” nesse objeto em nossa entidade, por padrão daremos o apelido de Assert para Constraints. Veja na linha 06:

<?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
{

Para que a validação seja feita colocaremos a annotation “@Assert\NotBlank” nos campos que queremos, veja na linha 5 e na linha 13:

/**
 * @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;

Pronto! Nossa entidade Post está criada e configurada, porém faremos modificações no fututo para que ela se adeque melhor em nosso projeto.

Gerando CRUD com Doctrine ORM

Agora que nossa entidade está concluída, vamos fazer um CRUD, para que possamos inserir, atualizar, visualizar e deletar nossos posts.

Porém antes, precisamos criar nossas tabelas, para fazer isso, vamos voltar para o terminal e digitar:

$ php app/console doctrine:schema:create

Receberemos a resposta do console:

“ATENÇÃO: Esta operação não deve ser executado em um ambiente de produção.

Criação de esquema de banco de dados…

Esquema de banco de dados criado com sucesso!”

ATTENTION: This operation should not be executed in a production environment.

Creating database schema...
Database schema created successfully!

Vamos ao CRUD! Para fazer um CRUD com Doctrine, é extremamente fácil, vamos digitar no terminal:

$ php app/console generate:doctrine:crud

Ao digitarmos o comando acima e darmos o enter, entramos no assistente para criarmos nosso CRUD, e ele nos pede para que informemos a entidade que queremos criar o CRUD, então digitamos: TablelessModelBundle:Post

$ The Entity shortcut name: TablelessModelBundle:Post

Logo após, nos pergunta se queremos gerar as ações de “gravação”, ele nos indica [não], porém nós queremos, então digitamos “yes”.

$ Do you want to generate the "write" actions [no]? yes

Nos pergunta como vamos configurar nosso CRUD, e nos indica [annotation], só damos um enter para prosseguir.

$ Configuration format (yml, xml, php, or annotation) [annotation]:

Após o enter, nos pergunta como vai ser a rota que vamos usar, e nos indica [/post], vamos usar essa mesma, damos um enter.

$ Routes prefix [/post]:

Nos pergunta se confirmamos a geração, e nos indica [yes], sim queremos, damos enter para prosseguir.

$ Do you confirm generation [yes]?

Pronto! CRUD criado, simples não!

Nesse momento vamos fazer uma configuração somente para vermos nossa aplicação rodando, porém não vamos usar essa rota, é somente para vermos se está tudo certo. Entre no arquivo app/config/route.yml e vamos adicionar a rota desse CRUD:

tableless_model:
    resource: "@TablelessModelBundle/Controller/"
    type:     annotation
    prefix:   /

Agora vamos rodar nosso aplicação pelo console:

$ php app/console server:run

E vamos entrar no navegador com a url:

http://127.0.0.1:8000/post/

Para inserirmos um novo post, é só dar um clique, em “Create a new entry”.

Eu inseri um post, com um texto “Lorem Ipsum”, apenas para reprodução das páginas, veja o resultado abaixo:

Lista de posts = url: http://127.0.0.1:8000/post/

Novo post = url: http://127.0.0.1:8000/post/new

Ver o post = url: http://127.0.0.1:8000/post/1

Editar post = url: http://127.0.0.1:8000/post/1/edit

Páginas criadas

Ao navegar pelas páginas, verificará, que não tem estilização, porém está funcional, vamos fazer isso mais tarde, poderá verificar também seu banco de dados, e verá que foi criado um banco de dados com o nome “symfony”, nesse banco, encontrará a tabela “post” que criamos.

Table post no banco de dados

Concluindo

Uma observação importante, estamos criando nossos códigos por linha de comando usando o console, é uma forma de mostrar a agilidade e produtividade, que o Symfony e o Doctrine nos permite, porém para entender o mínimo, o funcionamento do Symfony, ou de qualquer outro framework PHP, é de extrema importância saber PHP Orientado a Objetos, padrão MVC e outros Design Patterns. Fazer a reprodução codificando, sem usar o console, é uma boa forma de aprender e entender o funcionamento do Symfony.

Este projeto encontra-se no gitHub.

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.

Entidade Author criada

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:

Tabela Author

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:

http://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:

autor

Lembrando que a url de autor é:

http://127.0.0.1:8000/author/

e a url de post é:

http://127.0.0.1:8000/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!

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.

Entidade Author criada

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:

Tabela Author

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:

http://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:

autor

Lembrando que a url de autor é:

http://127.0.0.1:8000/author/

e a url de post é:

http://127.0.0.1:8000/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!

No tutorial anterior, criamos a entidade Author, e fizemos o relacionamento com os posts, neste tutorial vamos fazer as configurações adequadas para que possamos deixar nossa aplicação estruturada corretamente, e vamos criar e configurar a página index, onde os usuários terão acesso para visualizar e ler os posts.

Configurando

Vamos começar com as configurações.

Entrando no bundle CoreBundle, caminho: src/Tableless/CoreBundle, exclua a pasta Controller.

Excluindo a pasta controller

Ainda neste mesmo bundle vamos excluir a pasta view, caminho: src/Tableless/CoreBundle/Resources/view.

Excluindo a pasta view

Agora vamos entrar no bundle ModelBundle, caminho: src/Tableless/ModelBundle.

E vamos mover a pasta Controller desse bundle para o bundle CoreBundle

Vamos mover também a pasta view do ModelBundle para o bundle CoreBundle.

Depois das mudanças, nossa estrutura de pastas ficará como na imagem abaixo:

Estrutura de pastas pronta

Configurando os Controllers

Vamos continuar nossas configurações, agora vamos alterar nossos controllers para que os mesmos fiquem de acordo com a estrutura de pasta atual.

Primeiramente, vamos excluir a rota do ModelBundle, pois não vamos usá-la.

Entre no arquivo app/config/routing.yml e exclua as linhas abaixo:

tableless_model:
    resource: "@TablelessModelBundle/Controller/"
    type:     annotation
    prefix:   /

Deixando somente a rota tableless_core como mostrado na imagem abaixo:

Arquivo routing.yml

Agora vamos excluir o DefaultController.php, pois não vamos usar esse controller.

Abra o arquivo AuthorController.php, caminho: src/Tableless/CoreBundle/Controller/AuthorController.php

Na linha 3, mude o namespace.

De: Tableless\ModelBundle\Controller;

Para: Tableless\CoreBundle\Controller;

Na linha 43, mude a annotation:

De: @Template(“TablelessModelBundle:Author:new.html.twig”)

Para: @Template(“TablelessCoreBundle:Author:new.html.twig”)

Agora vamos configurar o controller PostController, caminho: src/Tableless/CoreBundle/Controller/PostController.php, vamos fazer a mesma alteração.

Na linha 3, mude o namespace.

De: Tableless\ModelBundle\Controller;

Para: Tableless\CoreBundle\Controller;

Na linha 43, mude a annotation:

De: @Template(“TablelessModelBundle:Post:new.html.twig”)

Para: @Template(“TablelessCoreBundle:Post:new.html.twig”)

Para verificarmos se correu tudo bem, vamos fazer o teste.

Rode o servidor

$ app/console server:run

Entre nas urls:

http://127.0.0.1:8000/post/

http://127.0.0.1:8000/author/

Se tudo foi configurado corretamente, nossa aplicação voltará a funcionar perfeitamente, veja:

Página index, e show

Criando um Controller

Nesse momento vamos criar um index, para nossa aplicação, para que seja nossa pagina principal, e possamos visualizar os posts de forma correta.

Podemos criar o controller codificando, porém para efeito de didática vamos criar através do console.

Entre no terminal e digite:

$ php app/console generate:controller

Ao digitarmos o comando acima e darmos enter, entramos no assistente do console do Symfony e ele nos comunica: Primeiro, você precisa dar o nome do controlador que você deseja gerar. Você deve usar a notação de atalho como AcmeBlogBundle:Post

Nesse momento digitamos:

$ Controller name: TablelessCoreBundle:IndexControler

Ao darmos o nome do nosso controller e darmos enter, o assistente nos pergunta: Qual o formato que vamos configurar a nossa rota?

E nos indica annotation, vamos deixar como está, e apenas damos enter:

$ Routing format (php, xml, yml, annotation) [annotation]:

A próxima pergunta é:

Qual o formato que vamos usar para template?

Ele mesmo nos indica o twig. Apenas damos enter.

$ Template format (twig, php) [twig]:

Após o enter ele nos pede o nome de nossas ações, que são os métodos que vamos criar para o nosso controller.

A primeira ação (método) vamos chamar de indexAction, e damos enter.

$ New action name (press  to stop adding actions): indexAction

O assistente nos pede a rota, vamos digitar “/” e damos enter.

$ Action route [/index]: /

Nos pergunta o caminho da nossa template, e ele nos indica um caminho, vamos deixar como está e apenas damos enter.

$ Templatename (optional) [TablelessCoreBundle:IndexControler:index.html.twig]:

Novamente o assistente nos pede para darmos um nome para uma nova ação (método), essa nova ação fará com que o post seja visualizado para a leitura, vamos dar o nome de showAction.

$ New action name (press  to stop adding actions): showAction

O assistente nos pede a rota, e nos indica como “/show”, vamos deixar como está e damos enter.

$ Action route [/show]:

Nos pergunta sobre caminho da template, e ele nos indica um caminho, vamos deixar como está e apenas damos enter.

$ Templatename (optional) [TablelessCoreBundle:IndexControler:show.html.twig]:

Ao darmos enter, entramos no modo de criação de uma nova ação, porém não queremos nenhuma outra ação, então apenas damos um enter, para entrarmos no processo de finalização.

$ New action name (press  to stop adding actions):

Ao entramos no processo de finalização o assistente nos pergunta se queremos confirmar a geração desse controller, ele nos indica sim, como queremos, apenas damos enter.

$ Do you confirm generation [yes]?

Pronto! Nosso controller IndexController está criado, juntamente com suas templates, veja a imagem abaixo:

Index controller

Ao entrarmos na url: http://127.0.0.1:8000/, vamos entrar no nosso index, e receberemos a mensagem abaixo:

Welcome to the IndexControler:index page

Criando um repositório

Se entrarmos em nosso controller IndexController, caminho: src/Tableless/CoreBunde/ IndexController.php, vamos perceber que nossos métodos estão criados, porém não estão implementados, vamos implementá-los, mas queremos que nosso index apresente para o usuário os últimos posts escritos, para que sempre o post mais atual seja apresentado em primeiro lugar, para isso devemos criar um repositório para nossa entidade Post.

Para isso, vamos entrar no bundle ModelBundle e crie uma pasta com o nome de Repository, dentro dessa pasta crie uma classe com o nome PostRepository.

Com a classe PostRepository aberta, adicione o nemspace dessa classe, e de um extends na classe EntityRepository do Doctrine, não se esquecendo de dar um use nessa classe.

use Doctrine\ORM\EntityRepository;

Crie um método privado com o nome getQueryBuilder e acrescente a EntityManager para que possamos usar nossa entidade Post, veja abaixo:

private function getQueryBuilder()
{
    $em = $this->getEntityManager();

    $queryBuilder = $em->getRepository('TablelessModelBundle:Post')
    ->createQueryBuilder('p');

    return $queryBuilder;
 }

Agora vamos criar um método publico chamado findAllInOrder, e vamos chamar o método getQueryBuilder(), para ordená-lo da forma que queremos, para que seja mostrado o último post postado primeiro. Veja abaixo:

public function findAllInOrder()
{
    $qb = $this->getQueryBuilder()
    ->orderBy('p.createdAt', 'desc');

    return $qb->getQuery()->getResult();
}

Veja a classe PostRepository pronta:

<?php

namespace Tableless\ModelBundle\Repository;

use Doctrine\ORM\EntityRepository;

class PostRepository extends EntityRepository
{
    private function getQueryBuilder()
    {
        $em = $this->getEntityManager();
        $queryBuilder = $em->getRepository('TablelessModelBundle:Post')
            ->createQueryBuilder('p');
        return $queryBuilder;
    }

     public function findAllInOrder()
    {
        $qb = $this->getQueryBuilder()
            ->orderBy('p.createdAt', 'desc');

        return $qb->getQuery()->getResult();
    }
} 

Agora temos que indicar para a entidade Post via annotation, onde está a classe PostRepository, para isso vamos abrir a entidade Post, caminho:src/Tableless/ModelBundle/Entity/Post.php, e acrescentar a annotation (repositoryClass=”Tableless\ModelBundle\Repository\PostRepository”) na linha 12 de sua entidade, veja:

/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity(repositoryClass="Tableless\ModelBundle\Repository\PostRepository")
 */
class Post extends Timestampable
{
    ...

Implementando o controller

Vamos voltar para nosso IndexController, para que possamos implementá- lo. Abra o IndexController , e no método indexAction() temos que chamar nossa entidade Post, para que através dela chamemos o método findAllInOrder() da classe PostRepository, e retornar o resultado em forma de array, veja abaixo:

public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();

        $posts = $em->getRepository('TablelessModelBundle:Post')->findAllInOrder();

        return [
            'posts' => $posts,
        ];
    } 

Também vamos implementar o método showAction(), para que seja buscado apena o post clickado pelo usuário, para isso devemos passar um parâmetro id, tanto no método, quanto na annotation para rota, para que seja buscado somente o post solicitado. Temos que chamar nossa entidade Post novamente, para que através dela chamemos o método find(), que já vem pré configurado pelo Doctrine, porém se o usuário requisitar um post que não existe, temos que passar uma mensagem de erro, para informá- lo, veja abaixo:

/**
     * @Route("/show/{id}")
     * @Template()
     */
    public function showAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $post = $em->getRepository('TablelessModelBundle:Post')->find($id);

        if (!$post) {
            throw $this->createNotFoundException('O post não existe! Volte para home!');
        }

        return [
            'post' => $post,
        ];
    }

Veja o IndexController pronto:

<?php

namespace Tableless\CoreBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

class IndexControlerController extends Controller
{
     /**
     * @Route("/")
     * @Template()
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();

        $posts = $em->getRepository('TablelessModelBundle:Post')->findAllInOrder();

        return [
            'posts' => $posts,
        ];
    }

     /**
     * @Route("/show/{id}")
     * @Template()
     */
    public function showAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $post = $em->getRepository('TablelessModelBundle:Post')->find($id);

        if (!$post) {
            throw $this->createNotFoundException('O post não existe! Volte para home!');
        }

        return [
            'post' => $post,
        ];
    }
}

Para vemos como está ficando nossa aplicação, abra a index.html.twig, do IndexController, caminho: src/Tableless/CoreBundle/Resouces/views/IndexController/index.html.twig e vamos dar um dump em posts, veja abaixo, vamos adicionar a linha 8:

{% extends "::base.html.twig" %}

{% block title %}TablelessCoreBundle:IndexControler:index{% endblock %}

{% block body %}
Welcome to the IndexControler:index page

    {{ dump(posts) }}
    
{% endblock %}

Vamos fazer a mesma modificação para o arquivo show.html.twig no mesmo diretório, porém ao em vez de dar um dump em posts, vamos dar um dump em post, sem o “s” no final, veja:

{% extends "::base.html.twig" %}

{% block title %}TablelessCoreBundle:IndexControler:show{% endblock %}

{% block body %}
Welcome to the IndexControler:show page

    {{ dump(post) }}
    
{% endblock %}

Para vemos o resultado, entre nas urls:

http://127.0.0.1:8000/

http://127.0.0.1:8000/show/{o id do seu post}

Veja as imagens para comparação:

index – url: http://127.0.0.1:8000/

index

Pagina show – url: http://127.0.0.1:8000/show/4 -> {o id do seu post} no meu caso 4.

show

Conclusão

No tutorial anterior, eu comentei que iriamos configurar nosso projeto e começarmos a trabalhar com o Twig, porém nossa configuração e a criação do index, deixou este tutorial extenso, mas no próximo tutorial, vamos trabalhar com o bootstrap e com o twig para que possamos visualizar nossos post em nossa home da forma adequada e bonita.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

w

Conectando a %s