Arquitetura de Software com CodeIgniter – Integrando o Doctrine
Neste quarto artigo sobre uma Arquitetura de Software com o CodeIgniter, mostrarei como fazer a integração do framework de persistência Doctrine com o framework CodeIgniter. Como explicado anteriormente, o Doctrine foi escolhido por fornecer formas robustas e flexíveis de se realizar consultas com uma sintaxe orientada a objetos, o que os desenvolvedores do projeto chamam de Doctrine Query Language (DQL), o que lembra a Hibernate Query Language (HQL) do Java. Além disso, ele fornece classes de persistência para cada tabela do banco de dados.
Agora saindo um pouco da teoria, é hora de por a mão na massa e começar a integração do framework. Antes de mais nada é necessário fazer o download da última versão estável do Doctrine, de preferência baixar a “Sandbox”, é possível encontrá-la na página oficial do projeto.
Integrando o Doctrine
A primeira etapa é criar uma pasta chamada doctrine dentro de system/database, do CodeIgniter. Após o download do pacote do Doctrine ser concluído, basta extraí-lo e copiar todo o conteúdo localizado na pasta lib deste pacote para a pasta system/database/doctrine. Agora, esta pasta deve conter uma pasta chamada Doctrine e um script PHP chamado Doctrine.php.
A segunda etapa é configurar algumas propriedades do Doctrine para que ele possa ser carregado e para deixá-lo apto a se conectar com um banco de dados. Dentro do arquivo system/application/config/database.php, basta localizar a linha com o seguinte trecho:
$db['default']['cachedir'] = "";
Logo abaixo desta linha, insira o seguinte trecho:
//Configura os dados da fonte de dados, declarados acima
$db['default']['dsn'] = $db['default']['dbdriver'] .
'://' . $db['default']['username'] .
':' . $db['default']['password'] .
'@' . $db['default']['hostname'] .
'/' . $db['default']['database'];
//Inclui a classe do Doctrine
require_once realpath(dirname(__FILE__) . '/../..') . DIRECTORY_SEPARATOR . 'database/doctrine/Doctrine.php';
//Seta o autoloader de classes
spl_autoload_register(array('Doctrine', 'autoload'));
//Carrega a conexão do Doctrine com a string de DSN configurada e o banco de dados escolhido
Doctrine_Manager::connection($db['default']['dsn'], $db['default']['database']);
//Seta a forma de se carregar os models para o tipo "conservative" ou "lazy initiation"
Doctrine_Manager::getInstance()->setAttribute('model_loading', 'conservative');
//Carrega os modelos para o autoloader
Doctrine::loadModels(realpath(dirname(__FILE__) . '/..') . DIRECTORY_SEPARATOR . 'models');
Este código define um Nome de Fonte de Dados (Data Source Name – DSN), depois inclui a classe Doctrine, configura o autoloader do framework, inicia a conexão com o banco de dados pelo DSN definido e no banco de dados definido neste arquivo de configuração (no trecho $db['default']['database'] = “”), seta para que o carregamento de classes seja “lazy”, ou seja, as classes agregadas são carregadas de acordo com a necessidade e, por último, seta a pasta para o Doctrine procurar por seus Models.
Após definir este trecho de código, deve-se garantir que o index.php da aplicação carregue o arquivo de configuração do banco de dados. Para que isso seja feito, basta entrar no arquivo index.php na raíz do CodeIgniter e modificar suas últimas duas linhas, deixando assim:
require_once APPPATH . 'config/database.php'; require_once BASEPATH . 'codeigniter/CodeIgniter'.EXT;
Configurando a Interface de Linha de Comando
O Doctrine possui uma interface executada em linha de comando que pode auxiliar muito na geração dos models, migrações e etc. É sempre bom deixar esta interface funcionando, em qualquer projeto que utilize o Doctrine. Primeiramente, deve-se criar o arquivo doctrine na pasta system/application, sem extensão nenhuma mesmo, e dar um chmod para que ele possa ser executado (chmod a+x). Este arquivo deve ter o seguinte conteúdo:
#!/usr/bin/env php
<?php
chdir(dirname(__FILE__));
include('doctrine.php');
Agora, é necessário criar o arquivo doctrine.php na pasta system/application e colocar o seguinte conteúdo nele:
<?php
require_once 'config/database.php';
//Configura o Doctrine Cli
$config = array('data_fixtures_path' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '/fixtures',
'models_path' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '/models',
'migrations_path' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '/migrations',
'sql_path' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '/sql',
'yaml_schema_path' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '/schema');
$cli = new Doctrine_Cli($config);
$cli->run($_SERVER['argv']); ?>
Ele será responsável por configurar cada pasta onde o Cli poderá atuar para realizar suas funcionalidades. A próxima etapa é criar os seguintes diretórios dentro de system/application:
- fixtures
- migrations
- schema
- sql
Agora é necessário setar o banco de dados que a aplicação utilizará, por exemplo, tarefas_ci:
$db['default']['database'] = "tarefas_ci";
Após estes passos estarem concluídos, a interface com a linha de comando deverá estar funcionando. Para testá-la, acesse um terminal e digite os seguintes comandos (para sistemas GNU/Linux), onde “suapasta” corresponde ao endereço físico da pasta da aplicação:
$ cd suapasta/system/application $ php doctrine
Caso apareça uma mensagem dizendo “No direct script access allowed” será necessário acessar o arquivo database.php no diretório system/application/config e comentar o trecho:
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Importante: deixe este trecho comentando somente para utilizar o Cli, deixe-o descomentado principalmente no ambiente de produção, por questões de segurança. O resultado de executar o Cli é o seguinte:
Doctrine Command Line Interface doctrine generate-migrations-models doctrine create-tables doctrine migrate doctrine build-all-load doctrine dump-data doctrine dql doctrine generate-yaml-db doctrine generate-models-db doctrine rebuild-db doctrine create-db doctrine generate-migrations-diff doctrine drop-db doctrine load-data doctrine generate-migration doctrine build-all doctrine generate-yaml-models doctrine build-all-reload doctrine compile doctrine generate-migrations-db doctrine generate-models-yaml doctrine generate-sql
Testando a Integração
Após várias etapas é hora de efetuar operações para testar a interface de linha de comando do Doctrine e a integração do mesmo com o CodeIgniter. O banco de dados utilizado nesta etapa é o seguinte:
CREATE DATABASE tarefas_ci;
USE tarefas_ci;
CREATE TABLE `tarefas` (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
titulo VARCHAR(200) NOT NULL,
situacao ENUM('A', 'F') DEFAULT 'A'
)ENGINE=InnoDB;
A configuração do banco de dados, encontrada no arquivo database.php da pasta system/application/config, deverá ser algo como o seguinte, onde os atributos hostname, username e password deverão ser configurados de acordo com os dados do banco:
//Host do banco de dados $db['default']['hostname'] = "localhost"; //Usuário para se conectar com o banco $db['default']['username'] = "root"; //Senha do usuário do banco de dados $db['default']['password'] = ""; //Banco de dados a se conectar $db['default']['database'] = "tarefas_ci"; //Driver de conexão com o banco $db['default']['dbdriver'] = "mysql";
Primeiramente, é necessário criar o schema para o Doctrine poder gerar os models de acordo com o banco de dados, este arquivo segue o padrão YAML para definição dos dados. Para isso basta criar o arquivo schema.yml na pasta system/application/schema e colocar o seguinte conteúdo nele:
Tarefas:
columns:
id:
primary: true
autoincrement: true
type: integer(10)
titulo:
type: string(200)
situacao:
type: enum
length: 2
values: ['A', 'F']
Agora, basta executar o seguinte comando, para os models serem criados em system/application/models:
$ php doctrine generate-models-yaml generate-models-yaml - Generated models successfully from YAML schema
Ao verificar o conteúdo da pasta system/application/models, os seguintes arquivos devem estar presentes: Tarefas.php e generated/BaseTarefas.php. O conteúdo destes arquivos são, respectivamente:
abstract class BaseTarefas extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('tarefas');
$this->hasColumn('id', 'integer', 10, array('primary' => true, 'autoincrement' => true, 'type' => 'integer', 'length' => '10'));
$this->hasColumn('titulo', 'string', 200, array('type' => 'string', 'length' => '200'));
$this->hasColumn('situacao', 'enum', 2, array('type' => 'enum', 'length' => 2, 'values' => array(0 => 'A', 1 => 'F')));
}
}
class Tarefas extends BaseTarefas
{
}
Agora é necessário criar o arquivo TarefasDAO.php na pasta system/application/models, ele terá o seguinte conteúdo:
<?php
class TarefasDAO
{
/**
* Seleciona as tarefas do banco de dados, retornando um array de objetos
* @access public
* @return array = Array de objetos Tarefa
*/
public function allData()
{
$query = new Doctrine_Query();
$query->from('Tarefas t');
$query->orderby('t.id DESC');
return $query->execute();
}
}
?>
Agora, pode-se carregar alguns dados para a aplicação, para popular o banco e posteriormente fazer algumas consultas. O primeiro passo para fazer isso é criar uma fixture que é utilizado para o Doctrine popular o banco, para isto basta criar o arquivo tarefas.yml na pasta system/application/fixtures e colocar o seguinte conteúdo nele:
Tarefas:
teste:
titulo: teste
situacao: A
teste2:
titulo: teste2
situacao: F
Então basta executar o seguinte comando no terminal:
$ php doctrine build-all-reload build-all-reload - Are you sure you wish to drop your databases? (y/n) y build-all-reload - Successfully dropped database for connection "tarefas_ci" named "tarefas_ci" build-all-reload - Generated models successfully from YAML schema build-all-reload - Successfully created database for connection "tarefas_ci" named "tarefas_ci" build-all-reload - Created tables successfully build-all-reload - Data was successfully loaded
Neste ponto o banco está devidamente configurado e possui 2 registros inseridos. Agora basta verificar se o Doctrine foi corretamente integrado no CodeIgniter. No Controller welcome.php localizado na pasta system/application/controllers, coloque o seguinte conteúdo:
<?php
class Welcome extends Controller
{
public function Welcome()
{
parent::Controller();
$this->load->library('smartyview');
}
public function index()
{
$dao = new TarefasDAO();
$tarefas = $dao->allData();
$this->smartyview->assign_by_ref('tarefas', $tarefas);
$this->smartyview->display('index.tpl');
}
}
?>
Nele pega-se a classe DAO, então é executado o método que retorna um array de todas as tarefas ordenadas pelo ID em ordem decrescente, passa-se este array de tarefas para a View e a exibe ao usuário. Agora basta então criar a View, ela ficará localizada na pasta system/application/views com o nome index.tpl, e terá o seguinte conteúdo:
<html>
<head>
<title>Arquitetura CI</title>
</head>
<body>
<h1>Lista de Tarefas</h1>
<ul>
{foreach from=$tarefas item=tarefa}
<li>{$tarefa.id}, {$tarefa.titulo} - {if $tarefa.situacao eq 'A'}Aberta{else}Fechada{/if}</li>
{/foreach}
</ul>
</body>
</html>
Aqui é feito um laço no array de objetos de Tarefas retornado pela DAO e a cada iteração é impresso, em um elemento de uma lista desordenada, o ID da tarefa, seu título e a situação, que pode ser Aberta ou Fechada.
Bom, com isso encerro mais um artigo sobre a arquitetura de desenvolvimento de software com CodeIgniter, Smarty e Doctrine. No próximo artigo me focarei em impelementar o padrão Facade no framework CodeIgniter, seguido pela mudança de nomenclatura em algumas camadas e, finalmente, pelo desenvolvimento de um sistema de controle de tarefas. Aguardem, até a próxima.
James
Postado em 03/01/2010 às 17:25Parabéns pela matéria, porém quando vou executar o via linha de comando ocorre o seguinte erro:
Fatal error: Uncaught exception ‘LogicException’ with message ‘Passed array does not specify an existing static method’ in D:\www\gestor\system\application\config\database.php:62 Stack trace: #0 D:\www\gestor\system\application\config\database.php(62): spl_autoload_register() #1 D:\www\gestor\system\application\doctrine.php(2): require_once(‘D:\www\gestor\s…’) #2 {main} thrown in D:\www\gestor\system\application\config\database.php on line 62