MVC na prática – entenda de uma vez por todas como isso funciona

UPDATE: Tem vídeo lá no final da página. Clique aqui para ir pra lá.

Olá.

O post de hoje é uma sugestão do nosso leitor Tiago Cardoso.

Vamos aprender de uma vez por todas como funciona o MVC na prática, e ver como isso pode facilitar bastante a manutenção de sites/sistemas e também aumentar a produtividade no desenvolvimento de novas aplicações.

Antes, vamos dar apenas uma relembrada no que é MVC.

M – MODEL – É a camada que vai interagir com seu banco de dados. Aqui você vai fazer as requisições e inserções em suas tabelas. Também é o local onde você irá fazer a lógica de negócios funcionar, exemplo: verificar o usuário/senha em um banco de dados e entregar a resposta pronta ao controller.

V – VIEW – É a camada visual, ou seja, é o que o seu usuário vai ver. Tem bastante HTML e pouco PHP.

C – CONTROLLER – Responsável pelas requisições do site. É como se fosse um intermediário entre o Model e a View. Tecnicamente você até pode fazer consultas ao banco de dados daqui, mas se você quiser seguir o padrão MVC, aconselho não fazer isto, e deixar a parte de banco de dados lá para o Model.

O ideal é sempre deixar os CONTROLLER o mais simples possível, sem dar-lhes muita responsabilidade. Lembre-se de não fazer um controller com um monte de métodos, pois assim ele ficará gigante e difícil de entender.

O ideal é você separar tudo em vários outros controllers com seus métodos. Ao separar sua aplicação, a manutenção futura ficará muito mais fácil de prestar.

A proposta do post de hoje é desenvolvermos uma aplicação bem simples, porém, que faça com que as 3 camadas, MVC se interajam.

Para isto, vamos criar um CRUD básico. CRUD é basicamente o que todo relacionamento com um banco de dados deve fazer, significa: CREATE, READ, UPDATE e DELETE.

Para não ficar tão abstrato, vamos criar uma aplicação que tem uma utilidade nas nossas vidas. Vamos fazer uma pequena agenda, onde basicamente o usuário vai inserir a descrição do compromisso e a data do evento. Bem simples.

Criando nossa Agenda

O primeiro passo é criarmos nosso banco de dados e a tabela. Crie um banco de dados qualquer e depois crie uma tabela. Se quiser, copie o código abaixo que cria nossa tabela:

CREATE TABLE IF NOT EXISTS `compromissos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`descricao` text COLLATE utf8_unicode_ci NOT NULL,
`data` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

 

Em seguida, configure o CodeIgniter para acessar seu bancos de dados. Se você não lembra ou não sabe como fazer isto, veja este post.

Depois, vamos configurar o CodeIgniter para trazer algumas bibliotecas e helpers que vamos utilizar durante o projeto. Precisamos carregar a biblioteca database e o helper url.

Para isto, acesse o arquivo: application/config/autoload.php e configure como segue abaixo:

Linha 55 deixe assim:

$autoload['libraries'] = array('database');

Linha 67

$autoload['helper'] = array('url');

Para ver se tudo está funcionando, recarregue seu CodeIgniter no navegador e veja se não surge nenhum erro.

Vamos criar agora uma página inicial que terá os links de navegação da nossa agenda. Serão basicamente 3 links:

Home – Leva até a página incial.

Novo Compromisso – Traz uma página com um formulário para inserir os dados na agenda

Listar Compromissos – Traz uma página que irá listar todos os compromissos cadastrados. Nesta tela também deverá haver em cada registro 2 links. Um para Editar e outro para Excluir

Ao clicar em Editar, uma nova tela surgirá com um formulário preenchido com os dados daquele registro, e no final um botão Salvar.

Aqui eu vou utilizar a própria estrutura de páginas e views do CodeIgniter, para evitar de ficar criando muita coisa. Então, na view welcome_message.php substitua o bloco de código como segue abaixo:

<div id="container">
<h1>Bem-vindo até a sua Agenda!</h1>

<div id="body">

<p>
Menu de Navegação
</p>
<ul>
<li><? echo anchor(base_url(), 'Home', 'title="Página Inicial"'); ?></li>
<li><? echo anchor('agenda/formulario', 'Novo Compromisso', 'title="Cadastrar um novo Compromisso"'); ?></li>
<li><? echo anchor('agenda/listar_compromissos', 'Listar Compromissos', 'title="Visualizar os compromissos Cadastrados"'); ?></li>
</ul>

</div>

</div>

 

Repare que eu utilizei a função anchor() do CodeIgniter para criar meus links. Esta é uma função bem legal, pois ela cria os links internos já com o endereço base do site.

Após colocar código acima e recarregar sua página no navegador, você deverá ver algo parecido com isto:

Print da página inicial da agenda

Repare que estamos direcionando os links para Novo Compromisso e Listar Compromissos para um único controller, o agenda, onde vamos centralizar todas as funções que tem relação com a agenda. E dentro deste controller nós criaremos as funções respectivas.

Vamos agora criar o controller agenda que irá chamar uma view onde existe um formulário de inserção de dados. Repare que eu não vou chamar esta função de novo_compromisso e sim de formulario pois a intenção aqui não é ter um formulário para inserir um compromisso e outro para editar. O que vamos fazer é sempre chamar este mesmo formulário tanto para novos registros quanto para edições. Veremos isto mais para frente.

Crie um arquivo chamado agenda.php e salve dentro da pasta: application/controllers e coloque dentro dele o seguinte conteúdo:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Agenda extends CI_Controller {

public function formulario()
{
$this->load->helper('form');

$this->load->view('formulario');
}
}

 

Repare que aqui vamos usar mais um helper do CodeIgniter. Será o helper form que nos ajuda a criar formulários. Este helper você pode carregá-lo tanto aqui, antes de chamar a view, quanto no autoload.php, lá dentro da pasta application/config. Porém, eu achei mais conveniente chamarmos aqui, pois não vamos utilizar este helper a todo momento, mas somente quando formos inserir ou editar um registro. Na listagem de compromissos, por exemplo, não precisaremos do helper form, pois é somente uma tabela. Então, acho inútil deixar o helper carregado na memória ocupando recursos do servidor à toa.

Agora vamos criar a view que a função formulario() está chamando.

Para isto, crie um arquivo chamado formulario.php dentro da pasta: application/views e coloque o seguinte conteúdo:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Novo Compromisso</title>

<style type="text/css">

::selection{ background-color: #E13300; color: white; }
::moz-selection{ background-color: #E13300; color: white; }
::webkit-selection{ background-color: #E13300; color: white; }

body {
background-color: #fff;
margin: 40px;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}

a {
color: #003399;
background-color: transparent;
font-weight: normal;
}

h1 {
color: #444;
background-color: transparent;
border-bottom: 1px solid #D0D0D0;
font-size: 19px;
font-weight: normal;
margin: 0 0 14px 0;
padding: 14px 15px 10px 15px;
}

code {
font-family: Consolas, Monaco, Courier New, Courier, monospace;
font-size: 12px;
background-color: #f9f9f9;
border: 1px solid #D0D0D0;
color: #002166;
display: block;
margin: 14px 0 14px 0;
padding: 12px 10px 12px 10px;
}

#body{
margin: 0 15px 0 15px;
}

p.footer{
text-align: right;
font-size: 11px;
border-top: 1px solid #D0D0D0;
line-height: 32px;
padding: 0 10px 0 10px;
margin: 20px 0 0 0;
}

#container{
margin: 10px;
border: 1px solid #D0D0D0;
-webkit-box-shadow: 0 0 8px #D0D0D0;
}
</style>
</head>
<body>

<div id="container">
<h1>Novo Compromisso</h1>

<div id="body">

<? echo form_open('agenda/salvar'); ?>
<p>
<label style="font-weight: bold;">Descrição</label><br />
<? echo form_textarea('descricao'); ?>

</p>

<p>
<label style="font-weight: bold;">Data do Compromisso</label><br />
<? echo form_input('data'); ?><span style="font-size: 12px; font-style: italic">formato: AAAA/mm/dd</span>
</p>

<p>
<input type="submit" value="Salvar">
</p>

<? echo form_close(); ?>

</div>

</div>

</body>
</html>

 

Este código ficou gigante porque eu copiei todo o conteúdo do welcome_message.php e alterei somente o que eu precisava. O certo seria eu criar um arquivo de estilos e importar, mas não é o foco aqui do nosso post, por isso, ficou assim mesmo.

Veja que para a criação do nosso formulário, estamos usando as funções do helper form . A vantagem é que, pelo menos no caso do form_open ele já cria para gente a tag de abertura do form já com tudo preenchido. Com relação aos INPUTS, fica a seu critério utilizar o helper ou não. Mas para fins didáticos, estou utilizando aqui.

Se tudo deu certo, a página deverá ficar com esta cara:

Print da tela de novo compromisso

Repare no action do formulário que o usuário será direcionado para a função salvar que está dentro do controller agenda. É neste função que vamos criar os códigos para carregarmos nosso model e inserir os dados recebidos.

Mas antes de criarmos nossa função salvar(), vamos criar finalmente nosso MODEL, que será responsável por conversar com nosso banco de dados.

Crie o arquivo model_agenda.php dentro da pasta application/models e coloque o seguinte conteúdo:

<?
class Model_agenda extends CI_Model {

function __construct()
{
parent::__construct();
}
public function salvar_dados(){

$dados = $this->input->post();

$query = $this->db->insert("compromissos", $dados);

if ($query) 
return true;
else 
return false;

}
}

?>

 

O que o model vai fazer é receber tudo o que foi enviado via POST e inserir direto na tabela. Como os nomes dos campos no formulário são exatamente os mesmos da tabela, então basta inserirmos diretos os valores.

Agora, vamos criar o controller salvar() que irá receber a requisição e irá se comunicar com o model.

Abra o controller agenda.php e crie nova função. Veja o código abaixo:

public function salvar(){

$this->load->model('model_agenda');

$resultado = $this->model_agenda->salvar_dados();

if ($resultado)
  $this->load->view('sucesso');
else 
  $this->load->view('erro');

}

 

O que acontece aqui: Repare que para utilizar um model, você precisa carregá-lo antes. Assim como as libraries e helpers, você pode pedir para o CodeIgniter carregar automaticamente seu model caso você vá utilizar a todo momento. Para isto, basta informar no nome do seu model no arquivo autoload.php. A parte de models fica no final do arquivo.

Uma vez carregado o model, para utilizarmos as funções dentro dele, basta utilizarmos a seguinte sintaxe:

$this->model_agenda->salvar_dados();

Neste caso estamos utilizando a função salvar_dados() que está dentro do model. Se fosse outra função, como veremos mais adiante, basta mudar o nome.

Veja que lá no arquivo do model_agenda, ele retorna true ou false dependendo do que ocorrer, então eu verifico e chamo uma view de sucesso ou de erro dependendo do retorno. Esta parte eu vou deixar para você criar. Mas crie as views, senão vai dar erro. Ou então só mude o load da view para um echo.

Certo, a parte de inserção de dados já está OK. O legal de usar models, é que este model_agenda.php que criamos, poderia ser utilizado por qualquer controller ou função que você criasse, reaproveitando o código várias vezes.

O próximo passo agora é criarmos as telas de apresentação dos compromissos agendados. Então, dentro do nosso controller agenda vamos criar uma nova função que irá chamar a view que irá listar o que temos cadastrado no banco de dados.

Copie o código abaixo e cole dentro do seu controller agenda.php

public function listar_compromissos(){

$this->load->model('model_agenda');

$consulta = $this->model_agenda->retorna_todos_registros();

$variaveis['consulta'] = $consulta;

$this->load->view("listar_compromissos", $variaveis);

}

 

A lógica aqui é mesma da inserção. Basicamente criaremos uma nova função dentro do nosso model model_agenda que irá trazer todos os registros do banco de dados. E chamamos esta função aqui e atribuímos seu retorno à uma variável, que será passada para a view e para montarmos nosso loop que irá mostrar os dados ao usuário.

Dentro do model_agenda.php, crie um nova função chamada retorna_todos_registros() e coloque o seguinte código:

public function retorna_todos_registros(){

$this->db->order_by("data", "desc");

$consulta = $this->db->get("compromissos");

return $consulta;
}
}

 

É uma consulta bem simples. O que eu faço é simplesmente dar um order by na tabela antes do get e em seguida, atribuo o resultado da consulta a  uma variável $consulta, e depois saio da função retornando o conteúdo de $consulta.

Novamente, esta função dentro deste model, pode ser utilizada várias vezes e em vários lugares dentro do CodeIgniter, sem precisar ficar recriando códigos a torto e a direta.

Vamos criar agora a view listar_compromissos.php que será responsável por mostrar os dados ao usuário.

Crie o arquivo listar_compromissos.php e salve dentro da pasta application/views

Dentro do arquivo, cole o seguinte código. Aqui estou colando só a parte que interessa. A parte de CSS você pode colocar depois, senão, fica muito grande o código.

<script>
function confirma(){
if (!confirm("Confirma a exclusão?"))
  return false;
return true;
}
</script>

<div id="container">
<h1>Compromissos Cadastrados</h1>

<div id="body">

<table style="width: 50%;" cellpadding="5" cellspacing="1">

<tr>
<th>ID</th>
<th>Descrição</th>
<th>Data do Compromisso</th>
<th>Ação</th>
</tr>

<? foreach($consulta -> result() as $linha): ?>
<tr>
<td style="text-align: center"><? echo $linha->id ?></td>
<td style="text-align: center"><? echo $linha->descricao ?></td>
<td style="text-align: center"><? echo $linha->data ?></td>
<td style="text-align: center"><? echo anchor("agenda/editar/$linha->id", "Editar") ?> | <? echo anchor("agenda/excluir/$linha->id", "Excluir", array("onclick" => "return confirma()")) ?></td>
</tr>
<? endforeach; ?>
</table>
</div>

</div>

 

O que eu fiz aqui foi simplesmente dar um loop pelo foreach na variável $consulta, que foi passada via parâmetro lá no controller. E montei uma tabelinha simples para mostrar os dados ao usuário.

Outra coisa que fiz também foi criar a coluna Ação, onde tem os links Editar e Excluir. Repare que utilizei o helper de url anchor para criar os links e dê uma atenção especial ao link Excluir.

Coloquei dentro dele  um parâmetro para chamar uma função javascript no evento onclick. Esta função simplesmente manda uma mensagem ao usuário perguntando se ele realmente quer excluir tal registro. Acho isto importante para links ou botões de exclusão.

Para que esta função funcione, dentro do onclick deve existir um return antes de chamar a função. Assim:

onclick="return confirma()"

Sem isto, não funciona e ele exclui direto. Se o usuário clicar em Cancelar, a página não é redirecionada, porém, caso confirme, então eu deixo o redirecionamento continuar.

É importante notar também que nos links Editar e Excluir eu chamo as funções dentro do controller agenda que ainda vamos criar daqui a pouco, mas note que é preciso passar o ID do registro em questão, para que possamos buscá-lo ou excluí-lo do banco de dados. Veja no código acima como é feito isto no CodeIgniter.

Se tudo deu certo, sua tela deve ficar parecida com isto:

Print da tela de Compromissos Cadastrados

O próximo passo agora é criamos a parte de Edição dos compromissos. Então, vamos criar uma função dentro do controller agenda que irá fazer isto.

Abra o controller agenda.php e coloque a função abaixo:

public function editar($id){

$this->load->helper('form');

$this->load->model('model_agenda');

$consulta = $this->model_agenda->retorna_todos_registros($id);

$variaveis['descricao'] = $consulta->row()->descricao;
$variaveis['data'] = $consulta->row()->data;

$this->load->view("formulario", $variaveis);

}

Veja que o parâmetro ID pode ser recuperado ao colocarmos a variável $id dentro dos parênteses da função. Isto já é suficiente para recebermos seu valor.

Em seguida, carregamos novamente o helper form, pois ele será utilizado para montar o formulário.

Agora temos  um pequeno problema. Precisamos recuperar somente um registro do banco de dados, e nosso model_agenda.php quando solicitado traz todos os registros. Então aqui ha duas soluções. Você poderia criar uma nova função dentro do model chamada por exemplo: retorna_registro_by_id(), onde você deverá passar o ID do registro antes de continuar, ou então, fazer uma pequena mudança no model_agenda.php para que ele permita trabalhar das duas maneiras, trazendo um registro ou todos.

Para isto, vamos modificar nosso model para que ele faça a seguinte verificação: se ao chamarmos a função retorna_todos_registros() passarmos um ID, então ele acrescenta na consulta SQL uma cláusula WHERE para restringir a pesquisa, caso contrário, traz todos os registros. Veja abaixo como ficará a nova função dentro do model:

public function retorna_todos_registros($id = 0){

$this->db->order_by("data", "desc");

if ($id != 0)
  $this->db->where("id", $id);

$consulta = $this->db->get("compromissos");

return $consulta;
}

Repare que eu coloquei um parêmtro $id que é iniciado no valor zero. Então, antes de fazer a consulta, eu verifico se este valor está igual a zero. Caso esteja, continuo na consulta. Caso não, então eu pego este valor e passo como parâmetro no WHERE da consulta.

Com isto, nós temos uma função que tem duas utilidades. Traz todos os registros se não for passado um ID ou então, retorna somente um registro, caso o ID seja informado.

Voltando a função editar() dentro do nosso controller, os dados retornados pela consulta ao model são armazenados na variável $consulta, e utilizando esta sintaxe:

$consulta->row()->descricao;

eu consigo recuperar os campos de somente um registro, sem precisar usar um foreach, que não faz sentido, pois temos somente uma linha sendo retornada aqui.

Fazendo isto para os dois campos, eu atribuo seus valores à duas variáveis que serão repassadas à view formulario, que é a mesma que criamos no início do post.

Mas aqui temos que fazer uma pequena mudança na view formulário.php. Temos que acrescentar os códigos que irão mostrar estes dados ao usuário. Então, dentro dos campos veja as mudanças que fiz:

<? echo form_textarea('descricao', isset($descricao) ? $descricao : "" ); ?>

<? echo form_input('data', isset($data) ? $data : ""); ?>

Coloquei aqui somente os códigos dos campos para facilitar.

Ali eu uso o operador ternário do PHP que simplesmente faz a verificação: se a variável $descricao existir, então mostro seu conteúdo, caso contrário, mostro vazio.

Se não souber como funciona o operador ternário do PHP, clique aqui.

Isso server para que não seja gerado um erro de variável inexistente na página, pois lembre-se que estes campos só existirão quando for clicado em Editar. Como é exatamente a mesma página que é chamada ao inserir um novo compromisso, lá não são passadas variáveis, por isso daria erro.

O próximo passo agora é modificarmos a função salvar_dados() dentro do model_agenda.php para que ele também salve dados editados, e não somente os novos. Ao fazer isto, precisaremos apenar acrescentar um novo campo na view formulário. Este campo será um campo do tipo hidden que irá receber o ID daquele registro. Então, ao postarmos para a nossa função salvar, basta fazermos a seguinte verificação: se o campo $id for vazio, então dê um insert, se não, dê um update.

Abra sua view formulario.php e entre após o botão submit mas antes da tag de fechamento do formulário, acrescente o seguinte campo:

<input type="hidden" name="id" value="<? echo $this->uri->segment(3,0); ?>">

O código $this->uri->segment(3,0) recupera o que existe no 3º segmento na URL. Neste caso ele conta a partir do controller, depois o método e depois o parâmetro.

O zero indica que se ele não encontrar nada, então retorna zero.

Agora na função salvar_dados() dentro do model_agenda.php nós saberemos que se o campo $id vir com um valor igual a zero, indica que é uma inserção de um novo registro, porém, se for diferente de zero, indica uma edição, então devemos fazer um update.

Abra o model_agenda.php e modifique a função salvar_dados() pela que segue abaixo:

public function salvar_dados(){

$id = $this->input->post("id");

$dados = array(

"descricao" => $this->input->post("descricao"),
"data" => $this->input->post("data")

);

//se o id for diferente de zero, então eu dou um update na tabela.
if ($id != 0) {
$this->db->where("id", $id);
$query = $this->db->update("compromissos", $dados);
} else {
$query = $this->db->insert("compromissos", $dados);
}

if ($query) 
return true;
else 
return false;

}

Além da mudança do ID tive que fazer mais uma mudança. Como no código original eu estava inserindo na tabela todos os campos que eram postados, tudo dava certo, pois somente dois campos eram postados, descrição e data, e há dois campos no banco de dados.

Neste caso, como acrescentamos um capo hidden no formulário, 3 campos serão postados. E ao dar um insert, será gerado um erro, pois o CodeIgniter não vai achar o campo novo no banco de dados.

Por isso, eu criei a array dados e ali eu tratei as informações que estão vindo via post. E o mais interessante é que esta variável dados serve tanto para o UPDATE quanto para o INSERT, bastando apenas informar o ID na cláusula UPDATE antes de chamá-la.

O último passo agora no nosso projeto é fazer a parte de exclusão.

Vamos então criar uma função excluir() dentro do controller agenda, que irá chamar uma função dentro do model_agenda.php que irá excluir o registro.

Abra o model_agenda.php e acrescente a seguinte função:

public function excluir($id){

$this->db->where("id", $id);

if ($this->db->delete("compromissos"))
  return true;
else
  return false;
}

Agora, temos que criar também a função excluir dentro do nosso controller agenda. Veja abaixo:

public function excluir($id){

$this->load->model('model_agenda');

$resultado = $this->model_agenda->excluir($id);

if ($resultado)
$this->load->view('sucesso');
else 
$this->load->view('erro');

}

Aqui eu aproveitei aquelas view de sucesso e erro na operação. O ideal é você criar mensagens personalizadas, mas isso eu acho que você já sabe fazer certo?

—-

Bom, este é o final do post de hoje. Ele ficou bem extenso mas acredito que agora você consegue saber o que é MVC e como aplicá-lo.

Abaixo eu deixo o link para que você baixe nosso projeto e teste no seu servidor.

Baixar o Projeto Agenda

Veja abaixo o vídeo onde eu faço o projeto acima:

 

P.S. Eu quero saber quais suas dúvidas sobre o mundo CodeIgniter. Poste nos comentários abaixo dúvidas e sugestões. Quem sabe sua dúvida pode se tornar um próximo post.

Abraços

Fabio

Fábio S. Reszko

Sou Programador PHP desde 2006 e eu acredito sinceramente que programar usando um Framework PHP é a solução para os problemas de códigos desorganizados, difíceis de entender e de dar manutenção no futuro. Se você também acredita nisto, então fique à vontade em explorar meu blog.