Você está na página 1de 40

Exemplo de MVC com PHP

MVC, Padres de Projeto por Tarcsio

Nesse artigo irei demonstrar na prtica, a aplicao do padro de projeto MVC com PHP. Para isso, construirei um aplicativo de Agenda de Contatos Telefnicos conforme requisitos funcionais, diagramas e cdigo que sero mostrados ao longo desse texto. Mas, antes de comear, aconselho a vocs que leiam o artigo de introduo a MVC, o mesmo poder ser visualizado acessando o link Entendendo o MVC (ModelView-Controller).

Hierarquia de Diretrios
Conforme a Imagem 1, irei seguir um padro de hierarquia de diretrios e arquivos, vou explicar sobre as responsabilidades dos diretrios e arquivos logo abaixo.

Imagem 1 Hierarquia de Diretrios e Pastas

Diretrio controllers

Conforme se pode deduzir pelo nome, irei us-lo para guardar as classes da camada de controle do sistema, as famosas classes controladoras ou controllers, responsveis por fazer o intermdio entre a camada de dados (models ou modelos) e visualizao (views), conforme o artigo de introduo a MVC citado acima.

Diretrio databases
Por se tratar de um projeto apenas de exemplo, irei persistir os dados dos contatos e telefones usando um banco de dados SQLite, nesse diretrio que o arquivo referente ao banco ficar. Caso queria conectar a aplicao com o MySQL, veja como no fim do artigo

Diretrio lib
Nesse diretrio, irei guardar as classes diretamente ligadas ao sistema, como por exemplo, classes de filtros de dados, validaes genricas, helpers (caso haja algum), interfaces e abstraes no ligadas camada de negcio do sistema. Se estivesse construindo um framework em PHP, guardaria as classes do mesmo nesse diretrio.

Diretrio models
Aqui, guardarei as classes de dados diretamente abstradas e ligadas s regras de negcio do sistema, como por exemplo, as classes Contato e Telefone, entre outras.

Diretrio views
Esse o diretrio onde guardarei os arquivos HTML do sistema, tais arquivos representam a camada de visualizao (view), da qual foi falada no artigo de introduo ao MVC.

Arquivo index.php
um arquivo ndex como qualquer outro, nele que a execuo do sistema ir comear.

Implementao das Classes das Camadas


Classes Primrias ou Genricas
Depois de todos os diretrios explicados, mostrarei a implementao de algumas classes necessrias para a separao do sistemas em camadas (Modelo Model, Viso View , Controlador Controller). A primeira delas que mostrarei, a de controlador genrico, no diretamente ligado s regras de negcio do sistema de Agenda Telefnica. Veja o exemplo abaixo. 1 2 3 4
<?php /** * Controlador que dever ser chamado quando no for * especificado nenhum outro *

5 * @package Exemplo simples com MVC 6 * @author DigitalDev * @version 0.1.1 7 * 8 * Camada - Controladores ou Controllers 9 * Diretrio Pai - controllers 10* Arquivo - IndexController.php 11*/ class IndexController 12{ 13 /** * Ao que dever ser executada quando 14 * nenhuma outra for especificada, do mesmo jeito que o 15 * arquivo index.html ou index.php executado quando nenhum 16 * referenciado 17 */ public function indexAction() 18 { 19 //redirecionando para a pagina de lista de contatos 20 header('Location: ?controle=Contato&acao=listarContato'); 21 } 22} 23?> 24 25 26 27 28 Conforme vocs devem ter visto acima, usei o sufixo Controller no nome da classe e Action no nome do mtodo. Esse o padro de nomenclatura que irei utilizar em todas as outras classes da camada Controle e seus mtodos. Alm disso, nomearei os arquivos que iro conter as classes com o mesmo nome da prpria classe. A segunda classe que implementarei ser a Application. A responsabilidade da mesma ser a de verificar qual classe da camada de controle (Controller) e qual mtodo da classe (Action) o usurio deseja executar. D uma olhada no cdigo abaixo, espero que ele seja simples o suficiente para esclarecer seu modo de funcionamento. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php /** * Camada - Controller * Diretrio Pai - lib * Arquivo - Application.php **/ /** * Essa funo garante que todas as classes * da pasta lib sero carregadas automaticamente */ function __autoload($st_class) { if(file_exists('lib/'.$st_class.'.php')) require_once 'lib/'.$st_class.'.php'; } /**

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

* Verifica qual classe controlador (Controller) o usurio deseja chamar * e qual mtodo dessa classe (Action) deseja executar * Caso o controlador (controller) no seja especificado, o IndexControllers ser o padro * Caso o mtodo (Action) no seja especificado, o indexAction ser o padro * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 **/ class Application { /** * Usada pra guardar o nome da classe * de controle (Controller) a ser executada * @var string */ protected $st_controller; /** * Usada para guardar o nome do metodo da * classe de controle (Controller) que dever ser executado * @var string */ protected $st_action; /** * Verifica se os parmetros de controlador (Controller) e ao (Action) foram * passados via parmetros "Post" ou "Get" e os carrega tais dados * nos respectivos atributos da classe */ private function loadRoute() { /* * Se o controller nao for passado por GET, * assume-se como padro o controller 'IndexController'; */ $this->st_controller = isset($_REQUEST['controle']) ? $_REQUEST['controle'] : 'Index'; /* * Se a action nao for passada por GET, * assume-se como padro a action 'IndexAction'; */ $this->st_action = isset($_REQUEST['acao']) $_REQUEST['acao'] : 'index'; }

/** * * Instancia classe referente ao Controlador (Controller) e executa * mtodo referente e acao (Action) * @throws Exception */ public function dispatch()

{ 66 $this->loadRoute(); 67 68 //verificando se o arquivo de controle existe 69 $st_controller_file = 'controllers/'.$this70 >st_controller.'Controller.php'; if(file_exists($st_controller_file)) 71 require_once $st_controller_file; 72 else 73 throw new Exception('Arquivo '.$st_controller_file.' nao 74 encontrado'); 75 76 //verificando se a classe existe $st_class = $this->st_controller.'Controller'; 77 if(class_exists($st_class)) 78 $o_class = new $st_class; 79 else 80 throw new Exception("Classe '$st_class' nao existe no 81 arquivo '$st_controller_file'"); 82 //verificando se o metodo existe 83 $st_method = $this->st_action.'Action'; 84 if(method_exists($o_class,$st_method)) 85 $o_class->$st_method(); 86 else throw new Exception("Metodo '$st_method' nao existe na 87 classe $st_class'"); 88 } 89 90 /** 91 * Redireciona a chamada http para outra pgina 92 * @param string $st_uri */ 93 static function redirect( $st_uri ) 94 { 95 header("Location: $st_uri"); 96 } 97 } 98 ?> 99 100 101 102 103 104 105

Apesar da classe acima no ter semelhana alguma com a classe IndexController que escrevi, ela tambm faz parte da camada de controle (Controller), mas est num nvel mais acima. O fato dela ser responsvel por verificar qual controlador (Controller) e qual mtodo (Action) ir executar, faz da mesma o corao do sistema. A terceira classe que irei implementar ser responsvel por cuidar da camada de visualizao. Apesar de cdigo um pouco extenso, ela uma classe sem muitas funcionalidade, mas poder ser enriquecida de acordo com nossas necessidades futuras.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

<?php /** * Essa classe responsvel por renderizar os arquivos HTML * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretrio Pai - lib * Arquivo - View.php */ class View { /** * Armazena o contedo HTML * @var string */ private $st_contents; /** * Armazena o nome do arquivo de visualizao * @var string */ private $st_view; /** * Armazena os dados que devem ser mostrados ao reenderizar o * arquivo de visualizao * @var Array */ private $v_params; /** * possivel efetuar a parametrizao do objeto ao instanciar o mesmo, * $st_view o nome do arquivo de visualizao a ser usado e * $v_params so os dados que devem ser utilizados pela camada de visualizao * * @param string $st_view * @param Array $v_params */ function __construct($st_view = null, $v_params = null) { if($st_view != null) $this->setView($st_view); $this->v_params = $v_params; } /** * Define qual arquivo html deve ser renderizado * @param string $st_view * @throws Exception */ public function setView($st_view) { if(file_exists($st_view)) $this->st_view = $st_view; else throw new Exception("View File '$st_view' don't

51 exists"); } 52 53 /** 54 * Retorna o nome do arquivo que deve ser renderizado 55 * @return string */ 56 public function getView() 57 { 58 return $this->st_view; 59 } 60 61 /** * Define os dados que devem ser repassados view 62 * @param Array $v_params 63 */ 64 public function setParams(Array $v_params) 65 { 66 $this->v_params = $v_params; } 67 68 /** 69 * Retorna os dados que foram ser repassados ao arquivo de 70 visualizao 71 * @return Array */ 72 public function getParams() 73 { 74 return $this->v_params; 75 } 76 77 /** * Retorna uma string contendo todo 78 * o conteudo do arquivo de visualizao 79 * 80 * @return string 81 */ 82 public function getContents() { 83 ob_start(); 84 if(isset($this->st_view)) 85 require_once $this->st_view; 86 $this->st_contents = ob_get_contents(); ob_end_clean(); 87 return $this->st_contents; 88 } 89 90 /** 91 * Imprime o arquivo de visualizao 92 */ public function showContents() 93 { 94 echo $this->getContents(); 95 exit; 96 } 97 } 98 ?> 99 100

101 102 103 104 105 106 107 108 109 110 111 112 113 Os dados de algumas classes da camada de modelo devero persistir no banco de dados, para isso, ser necessrio a implementao de pequeno bloco de cdigo responsvel pela conexo entre o sistema e o SGDB. A alternativa que adotarei ser a de implementar uma classe abstrata, que dever ser herdada pelas classes em que os dados devero ser armazenados no banco de dados. Veja cdigo da classe abstrata a seguir. 1 <?php /** 2 * Classe Abstrata responsvel por centralizar a conexo 3 * com o banco de dados 4 * 5 * @package Exemplo simples com MVC @author DigitalDev 6 * * @version 0.1.1 7 * 8 * Diretrio Pai - lib 9 * Arquivo - PersistModelAbstract.php 10 */ abstract class PersistModelAbstract 11{ 12 /** * Varivel responsvel por guardar dados da conexo do banco 13 * @var resource 14 */ 15 protected $o_db; 16 17 function __construct() 18 { 19 // Inicio de conexo com SQLite 20 $this->o_db = new PDO("sqlite:./databases/db.sq3"); 21 $this->o_db->setAttribute ( PDO::ATTR_ERRMODE , 22PDO::ERRMODE_EXCEPTION ); 23 // Fim de conexo com SQLite 24 25 26 /* //Inicio de conexo com MySQL 27 $st_host = 'ip ou host'; 28 $st_banco = 'bancodedados'; 29 $st_usuario = 'usuario'; 30 $st_senha = 'senha';

31 32 33 34 35 36 37 38 39 40 41} 42?> 43 44 45 46 47 48 49

$st_dsn = "mysql:host=$st_host;dbname=$st_banco"; $this->o_db = new PDO ( $st_dsn, $st_usuario, $st_senha ); //Fim de conexo com MySQL */ }

Conforme Vocs j devem ter visto, a classe PersistModelAbstract faz referncia ao arquivo db.sq3 dentro do diretrio databases, esse arquivo ser o responsvel por guardar nossos dados. Irei mostrar isso na prtica com o continuar desse artigo. Implementarei tambm, duas outras pequenas classes, a primeira ser usada para filtrar os dados passados via POST e GET e a segunda ser usada para validar os dados. Irei cham-las de DataFilter e DataValidator, respectivamente. 1 <?php /** 2 * Classe designada a filtragem de dados 3 * 4 * @package Exemplo simples com MVC 5 * @author DigitalDev @version 0.1.1 6 * * 7 * Diretrio Pai - lib 8 * Arquivo - DataFilter.php 9 */ 10class DataFilter { 11 /** 12 * Retira pontuacao da string * @param string $st_data 13 * @return string 14 */ 15 static function alphaNum( $st_data ) 16 { 17 $st_data = preg_replace("([[:punct:]]| )",'',$st_data); return $st_data; 18 } 19 20 /** 21 * Retira caracteres nao numericos da string 22 * @param string $st_data

* @return string 23 */ 24 static function numeric( $st_data ) 25 { 26 $st_data = preg_replace("([[:punct:]]|[[:alpha:]]| 27)",'',$st_data); return $st_data; 28 } 29 30 31 /** 32 * 33 * Retira tags HTML / XML e adiciona "\" antes * de aspas simples e aspas duplas 34 * @param string $st_string 35 */ 36 static function cleanString( $st_string ) 37 { 38 return addslashes(strip_tags($st_string)); } 39 40} ?> 41 42 43 44 45 46 47 48

1 /** 2 * Classe designada a validacao de formato de dados 3 * 4 * @package Exemplo simples com MVC 5 * @author DigitalDev * @version 0.1.1 6 * 7 * Diretrio Pai - lib 8 * Arquivo - DataValidator.php 9 */ DataValidator 10class { 11 /** 12 * Verifica se o dado passado esta vazio * @param mixed $mx_value 13 * @return boolean 14 */ 15 static function isEmpty( $mx_value ) 16 { if(!(strlen($mx_value) > 0)) 17 return true; 18 return false; 19 } 20 21 /**

<?php

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44} ?> 45 46 47 48 49 50 51 52 53 54

* Verifica se o dado passado e um numero * @param mixed $mx_value; * @return boolean */ static function isNumeric( $mx_value ) { $mx_value = str_replace(',', '.', $mx_value); if(!(is_numeric($mx_value))) return false; return true; } /** * Verifica se o dado passado e um numero inteiro * @param mixed $mx_value; * @return boolean */ static function isInteger( $mx_value ) { if(!DataValidator::isNumeric($mx_value)) return false; if(preg_match('/[[:punct:]&^-]/', $mx_value) > 0) return false; return true; }

Por fim, irei implementar o cdigo do arquivo index.php, o mesmo ser extremamente simples. Veja abaixo. 1 <?php /** 2 * Primeiro arquivo a ser executado. 3 * aqui que tudo comea 4 * 5 * @package Exemplo simples com MVC @author DigitalDev 6 * * @version 0.1.1 7 * 8 * Diretrio Pai - raiz do site 9 * Arquivo - index.php 10*/ //configurando o PHP para mostrar os erros na tela 11ini_set('display_errors', 1); 12 13//configurando o PHP para reportar todo e qualquer erro

14error_reporting(E_ALL); 15 16require_once 'lib/Application.php'; $o_Application = new Application(); 17$o_Application->dispatch(); 18?> 19 20 21 22 Nesse estgio de desenvolvimento, a hierarquia de diretrios e arquivos deve se dar como a Imagem 2 mostrada abaixo.

Imagem 2 Hierarquia de Diretrios e Pastas

A Camada de Negcios
Agora, com as classes do sistema implementadas, irei finalmente me preocupar com as regras de negcio da Agenda Telefnica. Veja a documentao abaixo. Lista de requisitos

Permitir ao usurio visualizar a lista de contatos na tela principal do sistema. Permitir ao usurio administrar o cadastro de contatos. Permitir ao usurio, selecionar o contato e visualizar os telefones do mesmo. Permitir ao usurio administrar o cadastro de telefones do contato selecionado. Permitir ao usurio cadastrar n telefones para o contato selecionado.

Diagrama de Casos de Uso

Imagem 3 Diagrama de Casos de Uso Diagrama de Classe Nesse diagrama, apenas documentarei as classes diretamente ligadas ao sistema de Agenda Telefnica, a diagramao das classes que implementei acima no ser abordada.

Imagem 4 Diagrama de Classes Diagrama de Entidade Relacionamento Pelo fato da necessidade da persistir os dados de contato e dos telefones, ser necessrio duas tabelas para guardar os dados das classes. Veja o Diagrama de Entidade Relacionamento (DER) abaixo.

Imagem 5 Diagrama de Entidade Relacionamento Agora que j tenho a arquitetura da camada de negcio j definida, o proximo passo implementar as classes responsveis por gerenciar as mesmas. Isso mesmo, estou me referendo s classes Contato e Telefone, e como eu j havia falado, os dados das mesmas devem persistir no banco de dados, por esse motivo, elas devem herdar a classe PersistModelAbstract. 1 2 3 4
<?php //incluindo o arquivo contendo a classe TelefoneModel require_once 'models/TelefoneModel.php'; /**

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

* Responsvel por gerenciar e persistir os dados dos * Contatos da Agenda Telefnica * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Modelo ou Model. * Diretrio Pai - models * Arquivo - ContatoModel.php **/ class ContatoModel extends PersistModelAbstract { private $in_id; private $st_nome; private $st_email; function __construct() { parent::__construct(); //executa mtodo de criao da tabela de Telefone $this->createTableContato(); }

/** * Setters e Getters da * classe ContatoModel */ public function setId( $in_id ) { $this->in_id = $in_id; return $this; } public function getId() { return $this->in_id; } public function setNome( $st_nome ) { $this->st_nome = $st_nome; return $this; } public function getNome() { return $this->st_nome; } public function setEmail( $st_email ) { $this->st_email = $st_email; return $this; } public function getEmail()

{ 55 return $this->st_email; 56 } 57 58 /** 59 * Retorna um array contendo os contatos * @param string $st_nome 60 * @return Array 61 */ 62 public function _list( $st_nome = null ) 63 { 64 if(!is_null($st_nome)) $st_query = "SELECT * FROM tbl_contato WHERE con_st_nome 65 LIKE '%$st_nome%';"; 66 else 67 $st_query = 'SELECT * FROM tbl_contato;'; 68 69 $v_contatos = array(); 70 try { 71 $o_data = $this->o_db->query($st_query); 72 while($o_ret = $o_data->fetchObject()) 73 { 74 $o_contato = new ContatoModel(); $o_contato->setId($o_ret->con_in_id); 75 $o_contato->setNome($o_ret->con_st_nome); 76 $o_contato->setEmail($o_ret->con_st_email); 77 array_push($v_contatos, $o_contato); 78 } 79 } catch(PDOException $e) 80 {} 81 return $v_contatos; 82 } 83 84 /** 85 * Retorna os dados de um contato referente * a um determinado Id 86 * @param integer $in_id 87 * @return ContatoModel 88 */ 89 public function loadById( $in_id ) { 90 $v_contatos = array(); 91 $st_query = "SELECT * FROM tbl_contato WHERE con_in_id = 92 $in_id;"; 93 $o_data = $this->o_db->query($st_query); 94 $o_ret = $o_data->fetchObject(); $this->setId($o_ret->con_in_id); 95 $this->setNome($o_ret->con_st_nome); 96 $this->setEmail($o_ret->con_st_email); 97 return $this; 98 } 99 100 /** * Salva dados contidos na instancia da classe 101 * na tabela de contato. Se o ID for passado, 102 * um UPDATE ser executado, caso contrrio, um 103 * INSERT ser executado 104 * @throws PDOException

* @return integer 105 */ 106 public function save() 107 { 108 if(is_null($this->in_id)) $st_query = "INSERT INTO tbl_contato 109 ( 110 con_st_nome, 111 con_st_email 112 ) 113 VALUES ( 114 '$this->st_nome', 115 '$this->st_email' 116 );"; 117 else $st_query = "UPDATE 118 tbl_contato 119 SET 120 con_st_nome = '$this->st_nome', 121 con_st_email = '$this->st_email' 122 WHERE con_in_id = $this->in_id"; 123 try 124 { 125 126 if($this->o_db->exec($st_query) > 0) 127 if(is_null($this->in_id)) 128 { /* 129 * verificando se o driver usado sqlite e 130 pegando o ultimo id inserido 131 * por algum motivo, a funo nativa do 132PDO::lastInsertId() no funciona com sqlite */ 133 if($this->o_db134 >getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') 135 { 136 $o_ret = $this->o_db->query('SELECT 137last_insert_rowid() AS con_in_id')->fetchObject(); return $o_ret->con_in_id; 138 } 139 else 140 return $this->o_db->lastInsertId(); 141 } else 142 return $this->in_id; 143 } 144 catch (PDOException $e) 145 { 146 throw $e; } 147 return false; 148 } 149 150 /** 151 * Deleta os dados persistidos na tabela de 152 * contato usando como referencia, o id da classe. */ 153 public function delete() 154 {

if(!is_null($this->in_id)) 155 { 156 $st_query = "DELETE FROM 157 tbl_contato 158 WHERE con_in_id = $this->in_id"; if($this->o_db->exec($st_query) > 0) 159 return true; 160 } 161 return false; 162 } 163 164 /** * Cria tabela para armazernar os dados de contato, caso 165 * ela ainda no exista. 166 * @throws PDOException 167 */ 168 private function createTableContato() 169 { /* 170 * No caso do Sqlite, o AUTO_INCREMENT automtico na chave 171 primaria da tabela 172 * No caso do MySQL, o AUTO_INCREMENT deve ser especificado 173na criao do campo */ 174 if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 175 'sqlite') 176 $st_auto_increment = ''; 177 else 178 $st_auto_increment = 'AUTO_INCREMENT'; 179 $st_query = "CREATE TABLE IF NOT EXISTS tbl_contato 180 ( 181 con_in_id INTEGER NOT NULL 182$st_auto_increment, 183 con_st_nome CHAR(200), con_st_email CHAR(100), 184 PRIMARY KEY(con_in_id) 185 )"; 186 187 //executando a query; 188 try 189 { $this->o_db->exec($st_query); 190 } 191 catch(PDOException $e) 192 { 193 throw $e; 194 } } 195 } 196 ?> 197 198 199 200 201 202 203 204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 Agora a vez da classe TelefoneModel ser implementada. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
<?php /** * Responsvel por gerenciar e persistir os dados de telefones dos * Contatos da Agenda Telefonica * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Models ou Modelo * Diretrio Pai - models * Arquivo - TelefoneModel.php */ class TelefoneModel extends PersistModelAbstract { private $in_id; private $in_ddd; private $in_telefone; private $in_contato_id; function __construct() { parent::__construct(); //executa mtodo de criao da tabela de Telefone $this->createTableTelefone(); }

/**

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

* Setters e Getters da * classe TelefoneModel */ public function setId( $in_id ) { $this->in_id = $in_id; return $this; } public function getId() { return $this->in_id; } public function setDDD( $in_ddd ) { $this->in_ddd = $in_ddd; return $this; } public function getDDD() { return $this->in_ddd; } public function setTelefone( $in_telefone ) { $this->in_telefone = $in_telefone; return $this; } public function getTelefone() { return $this->in_telefone; } public function setContatoId( $in_contato_id ) { $this->in_contato_id = $in_contato_id; return $this; } public function getContatoId() { return $this->in_contato_id; }

/** * Retorna um array contendo os telefones * de um determinado contato * @param integer $in_contato_id * @return Array */ public function _list( $in_contato_id ) { $st_query = "SELECT * FROM tbl_telefone WHERE con_in_id = $in_contato_id";

$v_telefones = array(); 76 try 77 { 78 $o_data = $this->o_db->query($st_query); 79 while($o_ret = $o_data->fetchObject()) { 80 $o_telefone = new TelefoneModel(); 81 $o_telefone->setId($o_ret->tel_in_id); 82 $o_telefone->setDDD($o_ret->tel_in_ddd); 83 $o_telefone->setTelefone($o_ret->tel_in_telefone); 84 $o_telefone->setContatoId($o_ret->con_in_id); array_push($v_telefones,$o_telefone); 85 } 86 } 87 catch(PDOException $e) 88 {} return $v_telefones; 89 } 90 91 /** 92 * Retorna os dados de um telefone referente 93 * a um determinado Id 94 * @param integer $in_id * @return TelefoneModel 95 */ 96 public function loadById( $in_id ) 97 { 98 $v_contatos = array(); 99 $st_query = "SELECT * FROM tbl_telefone WHERE tel_in_id = $in_id;"; 100 try 101 { 102 $o_data = $this->o_db->query($st_query); 103 $o_ret = $o_data->fetchObject(); $this->setId($o_ret->tel_in_id); 104 $this->setDDD($o_ret->tel_in_ddd); 105 $this->setTelefone($o_ret->tel_in_telefone); 106 $this->setContatoId($o_ret->con_in_id); 107 return $this; 108 } catch(PDOException $e) 109 {} 110 return false; 111 } 112 113 /** 114 * Salva dados contidos na instancia da classe * na tabela de telefone. Se o ID for passado, 115 * um UPDATE ser executado, caso contrrio, um 116 * INSERT ser executado 117 * @throws PDOException 118 * @return integer */ 119 public function save() 120 { 121 if(is_null($this->in_id)) 122 $st_query = "INSERT INTO tbl_telefone 123 ( con_in_id, 124 tel_in_ddd, 125 tel_in_telefone

) 126 VALUES 127 ( 128 $this->in_contato_id, 129 '$this->in_ddd', '$this->in_telefone' 130 );"; 131 else 132 $st_query = "UPDATE 133 tbl_telefone 134 SET tel_in_ddd = '$this->in_ddd', 135 tel_in_telefone = '$this->in_telefone' 136 WHERE 137 tel_in_id = $this->in_id"; 138 try { 139 140 if($this->o_db->exec($st_query) > 0) 141 if(is_null($this->in_id)) 142 { 143 /* 144 * verificando se o driver usado sqlite e 145pegando o ultimo id inserido * por algum motivo, a funo nativa do 146 PDO::lastInsertId() no funciona com sqlite 147 */ 148 if($this->o_db149>getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') { 150 $o_ret = $this->o_db->query('SELECT 151 last_insert_rowid() AS tel_in_id')->fetchObject(); 152 return $o_ret->tel_in_id; 153 } else 154 return $this->o_db->lastInsertId(); 155 } 156 else 157 return $this->in_id; 158 } catch (PDOException $e) 159 { 160 throw $e; 161 } 162 return false; } 163 164 /** 165 * Deleta os dados persistidos na tabela de 166 * telefone usando como referencia, o id da classe. 167 */ 168 public function delete() { 169 if(!is_null($this->in_id)) 170 { 171 $st_query = "DELETE FROM 172 tbl_telefone 173 WHERE tel_in_id = $this->in_id"; if($this->o_db->exec($st_query) > 0) 174 return true; 175 }

return false; 176 } 177 178 179 180 /** 181 * Cria tabela para armazernar os dados de telefone, caso 182 * ela ainda no exista. 183 * @throws PDOException */ 184 private function createTableTelefone() 185 { 186 /* 187 * No caso do Sqlite, o AUTO_INCREMENT automtico na chave 188primaria da tabela * No caso do MySQL, o AUTO_INCREMENT deve ser especificado 189 na criao do campo 190 */ 191 if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 192'sqlite') $st_auto_increment = ''; 193 else 194 $st_auto_increment = 'AUTO_INCREMENT'; 195 196 197 $st_query = "CREATE TABLE IF NOT EXISTS tbl_telefone 198 ( 199 tel_in_id INTEGER NOT NULL $st_auto_increment, 200 con_in_id INTEGER NOT NULL, 201 tel_in_ddd CHAR(5), 202 tel_in_telefone CHAR(12), 203 PRIMARY KEY(tel_in_id) )"; 204 205 //executando a query; 206 try 207 { 208 $this->o_db->exec($st_query); 209 } catch(PDOException $e) 210 { 211 throw $e; 212 } 213 } 214} 215?> 216 217 218 219 220 221 222 223 224 225

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 Depois das classes da camada de modelo implementadas, o prximo passo escrever o cdigo das classes de controle referente ao fluxo de gerenciamento das classes acima. Elas tambm sero duas, ContatoController e TelefoneController. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 1
<?php //incluindo classes da camada Model require_once 'models/ContatoModel.php'; /** * Responsvel por gerenciar o fluxo de dados entre * a camada de modelo e a de visualizao * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Controladores ou Controllers * Diretrio Pai - controllers * Arquivo - ContatoController.php * */ class ContatoController { /** * Efetua a manipulao dos modelos necessrios * para a aprensentao da lista de contatos */ public function listarContatoAction() { $o_Contato = new ContatoModel();

6 1 7 1 8 1 9 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 4 0 4

//Listando os contatos cadastrados $v_contatos = $o_Contato->_list(); //definindo qual o arquivo HTML que ser usado para //mostrar a lista de contatos $o_view = new View('views/listarContatos.phtml'); //Passando os dados do contato para a View $o_view->setParams(array('v_contatos' => $v_contatos)); //Imprimindo cdigo HTML $o_view->showContents(); }

/** * Gerencia a requisies de criao * e edio dos contatos */ public function manterContatoAction() { $o_contato = new ContatoModel(); //verificando se o id do contato foi passado if( isset($_REQUEST['in_con']) ) //verificando se o id passado valido if( DataValidator::isNumeric($_REQUEST['in_con']) ) //buscando dados do contato $o_contato->loadById($_REQUEST['in_con']); if(count($_POST) > 0) { $o_contato>setNome(DataFilter::cleanString($_POST['st_nome'])); $o_contato>setEmail(DataFilter::cleanString($_POST['st_email'])); //salvando dados e redirecionando para a lista de contatos if($o_contato->save() > 0) Application::redirect('?controle=Contato&acao=listarC ontato'); } $o_view = new View('views/manterContato.phtml'); $o_view->setParams(array('o_contato' => $o_contato)); $o_view->showContents(); } /** * Gerencia a requisies de excluso dos contatos */ public function apagarContatoAction() { if( DataValidator::isNumeric($_GET['in_con']) ) { //apagando o contato $o_contato = new ContatoModel(); $o_contato->loadById($_GET['in_con']);

1 4 2 4 3 4 4 4 to'); 5 } 4} 6 ?> 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6 2 6 3 6 4 6 5 6

$o_contato->delete(); //Apagando os telefones do contato $o_telefone = new TelefoneModel(); $v_telefone = $o_telefone->_list($_GET['in_con']); foreach($v_telefone AS $o_telefone) $o_telefone->delete(); Application::redirect('?controle=Contato&acao=listarConta }

6 6 7 6 8 6 9 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8 0 8 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9 9 0 9

1 9 2 9 3 9 4 e por sua vez a classe TelefoneController 1 <?php //incluindo classes da camada Model 2 require_once 'models/TelefoneModel.php'; 3 require_once 'models/ContatoModel.php'; 4 5 /** 6 * Responsvel por gerenciar o fluxo de dados entre 7 * a camada de modelo e a de visualizao * 8 * @package Exemplo simples com MVC 9 * @author DigitalDev 1 * @version 0.1.1 0* Camada - Controladores ou Controllers 1* * Diretrio Pai - controllers 1 * Arquivo - TelefoneController.php 1 */ 2 class TelefoneController 1{ 3 /** 1 * Efetua a manipulao dos modelos necessrios 4 * para a aprensentao da lista de telefones do contato */ 1 public function listarTelefonesAction() 5 { 1 if( isset($_REQUEST['in_con']) ) 6 if( DataValidator::isNumeric($_REQUEST['in_con']) ) 1 { $o_contato = new ContatoModel(); 7 $o_contato->loadById($_REQUEST['in_con']); 1 8 $o_telefone = new TelefoneModel(); 1 $v_telefones = $o_telefone->_list($_GET['in_con']); 9 $o_view = new View('views/listarTelefones.phtml'); 2 $o_view->setParams(array('o_contato' => 0 $o_contato,'v_telefones' => $v_telefones)); $o_view->showContents(); 2 } 1 } 2 2 /** 2 * Gerencia a requisies de criao * e edio dos telefones do contato 3 */ 2 public function manterTelefoneAction() 4 { 2 $o_contato = new ContatoModel();

$o_telefone = new TelefoneModel(); 5 2 if( isset($_REQUEST['in_con']) ) 6 if( DataValidator::isInteger($_REQUEST['in_con']) ) 2 $o_contato->loadById($_REQUEST['in_con']); 7 2 if( isset($_REQUEST['in_tel']) ) 8 if( DataValidator::isInteger($_REQUEST['in_tel']) ) $o_telefone->loadById($_REQUEST['in_tel']); 2 9 if(count($_POST) > 0) 3 { 0 $o_telefone3 >setDDD(DataFilter::numeric($_POST['in_ddd'])); $o_telefone1 >setTelefone(DataFilter::numeric($_POST['in_telefone'])); 3 $o_telefone->setContatoId($o_contato->getId()); 2 if($o_telefone->save() > 0) 3 Application::redirect('?controle=Telefone&acao=listar 3 Telefones&in_con='.$o_contato->getId()); } 3 4 $o_view = new View('views/manterTelefone.phtml'); 3 $o_view->setParams(array('o_contato' => 5 $o_contato,'o_telefone' => $o_telefone)); 3 $o_view->showContents(); 6 } 3 /** 7 * Gerencia a requisies de excluso de telefones do contato 3 */ 8 public function apagarTelefoneAction() 3 { if( isset($_GET['in_tel']) ) 9 if( DataValidator::isInteger($_GET['in_tel'])) 4 { 0 $o_telefone = new TelefoneModel(); 4 $o_telefone->loadById($_GET['in_tel']); 1 $o_telefone->delete(); Application::redirect('?controle=Telefone&acao=listar 4 Telefones&in_con='.$_GET['in_con']); 2 } 4 } 3} 4 ?> 4 4 5 4 6 4 7 4 8 4 9 5

0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 6 0 6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 7 0 7 1 7 2 7 3 7 4 7

5 7 6 7 7 7 8 7 9 8 0 8 1 8 2 8 3 8 4 8 5 8 6 8 7

Arquivos de visualizao ou views


As classes implementadas acima esto fazendo referncia arquivos HTML contidos na camada de visualizao, para esse programa funcionar, ser preciso implement-los. Isso o que irei fazer agora. Arquivo listarContatos.phtml Diretrio Pai views 1 <?php 2 $v_params = $this->getParams(); = $v_params['v_contatos']; 3 $v_contatos ?> 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 5 "http://www.w3.org/TR/html4/loose.dtd"> 6 <html> 7 <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF8 8"> 9 <title> Agenda Telef&ocirc;nica - Exemplo de MVC com PHP 10 </title> 11 <link rel="stylesheet" type="text/css" 12 href="template/css/default.css" /> 13</head> 14<body> <div align="center"> 15

<table width="80%" border="1"> 16 <tr> 17 <th> 18 ID 19 </th> <th> 20 Nome 21 </th> 22 <th> 23 E-mail 24 </th> <th colspan="3"> 25 A&ccedil;&otilde;es 26 </th> 27 </tr> 28 <?php foreach($v_contatos AS $o_contato) 29 { 30 ?> 31 <tr> 32 <td> 33 <?php echo $o_contato->getId()?> </td> 34 <td> 35 <?php echo $o_contato->getNome()?> 36 </td> 37 <td> 38 <?php echo $o_contato->getEmail()?> </td> 39 <td align="center"> 40 <a 41href='?controle=Telefone&acao=listarTelefones&in_con=<?php echo 42$o_contato->getId()?>'>Telefones</a> </td> 43 <td align="center"> 44 <a 45href='?controle=Contato&acao=manterContato&in_con=<?php echo 46$o_contato->getId()?>'>Editar</a> 47 </td> <td align="center"> 48 <a 49 href='?controle=Contato&acao=apagarContato&in_con=<?php echo 50$o_contato->getId()?>'>Apagar</a> 51 </td> </tr> 52 <?php 53 } 54 ?> 55 </table> 56 <br /> <a href='?controle=Contato&acao=manterContato'>Novo 57 Contato</a> 58 </div> 59</body> 60</html> 61 62 63

Arquivo listarTelefones.phtml Diretrio Pai views 1 $v_params = $this->getParams(); 2 $o_contato = $v_params['o_contato']; 3 $v_telefones = $v_params['v_telefones']; 4 ?> 5 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 6 <html> 7 <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF8 8"> 9 <title> 10 Agenda Tele&ocirc;nica - Exemplo de MVC com PHP 11 </title> 12 <link rel="stylesheet" type="text/css" href="template/css/default.css" /> 13 </head> 14 <body> 15 <div align="center"> 16 <table width="80%" border="1"> <tr> 17 <th colspan='3'> 18 Contato 19 </th> 20 </tr> 21 <tr> <th> 22 ID 23 </th> 24 <th> 25 Nome </th> 26 <th> 27 E-mail 28 </th> 29 </tr> 30 <tr> <td> 31 <?php echo $o_contato->getId()?> 32 </td> 33 <td> 34 <?php echo $o_contato->getNome()?> 35 </td> <td> 36 <?php echo $o_contato->getEmail()?> 37 </td> 38 </tr> 39 </table> <br /> 40 <table width="50%" border="1"> 41 <tr> 42 <th colspan="5"> 43 Telefones 44 </th> </tr> 45 <tr> 46 <th> 47 ID
<?php

</th> 48 <th> 49 DDD 50 </th> 51 <th> Telefone 52 </th> 53 <th colspan="2"> 54 A&ccedil;&otilde;es 55 </th> 56 </tr> <?php 57 foreach($v_telefones AS $o_telefone) 58 { 59 ?> 60 <tr> <td align="center"> 61 <?php echo $o_telefone->getId()?> 62 </td> 63 <td align="center"> 64 <?php echo $o_telefone->getDDD()?> 65 </td> <td align="center"> 66 <?php echo $o_telefone->getTelefone()?> 67 </td> 68 <td align="center"> 69 <a 70href='?controle=Telefone&acao=apagarTelefone&in_con=<?php echo 71$o_contato->getId()?>&in_tel=<?php echo $o_telefone72>getId()?>'>Apagar</a> </td> 73 <td align="center"> 74 <a 75href='?controle=Telefone&acao=manterTelefone&in_con=<?php echo 76$o_contato->getId()?>&in_tel=<?php echo $o_telefone77>getId()?>'>Editar</a> </td> 78 </tr> 79 <?php } 80 ?> 81 </table> 82 <br /> 83 <a href='?controle=Index'>Voltar</a> <a href='?controle=Telefone&acao=manterTelefone&in_con=<?php 84 echo $o_contato->getId()?>'>Novo Telefone</a> 85 </div> 86</body> 87</html> 88 89 90 91 92 93 94 95 96 97

Arquivo manterContato.phtml Diretrio Pai views 1 $v_params = $this->getParams(); 2 $o_contato = $v_params['o_contato']; 3 ?> 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 5 "http://www.w3.org/TR/html4/loose.dtd"> <html> 6 <head> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF8"> 8 <title> 9 Agenda Telef&ocirc;nica - Exemplo de MVC com PHP 10 </title> 11 <link rel="stylesheet" type="text/css" 12href="template/css/default.css" /> 13</head> 14<body> <div align="center"> 15 <form method='post'> 16 <table width="300" border="1"> <tr> 17 <th> 18 Nome 19 </th> 20 <th> 21 E-mail </th> 22 <th colspan="2"> 23 A&ccedil;&otilde;es 24 </th> 25 </tr> <tr> 26 <td> 27 <input type='text' name='st_nome' value='<?php 28 echo $o_contato->getNome()?>'> 29 </td> 30 <td> <input type='text' name='st_email' value='<?php 31 32echo $o_contato->getEmail()?>'> </td> 33 <td align="center"> 34 <a 35href='?controle=Contato&acao=listarContato'>Cancelar</a> </td> 36 <td align="center"> 37 <input type='hidden' name='controle' 38value='Contato'> 39 <input type='hidden' name='acao' 40value='manterContato'> <input type='hidden' name='in_con' value='<?php 41 echo $o_contato->getId()?>'> 42 <button type='submit'>Salvar</button> 43 </td> 44 </tr> </table> 45 </form> 46 </div> 47</body>
<?php

48</html> 49 50 Arquivo manterTelefone.phtml Diretrio Pai views 1 <?php $v_params = $this->getParams(); 2 $o_contato = $v_params['o_contato']; 3 $o_telefone = $v_params['o_telefone']; 4 ?> 5 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 6 <html> 7 <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF8 8"> 9 <title> 10 Agenda Telef&ocirc;nica - Exemplo de MVC com PHP 11 </title> 12 <link rel="stylesheet" type="text/css" 13href="template/css/default.css" /> 14</head> <body> 15 <div align="center"> 16 <table width="80%" border="1"> 17 <tr> <th colspan='3'> 18 Contato 19 </th> 20 </tr> 21 <tr> <th> 22 ID 23 </th> 24 <th> 25 Nome 26 </th> <th> 27 E-mail 28 </th> 29 </tr> 30 <tr> <td> 31 <?php echo $o_contato->getId()?> 32 </td> 33 <td> 34 <?php echo $o_contato->getNome()?> 35 </td> <td> 36 <?php echo $o_contato->getEmail()?> 37 </td> 38 </tr> 39 </table> <br /> 40 <form method='post'> 41 <table width="40%" border="1"> 42 <tr> 43 <th colspan="4">

Telefones 44 </th> 45 </tr> 46 <tr> 47 <th> DDD 48 </th> 49 <th> 50 Telefone 51 </th> 52 <th> A&ccedil;&otilde;es 53 </th> 54 </tr> 55 <tr> 56 <td align="center"> <input type='text' name='in_ddd' value='<?php echo 57 58$o_telefone->getDDD()?>' size="5" maxlength="3"> </td> 59 <td align="center"> 60 <input type='text' name='in_telefone' value='<?php 61echo $o_telefone->getTelefone()?>'> </td> 62 <td align="center"> 63 <input type='hidden' name='controle' 64value='Telefone'> 65 <input type='hidden' name='acao' 66value='manterTelefone'> <input type='hidden' name='in_con' value='<?php 67 echo $o_contato->getId()?>'> 68 <input type='hidden' name='in_tel' value='<?php 69echo $o_telefone->getId()?>'> 70 <button type='submit'>Salvar</button> </td> 71 </tr> 72 </table> 73 </form> 74 <br /> 75 <a href='?controle=Telefone&acao=listarTelefones&in_con=<?php 76echo $o_contato->getId()?>'>Cancelar</a> </div> 77 </body> 78</html> 79 80 81 82 83 84 85 86

Cada mtodo (Action) da classe de controle (Controller) faz referncia um arquivo HTML (View). Assim se cumpre a profecia de intermdio entre a camada de controle e a camada de visualizao. Onde o controle faz consultas camada de modelo e repassa a resposta para a camada de visualizao.

Aps o cdigo implementado, para selecionar o Controlador e a Ao que quer executar, basta envia-las via GET ou POST, como por exemplo ?controle=Contato&acao=listarContato. Com esses parmetros, o mtodo listarContatoAction da classe ContatoController ser executado. Para entender um pouco melhor o funcionamento desse programa, visualize o Diagrama de Sequncia da funcionalidade Listar Contatos mostrado na Imagem 6 abaixo. Ele ser executado por padro quando nenhum outro for requisitado pelo usurio.

Imagem 6 Diagrama de Sequncia do Caso de Uso Listar Contatos Quer ver isso funcionando? Acesse o link http://digitaldev.com.br/exemploMVC/ Quer fazer download do cdigo para estudar? Acesse o link http://digitaldev.com.br/downloads/exemploMVC.zip

Conectando a agenda com o MySQL


Caso voc queira conectar a aplicao desenvolvida com o MySQL, basta editar o arquivo lib/PersistModelAbstract.php e deix-lo como abaixo, preenchendo apenas os dados da sua conexo e credenciais referentes. 1 2 3 4
<?php /** * Classe Abstrata responsvel por centralizar a conexo * com o banco de dados *

5 * @package Exemplo simples com MVC 6 * @author DigitalDev * @version 0.1.1 7 * 8 * Diretrio Pai - lib 9 * Arquivo - PersistModelAbstract.php 10 */ class PersistModelAbstract 11abstract { 12 /** 13 * Varivel responsvel por guardar dados da conexo do banco * @var resource 14 */ 15 protected $o_db; 16 17 function __construct() 18 { 19 20 /* // Inicio de conexo com SQLite 21 $this->o_db = new PDO("sqlite:./databases/db.sq3"); 22 $this->o_db->setAttribute ( PDO::ATTR_ERRMODE , 23PDO::ERRMODE_EXCEPTION ); 24 // Fim de conexo com SQLite */ 25 26 //Inicio de conexo com MySQL 27 $st_host = 'ip ou host'; 28 $st_banco = 'bancodedados'; 29 $st_usuario = 'usuario'; 30 $st_senha = 'senha'; 31 32 $st_dsn = "mysql:host=$st_host;dbname=$st_banco"; 33 $this->o_db = new PDO 34 ( 35 $st_dsn, 36 $st_usuario, 37 $st_senha ); 38 //Fim de conexo com MySQL 39 } 40} 41?> 42 43 44 45 46 47 48

Mais sobre MVC com PHP


H um tempo atrs, encontrei um material em vdeo muito bom sobre como montar um Mini-Framework no padro MVC com PHP. Para acessar esse videos, clique em Criando um Mini Framework PHP 5 com MVC

Mais sobre Padres de Projeto


O MVC um dos Padres de Projetos mais importantes hoje em dia, mas no est sozinho. Existem vrios outros padres por ai, eles nos ajudam a organizar o cdigo, resolver problemas que costumamos enfrentar no dia-dia da Programao Orientada a Objeto. Sendo assim, vale muito a pena estudar sobre eles, pois um bom programador no aquele que apenas codifica solues excepcionais, mas tambm o que consegue organizar com clareza seu cdigo. Veja uma lista de alguns padres organizados por suas categorias abaixo, j um pontap inicial para se estudar. Espero ter ajudado.

Padres de criao
Abstract Factory, Builder, Factory Method, Prototype, Singleton

Padres estruturais
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Padres comportamentais
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor

Você também pode gostar