Arquitetura de Software com CodeIgniter – Aplicação de Exemplo
Olá a todos, como vão? Finalmente depois de algum tempo consegui implementar uma aplicação de exemplo utilizando a arquitetura de software proposta com o framework CodeIgniter. É essencial para você leitor, ter acompanhado os artigos anteriores onde a arquitetura é definida e explanada. Este artigo irá guiá-los no desenvolvimento desta aplicação. Aproveitem!
A Aplicação
A aplicação desenvolvida é uma lista de afazeres (todo list), altamente simples, porém tendo ela como base é possível desenvolver futuras aplicações, principalmente por ela possuir muitas das funcionalidades que os sistemas reais possuem. O banco de dados definido para a aplicação é mostrado abaixo.
CREATE DATABASE todo_ci; USE todo_ci; CREATE TABLE tasks( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, completed BOOLEAN DEFAULT FALSE )ENGINE=InnoDB;
O banco consiste apenas de uma tabela para armazenar as tarefas, contendo o título da tarefa e se ela foi completada (TRUE) ou não (FALSE). Em um sistema de controle de tarefas um pouco mais completo também existiria uma tabela de usuários, mas foge um pouco do escopo desta aplicação. Para configurar o acesso ao banco na arquitetura, é necessário modificar o arquivo database.php localizado em application/config, alterando os atributos: hostname, username, password, database. Como no seguinte trecho:
$db['default']['hostname'] = "localhost"; $db['default']['username'] = "root"; $db['default']['password'] = ""; $db['default']['database'] = "todo_ci";
Outra coisa a se fazer é configurar no arquivo autoload.php dentro de application/config o carregamento automático dos helpers e bibliotecas utilizadas. Para fazer isso basta alterar as linhas:
$autoload['libraries'] = array(); $autoload['helper'] = array();
para
$autoload['libraries'] = array('form_validation', 'session');
$autoload['helper'] = array('url');
Negócio
Como a arquitetura está bem definida com camadas lógicas para negócio e apresentação, pode-se facilmente separar as duas dividindo o desenvolvimento em duas etapas. Na parte de negócio é necessário fazer toda codificação referente a Facade, DAO e Model.
Camadas referentes ao banco de dados
As camadas Model e DAO, são as referentes ao banco de dados. O Model vai ser a representação de uma entidade do banco de dados como um objeto da aplicação e o Data Access Object (DAO) será o objeto que possuirá operações com o banco, como, por exemplo, operações de salvar e obter registros. Para gerar o Model diretamente pelo Doctrine, tendo o arquivo de configuração apresentado acima devidamente configurado, é necessário gerar o arquivo schema.yml, dentro da pasta application/schema, que define os atributos das entidades do banco, ele possui o seguinte conteúdo:
Tasks:
columns:
id:
primary: true
autoincrement: true
type: integer(10)
title:
type: string(255)
completed:
type: boolean
Então, para gerar a classe basta ir a um terminal, e estando na pasta system/application executar o seguinte comando:
$ php doctrine generate-models-yaml
Ele então gerará os arquivos Tasks.php e BaseTasks.php, contidos, respectivamente, em: application/models e application/models/generated. Estes arquivos possuem os seguintes conteúdos:
abstract class BaseTasks extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('tasks');
$this->hasColumn('id', 'integer', 10, array('primary' => true, 'autoincrement' => true, 'type' => 'integer', 'length' => '10'));
$this->hasColumn('title', 'string', 255, array('type' => 'string', 'length' => '255'));
$this->hasColumn('completed', 'boolean', null, array('type' => 'boolean'));
}
}
class Tasks extends BaseTasks
{
}
Com isso o Model está gerado, porém ainda falta o arquivo DAO, basta criar então um arquivo chamado TasksDAO.php na pasta application/models e colocar o seguinte conteúdo nele:
<?php
class TasksDAO
{
}
Bom, após ter tudo gerado é necessário criar os métodos referentes as operações básicas com o banco de dados, que são: Create, Retrieve, Update e Delete, ou o tão famoso CRUD. Todas estas operações estarão localizadas na classe TasksDAO. A operação para se criar uma nova tarefa ou atualizá-la é apresentada no código abaixo:
public function save($data)
{
if ( !isset($data['id']) || !$tasks = $this->fetch($data['id']) ) {
$tasks = new Tasks();
}
$tasks->merge($data);
$tasks->save();
}
Apesar do Model a nível de código possuir somente os atributos que o banco de dados possui, graças a ele extender da classe do Doctrine é possível se aproveitar de todos os métodos do framework, o que transforma o Model em um Active Record, possibilitando utilizar diretamente dele o método save. Se um id for passado, é feita uma verificação se este id existe e então, caso exista, a operação será uma atualização de um registro já populado. A parte referente ao procedimento de exclusão de um registro é o apresentado abaixo:
public function delete($id)
{
$tasks = $this->fetch($id);
if ( $tasks ) {
return $tasks->delete();
}
return false;
}
O método irá procurar por uma tarefa com o id passado e, caso encontre-a, irá excluí-la. O procedimento para se obter todas as tarefas é:
public function fetchAll()
{
$query = new Doctrine_Query();
$query->from('Tasks t');
$query->orderby('t.id DESC');
return $query->execute();
}
A partir de uma Doctrine Query ele irá procurar todas as tarefas existentes no banco de dados e retorná-las para as camadas superiores. E, por fim, o procedimento para se obter uma determinada tarefa é:
public function fetch($id)
{
$task = Doctrine::getTable('Tasks')->find($id);
if ( $task ) {
return $task;
}
return false;
}
Ele irá obter uma tarefa baseado no id passado para o método e irá retorná-la. Com isso a camada de banco de dados está pronta.
Regra de Negócio
A regra de negócio da aplicação ficará na camada Facade, como explicado anteriormente. Ela conterá métodos que o Controller irá chamar. No caso desta aplicação de exemplo estes procedimentos são simplesmente os referentes a um CRUD, mas em uma aplicação real, regras como, por exemplo, de cálculo de juros, ou de fechamento de um pedido, ficariam nesta camada. Na Facade também será feita toda a validação dos dados vindos do formulário. A Facade criada é um arquivo chamado tasks_facade.php e está localizada na pasta application/facades, ele possui o seguinte conteúdo:
<?php
class TasksFacade extends Facade
{
public function index()
{
$dao = new TasksDAO();
$tasks = $dao->fetchAll();
return $tasks;
}
public function save()
{
$this->form_validation->set_rules('title', 'Title', 'required|max_length[255]|htmlspecialchars');
if ( $this->form_validation->run() == FALSE ) {
return false;
} else {
$array_data = array();
$array_data['title'] = $this->form_validation->set_value('title');
$dao = new TasksDAO();
$dao->save($array_data);
return true;
}
}
public function update()
{
$this->form_validation->set_rules('id', 'Código', 'required');
$this->form_validation->set_rules('title', 'Título', 'required|max_length[255]|htmlspecialchars');
if ( $this->form_validation->run() == FALSE ) {
return false;
} else {
$array_data = array();
$array_data['id'] = $this->form_validation->set_value('id');
$array_data['title'] = $this->form_validation->set_value('title');
$array_data['completed'] = ( isset($_POST['completed']) ) ? true : false;
$dao = new TasksDAO();
$dao->save($array_data);
return true;
}
}
public function get($id)
{
$dao = new TasksDAO();
return $dao->fetch($id);
}
public function delete($id)
{
$dao = new TasksDAO();
$dao->delete($id);
return true;
}
}
O primeiro método será acessado para fornecer todas as tarefas existentes no banco de dados, ele simplesmente chama a DAO para obter estes dados e retorna para a camada de mais alto nível. Os métodos save e update, serão referentes à inserção de um novo registro e à alteração de um registro já existente; onde eles irão validar os dados enviados pelo formulário e, caso tudo esteja corretamente preenchido, irão chamar a DAO para persistir estes dados em banco. O método get irá chamar a DAO para obter a tarefa em questão, passando-a para a camada de cima. O último método, o delete simplesmente irá excluir a tarefa.
Esta é toda a parte referente a negócio da aplicação, agora a última coisa a se fazer é implementar a camada de mais alto nível.
Camada de mais alto nível
Esta camada é composta pelo Controller e pela View, onde o primeiro é relacionado ao controle de fluxo da aplicação e o segundo à apresentação com o usuário. Primeiramente é necessário criar o controller para as tarefas, o nome do arquivo é tasks_controller.php localizado em application/controllers, ele deverá extender da classe Controller padrão do CodeIgniter, como demonstrado asseguir:
<?php
class TasksController extends Controller
{
public function TasksController()
{
parent::Controller();
$this->load->facade('tasks_facade');
}
}
Este código declara a classe do Controller em questão e seu construtor já irá carregar a Facade utilizada para processar as regras de negócio. O primeiro método a ser implementado é o referente a listagem das tarefas, ele é demonstrado abaixo:
public function index()
{
if ( $this->session->flashdata('success') ) {
$this->smartyview->assign('message', $this->session->flashdata('success'));
}
$tasks = $this->tasksfacade->index();
$this->smartyview->assign_by_ref('tasks', $tasks);
$this->smartyview->display('tasks/index.tpl');
}
Primeiramente ele verifica se existe alguma mensagem para exibir ao usuário, caso existe ele passa esta mensagem para a View. Posteriormente, ele irá chamar a camada Facade para obter os dados das tarefas e, então passará estas tarefas para a View. Agora que o Controller já está criado, junto com seu método index, é interessante configurar o CodeIgniter para que este Controller seja o principal da aplicação, para isso basta localizar a seguinte linha dentro de application/config/routes.php:
$route['default_controller'] = "welcome";
e mudá-la para:
$route['default_controller'] = "tasks";
Ao acessar a raíz do projeto ele então já chamará a ação index do Controller tasks. Agora é necessário desenvolver o layout da aplicação, dentro da pasta views é necessário criar a pasta geral, e criar os arquivos header.tpl e footer.tpl eles serão o cabeçalho e o rodapé da aplicação. O arquivo header.tpl possui o seguinte código:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Afazeres - Arquitetura CodeIgniter</title>
<link rel="stylesheet" type="text/css" href="{php}echo base_url(){/php}css/style.css" />
</head>
<body>
<div id="wrapper">
<div id="header">
<h1>Lista de Afazeres</h1>
</div>
<div id="content">
E o footer.tpl:
</div> <div id="footer"> <p>Fernando Mantoan.</p> </div> </div> </body> </html>
Com o layout já definido, é hora de criar as Views referentes ao Controller tasks, estas Views deverão estar dentro da pasta application/views/tasks, o primeiro arquivo criado é referente a listagem das tarefas, ele é o arquivo index.tpl e possui o seguinte conteúdo:
{include file="geral/header.tpl"}
{if $message}
<p class="success">{$message}</p>
{/if}
<p>
{php}
echo anchor('tasks/add', 'Nova Tarefa');
{/php}
</p>
<table>
<thead>
<tr>
<th scope="col">Título</th>
<th scope="col">Tarefa Completada?</th>
<th scope="col" colspan="2"></th>
</tr>
</thead>
<tbody>
{foreach from=$tasks item=task}
<tr>
<td class="title">{$task.title}</td>
<td>{if $task.completed}Completada{else}Não Completada{/if}</td>
<td class="actions center">
{php}$task = $this->get_template_vars('task');{/php}
{php}echo anchor('tasks/edit/' . $task->id, 'Editar');{/php}
{php}echo anchor('tasks/delete/' . $task->id, 'Excluir', 'onclick="return window.confirm(\'Deseja excluir esta tarefa?\');"');{/php}
</td>
</tr>
{/foreach}
</tbody>
</table>
{include file="geral/footer.tpl"}
Para utilizar algumas das funções nativas do CodeIgniter, foi necessário utilizar a tag {php} do Smarty, que deixa para o PHP executar o código definido entre os delimitadores. Quem for levar mais para frente a arquitetura poderá integrar estas funções com o Smarty para utilizá-las como Helpers e não ter a necessidade de utilizar as funções como código PHP. Agora que a listagem está funcionando é hora de fazer o cadastro de novas tarefas, para isso é adicionado o seguinte método à classe TasksController:
public function add()
{
if ( !$this->tasksfacade->save() ) {
$this->smartyview->display('tasks/add.tpl');
} else {
$this->session->set_flashdata('success', 'Tarefa cadastrada!');
redirect('tasks/index');
}
}
Ele simplesmente verificará se foi possível salvar um novo registro, caso não exibe o formulário de cadastro, e caso a tarefa tenha sido cadastrada redireciona para a listagem de tarefas passando também uma mensagem de sucess. A View referente a este método é o arquivo add.tpl apresentado abaixo:
{include file="geral/header.tpl"}
<h2>Nova Tarefa</h2>
{php}echo validation_errors('<p class="error">', '</p>'){/php}
{php}echo form_open('tasks/add'){/php}
<div>
<label for="title">Título:</label>
<br />
<input type="text" name="title" value="{php}set_value('title'){/php}" />
</div>
<div><input type="submit" value="Salvar" /></div>
{php}echo form_close(){/php}
{include file="geral/footer.tpl"}
Novamente é necessária a utilização da tag {php} do Smarty, para aproveitar ao máximo os Helpers do CodeIgniter. Esta tela exibirá erros, caso ocorram, e também, o formulário para cadastro de uma nova tarefa. O próximo método a ser implementado é o de atualização de tarefas, ele é apresentado a seguir:
public function edit($id = null)
{
if ( is_null($id) || !$task = $this->tasksfacade->get($id) ) {
redirect('tasks/index');
}
if ( !$this->tasksfacade->update() ) {
$this->smartyview->assign_by_ref('task', $task);
$this->smartyview->display('tasks/edit.tpl');
} else {
$this->session->set_flashdata('success', 'Tarefa atualizada!');
redirect('tasks/index');
}
}
Ele basicamente verificará se um id foi ou não passado, e se foi informado um id existente no banco de dados, caso estes requisistos sejam atendidos ele irá mostrar pro usuário o formulário de alteração ou uma mensagem de sucesso na alteração. O formulário de alteração (arquivo edit.tpl) é exibido abaixo:
{include file="geral/header.tpl"}
<h2>Editar Tarefa</h2>
{php}echo validation_errors('<p class="error">', '</p>'){/php}
{php}echo form_open('tasks/edit/' . basename($_SERVER['PHP_SELF'])){/php}
<div>
<input type="hidden" name="id" value="{$task.id}" />
<label for="title">Título:</label>
<br />
<input type="text" name="title" value="{$task.title}" />
<br />
<label>Completada? <input type="checkbox" {if $task.completed}checked="checked"{/if} name="completed" value="1" />
</div>
<div><input type="submit" value="Salvar" /></div>
{php}echo form_close(){/php}
{include file="geral/footer.tpl"}
Novamente para utilizar ao máximo os Helpers do CodeIgniter foram necessários alguns workarounds, este formulário segue a mesma idéia do formulário de cadastro, a diferença é que a URL dele precisa do id da tarefa, assim como um input hidden com este id no formulário, e ele apresenta um checkbox para o usuário informar se a tarefa já foi completada ou não. O último método do Controller necessário para esta aplicação é o referente a exclusão de registros. Este método é apresentado abaixo:
public function delete($id = null)
{
if ( is_null($id) ) {
redirect('tasks/index');
} else {
$this->tasksfacade->delete($id);
$this->session->set_flashdata('success', 'Tarefa excluída!');
redirect('tasks/index');
}
}
Neste caso não é necessário apresentar nenhuma View. A última coisa a se fazer para esta aplicação é definir alguns estilos CSS, para isto foi criada uma folha de estilos simples para tornar a aplicação um pouco mais bonita, esta folha de estilos deve ser armazenada na raíz do projeto em uma pasta com o nome de css, como, por exemplo em ci_todo/css, e o arquivo desta folha de estilos é style.css:
BODY {
font-family: Arial, Verdana, Helvetica, sans-serif;
}
A {
color: #000;
font-weight: bold;
}
DIV#wrapper {
left: 50%;
margin-left: -300px;
position: relative;
width: 600px;
}
DIV#header {
margin: 20px 0 40px 0;
}
DIV#header H1 {
color: #f60;
font-size: 32px;
letter-spacing: -2px;
margin: 0;
}
DIV#content {
font-size: 12px;
}
DIV#content TABLE {
border-collapse: collapse;
width: 100%;
}
DIV#content TABLE THEAD TR TH, DIV#content TABLE TBODY TR TD {
text-align: left;
padding: 9px;
}
DIV#content TABLE THEAD TR TH {
background: #eee;
}
DIV#content TABLE TR TD.title {
width: 40%;
}
DIV#content TABLE TR TD.actions {
width: 18%;
}
DIV#content FORM LABEL {
cursor: pointer;
}
DIV#content FORM INPUT[type="text"] {
background: #f6f6f6;
border: 1px #eee solid;
font-size: 12px;
margin-top: 10px;
padding: 3px;
width: 99%;
}
DIV#content FORM INPUT[type="submit"] {
background: #f60;
border: none;
color: #fff;
margin-top: 20px;
padding: 4px;
}
UL, LI {
margin: 0;
}
P.error, .errorExplanation LI {
background-color: #603131;
border: 1px solid #5c2d2d;
color: #fff;
font-weight: bold;
padding: 10px;
margin: 0 0 15px 0;
}
P.success {
background-color: #317b17;
border: 1px solid #317b17c;
color: #fff;
font-weight: bold;
padding: 10px;
margin-bottom: 15px;
}
DIV#footer {
font-size: 9px;
font-weight: bold;
margin-top: 80px;
}
.center {
text-align: center !important;
}
Algumas screenshots deste sistema são apresentadas abaixo:
Fim
Bom, assim concluo toda esta série de artigos referentes a uma Arquitetura de Desenvolvimento de Aplicações com o CodeIgniter, eu sinceramente achava esta solução muito boa na época que a criei, porém após terminar a arquitetura proposta no meu trabalho de conclusão de curso eu fiquei muito mais animado com o Zend Framework e este artigo é o último referente a esta arquitetura com CI, o que pode acontecer é de haver atualizações dos artigos sobre esta arquitetura para melhorar o entendimento e revisar os conceitos apresentados caso necessário. Como já havia dito antes, para quem procura uma solução simples e rápida esta arquitetura com o CI pode ser aplicável, mas existem diversas melhorias a serem feitas, seu uso pode então depender muito da disponibilidade de melhorá-la e do gosto por este framework. Bom, por hoje é só, até mais!


