Olá, tudo bem?!
No ecossistema PHP temos disponíveis diversos projetos super bacanas, e para trabalhar na camada de banco de dados temos a nossa disposição o Doctrine, o mais popular projeto voltado para Database Storage e Object Mapping.
O que você pode estar pensando é “Meu Framework X já tem implementações para isso”, sim de fato todos os grandes frameworks de mercado tem disponível componentes para abstração de banco de dados, porem uma das grandes vantagens do Doctrine é justamente ele poder ser utilizado em praticamente qualquer framework ou estrutura de projeto.
A partir do momento que você estrutura sua aplicação para utilizar o Doctrine nada o impedirá que no futuro você migre toda a lógica para outro projeto, essa facilidade vai lhe poupar muitas dores de cabeça quando chegar o momento de refatorar sua aplicação.
Sem mais delongas, vamos analisar a estrutura que será utilizada neste post, abaixo segue estrutura de diretórios e arquivo.
- exemplo-doctrine
- config
- Será armazenado toda a lógica de configuração da aplicação.
- db
- Responsável por armazenar o banco de dados (SQLite) e o dump que será gerado.
- src
- Responsável por conter toda a lógica da nossa aplicação.
- composer.json
- Configurações do composer para o projeto.
- config
Conforme apresentado acima, crie a estrutura de diretórios necessários para a execução deste post.
Instalando as dependências necessárias
Abra o arquivo composer.json e insira o seguinte código:
{
"require": {
"doctrine/orm": "^2.5",
"doctrine/dbal": "^2.5",
"symfony/yaml": "^3.2"
},
"autoload":{
"psr-4":{
"DiegoBrocanelli\\":"src/"
}
}
}
Como podemos observar, estamos utilizando os componentes do Doctrine e uma dependência do Symfony. Após a inserção das configurações, acesse a raiz do projeto e execute o seguinte comando pelo terminal:
composer install
Aguarde o processo de instalação que pode levar alguns minutos dependendo da sua conexão de internet.
Configurando o projeto
Agora devemos criar nossas configurações necessárias para utilização do componente, acesse o diretório ‘exemplo-doctrine/config’ e crie o arquivo ‘bootstrap.php’ . Neste arquivo iremos centralizar as configurações necessárias para execução do Doctrine, segue código abaixo:
<?php
// Importação do autoload do composer
require_once __DIR__.'/../vendor/autoload.php';
// Importação dos pacotes necessários
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
// Criar uma configuração ORM do Doctrine simples "default" para utilizar Annotations
$isDevMode = true;
$configuration = Setup::createAnnotationMetadataConfiguration(
[__DIR__.'/../src'],
$isDevMode
);
// Configurações do banco de dados
// Estamos utilizando o SQLite, para facilitar a reprodução do post
$connection = [
'driver' => 'pdo_sqlite', // Vamos utilizar o drive pdo do sqlite
'path' => __DIR__.'/../db/db.sqlite' // caminho onde será armazenado o DB.
];
// Obtemos o Entity Manager
$entityManager = EntityManager::create($connection, $configuration);
Observação: O Doctrine suporta configurações por Annontation, XML ou YAML, porem na minha opinião as annontations é a forma mais clara e fácil de definir sua entidade, gerando o menor número de arquivos possível.
Após a criação das configurações, devemos criar um arquivo que nos auxiliará nas execuções dos comandos pelo terminal. Crie o arquivo ‘cli-config.php_no diretório ‘exemplo-doctrine/config’ , segue código abaixo:
<?php
// Importação do autoloader do composer
require __DIR__.'/bootstrap.php';
// Retorna o componente que nos auxilia na utilização do Schema tool
// Necessário para gerar Tabelas para trabalhar com metadados
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
Criação da entidade Products
Com as devidas configurações realizadas, o próximo passo é criar a entidade que representara nossa tabela do banco de dados, para isso crie a classe ‘Products.php’ dentro do diretório ‘exemplo-doctrine/src’, segue código abaixo:
<?php
namespace DiegoBrocanelli;
/**
* @Entity @Table(name="products")
*/
class Product
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
protected $id;
/**
* @Column(type="string")
*/
protected $name;
/**
* @Column(type="datetime")
*/
protected $created;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function setCreated(\DateTime $created)
{
$this->created = $created;
}
public function getCreated()
{
return $this->created;
}
}
Como podemos observar, o código acima é bem simples contendo apenas atributos e métodos getters e setters, atentando ao fato de não termos criado PHPDoc, para que assim foquemos apenas nos conceitos das annontations minimizando conflito de entendimento. É por meio dessas annontations que iremos ‘informar’ ao doctrine nossa entidade e suas especificações, segue abaixo maiores detalhes sobre as annontations utilizadas:
- Class Products
- @Entity
- Responsável por informar ao Doctrine que esta nossa classe é nossa entidade.
- @Table(name=”products”)
- Informamos que nossa entidade representa a tabela ‘products’< contida no banco de dados.
- @Entity
- Column id
- @Id
- Informa que essa coluna é primary key.
- @Column(type=”integer”)
- efine o tipo “inteiro” para a coluna.
- @GeneratedValue
- Informa o desejo de auto increment< para os valores dessa coluna.
- @Id
- Column name
- @Column(type=”string”)
- Define o tipo ‘string’ para a coluna.
- @Column(type=”string”)
- Column created
- @Column(type=”datetime”)
- Define o tipo Datetime< para a coluna.
- Observação: Assim que implementarmos a pesquisa ficará mais claro a estrutura do objeto gerado pelo Doctrine.
- @Column(type=”datetime”)
Após nossa entidade devidamente criada, podemos dar início a implementação dos nosso recursos.
Criando nosso banco de dados
Após a criação da entidade devemos realizar a criação do banco de dados, acesse a raiz do projeto e execute o seguinte comando:
vendor\bin\doctrine orm:schema-tool:create
- vendor\bin\doctrine
- Esta parcela do comando referencia a utilização do Doctrine que instalamos com o Composer.
- orm:schema-tool:creat
- Comando para criação do banco de dados, lembrando que o arquivo será armazenado em ‘exemplo-doctrine/db/db.sqlite’<.
Após execução do comando o retorno deve ser semelhante ao apresentado abaixo:
ATTENTION: This operation should not be executed in a production environment.
Creating database schema...
Database schema created successfully!
Como o próprio retorno nos informa, nunca devemos utilizar esta ação em produção, apenas em ambiente de desenvolvimento!
Inserindo registros no banco de dados
Para a implementação do recurso de insert, crie a classe _‘CreateProduct.php’ dentro do diretório _‘exemplo-doctrine/src’, segue código abaixo:
<?php
// Importamos o autoload do composer
require_once __DIR__.'/../config/bootstrap.php';
// Como vamos trabalhar com envio de dados passados pelo terminal
// apenas garanto que haja valor para que seja processado a ação.
if (isset($argv)) {
// coletamos o nome passado pelo usuário no terminal
$newProductName = $argv[1];
// Instanciamos nossa entidade Products
$product = new DiegoBrocanelli\Product();
// Passamos o novo nome para a entidade
$product->setName($newProductName);
// Passamos a data de criação para a aentidade
$product->setCreated(new \DateTime(date('Y-m-d H:i:s')));
// Persistimos seus dados
$entityManager->persist($product);
// Descarregamos a ação
$entityManager->flush();
// Para melhor visualização do resultado, retornamos uma mensagem
// com o id do registro salvo no DB.
echo 'Created Product with ID '.$product->getId()."\n";
}
Para realizarmos a inserção de dados, execute o seguinte comando pelo terminal na raiz do projeto:
php src\CreateProduct.php produto_1
Resultado:
Created Product with ID 1
Como podemos observar, com poucas linhas de código criamos um recurso simples para inserção de registro no banco de dados. 🙂
Pesquisando todos os registros cadastrados
Para a implementação do recurso de select, crie a classe _‘ListProduct.php’ dentro do diretório _‘exemplo-doctrine/src’, segue código abaixo:
<?php
// Importamos o autoload do composer
require_once __DIR__.'/../config/bootstrap.php';
// Como vamos trabalhar com envio de dados passados pelo terminal
// apenas garanto que haja valor para que seja processado a ação.
if (isset($entityManager)) {
//Importamos o repository que nos auxiliará com a pesquisa.
$productRepository = $entityManager->getRepository('DiegoBrocanelli\Product');
// Podemos acessar o método findAll() responsável por retornar todos os
// registros cadastrados em nossa tabela products
$products = $productRepository->findAll();
// Realizamos uma iteração de dados
foreach ($products as $product) {
// Exibimos o resultado de cada registro encontrado
var_dump($product);
}
}
Exemplo de retorno de pesquisa:
class DiegoBrocanelli\Product#64 (3) {
protected $id =>
int(1)
protected $name =>
string(8) "'produto_1"
protected $created =>
class DateTime#61 (3) {
public $date =>
string(26) "2017-05-16 23:53:49.000000"
public $timezone_type =>
int(3)
public $timezone =>
string(17) "America/Sao_Paulo"
}
}
Como podemos observar, com apenas a chamada do método findAll() obtemos todos os registros inseridos em nossa tabela no banco de dados, o atributo ‘created’ é um objeto do tipo DateTime conforme configurado na entidade.
Pesquisando um produto em específico
Para a implementação do recurso que irá pesquisar um produto em especifico crie a classe ‘ShowProduct.php’ dentro do diretório‘exemplo-doctrine/src’, segue código abaixo:
<?php
// Importamos o autoload do composer
require_once __DIR__.'/../config/bootstrap.php';
// Como vamos trabalhar com envio de dados passados pelo terminal
// apenas garanto que haja valor para que seja processado a ação.
if (isset($argv)) {
// Recebemos o id informado
$id = (int)$argv[1];
// Nosso entity manager nos fornece acesso ao método find()
// no qual nos permite informar nossa entidade e o id para pesquisa
$product = $entityManager->find('DiegoBrocanelli\Product', $id);
// Caso não seja encontrado nenhum produto com o id desejado
// será retornado mensagem informando o usuário
if($product === null){
echo "No product found. \n";
exit(1);
}
// Caso o produto seja encontrado exibimos seu nome e data de criação
echo sprintf('-%s\n', $product->getName() . ' - ' . $product->getCreated());
}
Como podemos observar, com o método find() podemos informar nossa entidade e o id do registro ao qual desejamos encontra na tabela.
Atualizar produtos
Para a implementação do recurso de update< crie a classe ‘UpdateProduct.php’ dentro do diretório <‘exemplo-doctrine/src’, segue código abaixo:
<?php
// Importamos o autoload do composer
require_once __DIR__.'/../config/bootstrap.php';
// Como vamos trabalhar com envio de dados passados pelo terminal
// apenas garanto que haja valor para que seja processado a ação.
if (isset($argv)) {
// Recebemos o id do registro a ser atualizado
$id = $argv[1];
// Recebemos o novo nome do produto
$newName = $argv[2];
// Pesquisamos para valizar existência do produto no banco de dados
$product = $entityManager->find('DiegoBrocanelli\Product', $id);
// Caso produto não seja localizado, será retornado mensagem infromando o usuário
if ($product === null) {
echo "Product $id does not exist.\n";
exit(1);
}
// Informamos o nome atual
echo 'Old name: ' . $product->getName() ."\n";
// Inserimos o novo nome desejado para o produto
$product->setName($newName);
// Executamos a ação de update
$entityManager->flush();
// Retornamos para o usuário o produto com seu novo nome
echo 'New name: ' . $product->getName() ."\n";
}
Como podemos observar, o processo de update ocorre sem mistérios de forma clara e fácil apenas manipulando o objeto e passando para que o Doctrine< faça todo o trabalho.
Remover produtos
Para a implementação do recurso de _delete <_crie a classe ‘DeleteProduct.php’ dentro do diretório <‘exemplo-doctrine/src’, segue código abaixo:
<?php
// Importamos o autoload do composer
require_once __DIR__.'/../config/bootstrap.php';
// Como vamos trabalhar com envio de dados passados pelo terminal
// apenas garanto que haja valor para que seja processado a ação.
if (isset($entityManager)) {
// Recebemos o id do registro a ser atualizado
$id = $argv[1];
// Pesquisamos para valizar existência do produto no banco de dados
$product = $entityManager->find('DiegoBrocanelli\Product', $id);
// Caso produto não seja localizado, será retornado mensagem infromando o usuário
if ($product === null) {
echo "Product $id does not exist.\n";
exit(1);
}
// Executamos a ação de remoção
$entityManager->remove($product);
// Efetiva a ação de remoção
$entityManager->flush();
// Retorna mensagem informativa de ação realizada com sucesso para o usuário
echo 'Product remove successfully!';
}
Como podemos observar, o processo de remoção é similar a todos os demais processo, o Doctrine tem métodos descritivos que facilitam sua utilização 🙂
Remover o banco de dados (Drop database)
No dia a dia temos a necessidade de apagar nosso banco de dados, para isso o Doctrine< disponibiliza um recurso para nos auxiliar. Na raiz do nosso projeto execute o seguinte comando:
Execute este comando com parcimônia, após sua execução seu banco de dados será APAGADO!
// Comando para APAGAR o banco de dados
vendor\bin\doctrine orm:schema:drop --force
Após execução do comando será exibido uma mensagem de retorno semelhante a descrita abaixo:
Dropping database schema...
Database schema dropped successfully!
Gerar dump da nossa base de dados
Um dos inúmeros recursos bacanas que o D_octrine<_ dispõem é a criação de um arquivo dump< da base de dados, arquivo que conterá toda estrutura da tabela products<, para geramos nosso arquivo execute o seguinte comando na raiz do projeto:
// Comando para geração de arquivo de dump da base de dados
vendor\bin\doctrine orm:schema-tool:create --dump-sql > db/dump.sql
Após execução com sucesso do comando, será criado nosso arquivo no diretório ‘exemplo-doctrine/db/dump.sql’<, contendo a seguinte estrutura:
CREATE TABLE products (
id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
created DATETIME NOT NULL, PRIMARY KEY(id)
);
Observação: O retorno se dá um uma única linha, porém para que fique melhor sua visualização no post foi quebrado em linhas.
No decorrer deste post podemos observar o quão fácil é a utilização do Doctrine, infelizmente apenas abordamos uma pequena fração de todo o potencial e recursos que ele nos fornece. Caso tenha interesse em seguir com seus estudos indico que adquira o livro escrito pelo Elton Minneto “Doctrine na prática” antes que alguém pense o contrário não estou ganhando nenhum centavo para indicar o livro, estou fazendo pois é o melhor livro de Doctrine< em PT-BR escrito por um profissional que respeito e admiro.
Espero que tenham apreciado o post, ficou um pouco maior do que eu desejava, porem para abordar todo o ciclo de utilização tentei ser o mais descritivo possível para auxiliar a máximo a compreensão das etapas, como dito anteriormente este post é introdutório onde existem uma infinidade de recursos não explorados que valem muito a pena serem estudados.
Duvidas, sugestões, crítica ou elogios recomendo que deixe nos comentários para que assim possamos interagir e gerar mais conhecimento.
Até a próxima, abraços! 🙂