Como vimos neste artigo, é possível obter classes de entidades existentes em nosso banco de dados com o Doctrine. Neste artigo, vou explanar mais detalhadamente dois métodos de realizar isto elaborando um projeto simples, na prática. O primeiro é via cógido PHP; o outro é através de linha de comando.
Iremos utilizar a seguinte hierarquia de pasta:
Na hierarquia acima, criei a pasta src, que conterá nosso código de desenvolvimento com as classes do projeto organizadas em subpastas. A subpasta Entity terá, como o nome sugere, as entidades de fato. A pasta vendor conterá as bibliotecas PHP que vamos utilizar ao longo do desenvolvimento.
O Composer automaticamente cria esta pasta, porém criei para modos didático afim de organizar nosso código.
Agora, através do composer vamos obter as bibliotecas que precisamos. Crie o arquivo composer.json na raiz do nosso projeto, conforme abaixo:
Caso precise, veja detalhes da instalação do composer aqui:
{
"require": {
"doctrine/orm": "2.4.*",
"symfony/yaml": "2.*"
},
"autoload": {
"psr-4": {
"App\\": "src"
}
}
}
O arquivo tem o doctrine/orm e o symfony/yaml como bibliotecas requeridas, e no autoloader utilizamos a especificação PSR-4, tendo em vista que este padrão nos dá flexibilidade de criar o namespace ‘App’ dentro da pasta ‘src’, algo que não seria possível com o PSR-0. Caso queira mais informações sobre especificação PSR-4, clique aqui.
Coloquei o Symfony/Yaml como dependência porque o Doctrine sugere tal biblioteca e precisaremos dela em nosso projeto para exportar as entidades no formato yml.
No terminal rodamos o composer:
E na pasta vendo estarão nossas bibliotecas e o autoloader:
Agora já temos nossa hierarquia de pasta, a definição de nosso autoloader e a biblioteca Doctrine devidamente configurada. Vamos ver nosso banco de dados.
Sistema de cadastro de atletas
Digamos que faremos um sistema de cadastro de atletas e eles podem estar em uma ou mais modalidades, ter um ou mais e-mails e telefones. Criei o banco de dados em MySQL e modelei utilizando o Workbench, que é open source e pode ser encontrado aqui.
Aqui é um dos passos de grande importância no desenvolvimento de software. Uma modelagem de banco de dado mal elaborada acarreta diversos problemas no decorrer do desenvolvimento e um reparo de erro de modelagem pode custar caro se não for identificado antes do desenvolvimento de fato.
Veja nossa modelagem feita:
Veja que do relacionamento entre atleta e modalidade surgiu uma tabela cuja a finalidade é apenas guardar o id de cada entidade, afim de definir um vínculo de uma com a outra. Qual o motivo pelo qual não fizemos o mesmo com o e-mail e com o telefone? Porque se trata de um relacionamento “N pra N”: um atleta pode ter muitas modalidades e uma modalidade pode ter muitos atletas. Com o telefone é diferente. Um atleta pode ter muitos telefones, mas um telefone pertence a apenas um atleta; a mesma coisa com o e-mail. O objetivo não é aprofundar na normalização de banco de dados mas fiz questão de ressaltar bem esse passo, pois o considero muito importante.
Segue o código de nosso modelo:
CREATE SCHEMA IF NOT EXISTS `imasters` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `imasters` ;
CREATE TABLE IF NOT EXISTS `imasters`.`atleta` (
`id` INT NOT NULL AUTO_INCREMENT,
`nome` VARCHAR(80) NOT NULL,
`cpf` CHAR(11) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `imasters`.`modalidade` (
`id` INT NOT NULL AUTO_INCREMENT,
`nome` VARCHAR(60) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `imasters`.`telefone` (
`id` INT NOT NULL AUTO_INCREMENT,
`atleta_id` INT NOT NULL,
`numero` INT(9) NOT NULL,
`ddd` INT(3) NOT NULL,
PRIMARY KEY (`id`),
INDEX `fk_telefone_atleta1_idx` (`atleta_id` ASC),
CONSTRAINT `fk_telefone_atleta1`
FOREIGN KEY (`atleta_id`)
REFERENCES `imasters`.`atleta` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `imasters`.`email` (
`id` INT NOT NULL AUTO_INCREMENT,
`atleta_id` INT NOT NULL,
`email` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`),
INDEX `fk_email_atleta1_idx` (`atleta_id` ASC),
CONSTRAINT `fk_email_atleta1`
FOREIGN KEY (`atleta_id`)
REFERENCES `imasters`.`atleta` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `imasters`.`atleta_modalidade` (
`atleta_id` INT NOT NULL,
`modalidade_id` INT NOT NULL,
PRIMARY KEY (`atleta_id`, `modalidade_id`),
INDEX `fk_atleta_has_modalidade_modalidade1_idx` (`modalidade_id` ASC),
INDEX `fk_atleta_has_modalidade_atleta_idx` (`atleta_id` ASC),
CONSTRAINT `fk_atleta_has_modalidade_atleta`
FOREIGN KEY (`atleta_id`)
REFERENCES `imasters`.`atleta` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_atleta_has_modalidade_modalidade1`
FOREIGN KEY (`modalidade_id`)
REFERENCES `imasters`.`modalidade` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Engenharia reversa
Com o banco de dados feito, agora podemos criar nossas entidades. Vamos utilizar o Doctrine para exportar do banco de dados cada tabela (ou as mais importantes) e transformá-las em classes para que possamos trabalhar.
Vamos criar na raiz no nosso projeto o arquivo bootstrap.php. Ele é responsável em criar o uma instância EntityManager, que irá gerenciar as ações das entidades.
<?php
require_once "vendor/autoload.php";
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
$paths = array("/src");
$isDevMode = true;
$dbParams = array(
'dbname' => 'imasters',
'user' => 'root',
'password' => 'root',
'host' => '127.0.0.1',
'driver' => 'pdo_mysql',
);
$config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
$entityManager = EntityManager::create($dbParams, $config);
Este é um arquivo padrão no Doctrine, onde eu basicamente informo em qual path está o código do meu projeto, qual os dados do banco que irei trabalhar e qual ambiente que estou desenvolvendo.
O Doctrine permite quatro tipo de exportações de mapeamento: XML, YML, PHP e Annotation. Todos com suas devidas características trazem o mapeamento do banco transcrevendo-o em arquivos para serem utilizados.
Há uma maneira de exportar com PHP, chamando o arquivo .php pelo browser ou pelo console, porém o Doctrine tem uma ferramenta que permite, através de linha de comando, realizar o mesmo procedimento, dentre outros.
Para tal ferramenta funcionar, é requerido o arquivo cli-config.php. Nele conterá o HelperSet, com informações que já configuramos no arquivo bootstrap.php.
Vejamos abaixo as duas formas:
O arquivo export.php (exportando via código)
<?php
// Requerimos o arquivo bootstrap.php com as configurações de banco de dados e retornando o EntityManager
require_once 'bootstrap.php';
/*
* Estamos agora recuperando os metadados com o DatabaseDriver
*/
$entityManager->getConfiguration()->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$entityManager->getConnection()->getSchemaManager()
)
);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($entityManager);
$metadata = $cmf->getAllMetadata();
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
/*
* Obtemos uma instancia Exporter e exportamos os arquivos a pasta determinada como segundo parâmetro.
*
*/
$exporter = $cme->getExporter('xml', 'src/Entity');
//$exporter->setEntityGenerator(new \Doctrine\ORM\Tools\EntityGenerator());
$exporter->setMetadata($metadata);
$exporter->export();
Observações:
- No código $exporter = $cme->getExporter(‘yml’, ‘src/Entity’); onde está ‘yml’, você pode substituir pelo modo que desejar (php, xml ou annotation). O segundo parâmetro diz respeito à pasta onde você quer exportar as Entidades;
- Veja que deixei o código: $exporter->setEntityGenerator( new\Doctrine\ORM\Tools\EntityGenerator() ); comentado, precismos deste código para exportar arquivos com Annotation do Doctrine. Eu, particularmente, sempre exporto para o modo Annotation. É uma escolha pessoal e talvez o costume de já haver trabalhado bastante com hibernate no Java.
Veja abaixo o resultado:
O arquivo cli-config.php (exportando via linha de comando)
<?php // Requerimos o arquivo bootstrap.php com as configurações de banco de dados e retornando o EntityManager require_once 'bootstrap.php'; /* * Aqui estamos setando o HelperSet, o Doctrine exige esta configuração. o DB que é a conexão e EM é o EntityManager. * No arquivo boostrap o configuramos. * */ $helperSet = new \Symfony\Component\Console\Helper\HelperSet(array( 'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($entityManager->getConnection()), 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager) )); return $helperSet;
Em nosso console:
Note que coloquei a exportação do modo Annotation na pasta Entity, pois, caso eu colocasse na pasta src, o código iria reclamar que já existe os arquivos exportados e tentaria substituí-los.
No nosso projeto você verá algo como:
Agora é só se preocupar com a lógica de negócio.
Aqui se encerra nosso artigo, tudo de bom para todos. Até a próxima.
Sugestões e dúvidas? Podem comentar abaixo, que eu procurarei responder a todos.











