Tenho recebido diversos e-mails com perguntas relacioandas a como fazer consultas nos dados armazenados. Então, resolvi escrever este artigo para dar continuidade ao primeiro. A base dos arquivos que vou utilizar são os apresentados no artigo anterior.
Preparações
1. Bootstrap
É interesssante fazermos um bootstrap, assim, quando instanciarmos uma entidade, o arquivo será carregado automaticamente e não precisaremos nos preocupar com includes. Para isso, utilizarei a função spl_autoload_register.
Vamos então criar um arquivo chamado “autoload_register”, que deve ficar dentro de “/application/libraries” com o seguinte código:
//Registra o autoload das classes spl_autoload_register('Autoload_Register::register'); /** * Executa o autoload de classes que não são do CI nem de PEAR */ class Autoload_Register { const CI_PREFIX = "CI_"; const PEAR = 'PEAR'; /** * Faz o registro * * @param string $classe * @return void */ public static function register($classe) { $paths = array(); $paths[] = APPPATH . 'models/Entities'; //Classes do CI e do PEAR são ignoradas if (strstr($classe, self::CI_PREFIX) || stristr($classe, self::PEAR)) { //Nada a fazer return; } //Remove o namesapce $vetClasse = explode('\\', $classe); $classe = $vetClasse[count($vetClasse)-1]; //Garante que "$paths" seja um array if (is_array($paths) && count($paths) > 0) { //Localiza arquivos nos paths determinados no inicio da classe foreach ($paths as $dir) { $files = self::find($dir, $classe . EXT); //Se tiver arquivos a serem incluidos, executa o require_once para cada um deles if (is_array($files) && count($files) > 0) { foreach ($files as $file) { if (is_file($file)) require_once $file; } } } } } // ------------------------------------------------------------------------ /** * Procura por arquivos * * @param string $dir * @param string $pattern * @return array */ private function find($dir, $pattern) { // Elimina caracteres inválidos para comandos shell $dir = escapeshellcmd($dir); // pega a lista de todas as coincidencias no diretorio corrente $files = glob("$dir/$pattern"); // procura a lista de todos os sub diretorios no diretorio corrente // diretorios iniciados por um ponto também são incluidos foreach (glob("$dir/{.[^.]*,*}", GLOB_BRACE | GLOB_ONLYDIR) as $sub_dir) { $arr = self::find($sub_dir, $pattern); // chamada revursiva $files = array_merge($files, $arr); // faz um merge no array com os arquivos do sub diretorio } // retorna todos os arquivos encontrados return $files; } }
Criado o arquivo, precisamos colocá-lo no autoload do CI. A chave ‘libraries’ da matriz $autoload deve ficar da seguinte forma:
$autoload['libraries'] = array('database', 'Doctrine', 'autoload_register');
Pronto, quando o CI carregar essa library, a primeira coisa que ela vai fazer é registrar no SPL_AUTOLAD o método “register” da classe “Autoload_Register”. E sempre que for criada uma nova instância de uma classe, o PHP vai passar primeiro por esse método “register”, que por sua vez vai procurar dentro na pasta “models/Entities” o arquivo que ele deve fazer o require.
2. Preparando os dados
Antes de fazer o teste, vou adicionar um campo no banco de dados chamado “idade”. Para isso vou adicionar a propriedade $idade na classe “usuário”. Isso vai facilitar explicações futuras.
/** * @Column(type="integer", nullable=true) * @var type */ public $idade;
E vou rodar um comando do doctrine para atualizar a base de dados:
php doctrine orm:schema-tool:update --force
Feito isso, podemos fazer um teste alterando a forma popular do objeto “usuário” para o seguinte:
public function index() { $usuario = new usuario(); $usuario->set_nome('Ricardo'); $usuario->set_email('meuemail@mail.com.br'); $usuario->set_telefone('11-1122-3345'); $usuario->set_celular('11-9988-7765'); $usuario->set_idade(25); $this->doctrine->em->persist($usuario); $this->doctrine->em->flush(); }
Repare que foi retirado o “$this->load->model(‘Entities/usuario’, ‘usuario’);” do arquivo que fizemos no artigo anterior. Tembém foram implementados os getters e setters da classe “usuario”.
Para melhorar os testes e não precisar gastar a criatividade inserindo diversos usuários, podemos utilizar o site http://www.generatedata.com para gerar dados aleatórios e popular o banco.
Mão na massa
Finalmente vamos ao que interessa: fazer as consultas. Existem diversas formas de consultar um dado com o Doctrine. Vou citar aqui as que mais utilizo e a partir daí cada um poderá utilizar o que achar mais adequado para cada situação.
A documentação está disponível no site do Doctrine.
1. find()
A sintaxe é: $em->find($entidade, $id). A função find() vai procurar na entidade informada o registro com o ID solicitado. Podemos utilizar da segunte forma:
/** * Localiza o usuario para ser editado * * @param int $id */ public function editar($id) { $usuario = $this->doctrine->em->find('usuario', $id); if ($usuario instanceof usuario) { echo 'Nome: ' . $usuario->get_nome() . '<br>'; //Aqui pode carregar a view para editar o usuário } else { echo 'Usuário não localizado'; //Volta para a listagem informando que o usuário não existe } }
2. findOneBy()
A sintaxe é: $em->getRepository($entidade)->findOneBy(array($campo => $valor)). Esta função faz uma busca na entidade informada buscando pelo campo e valores solicitados. Veja um exemplo:
/** * Consulta pelo campo e valor solicitado * * @param string $campo Nome do campo onde se deseja fazer a busca * @param mixed $valor Valor que deve ser localizado * @link http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-simple-conditions */ public function localizar($campo, $valor) { $usuario = $this->doctrine ->em ->getRepository('usuario') ->findOneBy(array($campo => $valor)); if ($usuario instanceof usuario) { echo 'Nome: ' . $usuario->get_nome() . '<br>'; } else { echo 'Usuário não localizado'; } }
Então, para buscar o usuário “ricardo” basta fazer o seguinte:
$this->localizar('nome', 'ricardo');
Isto irá gerar uma SQL assim: SELECT * FROM usuario WHERE nome = ‘ricardo’.
3. DQL – Doctrine Query Language
Para fazermos buscas mais avançadas, podemos utilizar as DQLs (Doctrine Query Language), onde iremos escrever um código muito parecido com a SQL com pequenos detalhe de diferença, veja:
/** * Localiza um usuário que tenha a idade entre dois valores * * @param type $idade_menor * @param type $idade_maior * @link http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-dql DQL */ public function localizar_por_idade($idade_menor, $idade_maior) { $usrs = $this->doctrine ->em ->createQuery("SELECT u FROM usuario u WHERE u.idade >= {$idade_menor} and u.idade <= {$idade_maior}") ->getResult(); if (is_array($usrs) && count($usrs) > 0) { foreach ($usrs as $i => $usuario) { if ($usuario instanceof usuario) { echo "{$i} - Nome: {$usuario->get_nome()}<br>"; } } } }
Veja que interessante, na SQL, no lugar do nome da tabela, utilizei o nome da entidade que criamos e em vez de utilizar um * para trazer todos os campos, utilizei o apelido dado à entidade.
Dessa forma, poderemos inclusive fazer joins quando necessário. No próximo artigo vou escrever sobre o relacionamento de entidades. Vamos gerar a entidade “endereco” e por fim faremos uma busca com join entre as tabelas para mostrar o poder da DQL.
Conclusão
O Doctrine nos dá muita rapidez no desenvolvimento – isso será percebido mais ainda no próximo artigo, quando eu mostrar os relacionamentos entre entidades e a facilidade de acessso aos dados.
Existem outras forma de fazer busca no Doctrine, mas espero que com essas que apresentei aqui, seja possivel você começar a entender melhor como ele funciona e partir para novas implementações. Os arquivos do artigo estão disponíveis aqui.
No próximo artigo irei abordar o relacionamento entre entidades.
Espero que esse artigo possa ajudar vocês. Um abraço e até o próximo!