Nesse artigo irei demonstrar na prática, a aplicação do padrão de projeto MVC com PHP. Para isso, construirei um aplicativo de Agenda de Contatos Telefônicos conforme requisitos funcionais, diagramas e código que serão mostrados ao longo desse texto. Mas, antes de começar, aconselho a vocês que leiam o artigo de introdução a MVC, o mesmo poderá ser visualizado acessando o link Entendendo o MVC (Model-View-Controller).
Hierarquia de Diretórios
Conforme a Imagem 1, irei seguir um padrão de hierarquia de diretórios e arquivos, vou explicar sobre as responsabilidades dos diretórios e arquivos logo abaixo.
Diretório “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, responsáveis por fazer o intermédio entre a camada de dados (models ou modelos) e visualização (views), conforme o artigo de introdução a MVC citado acima.
Diretório “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 diretório que o arquivo referente ao banco ficará.
Caso queria conectar a aplicação com o MySQL, veja como no fim do artigo
Diretório “lib”
Nesse diretório, irei guardar as classes diretamente ligadas ao sistema, como por exemplo, classes de filtros de dados, validações genéricas, helpers (caso haja algum), interfaces e abstrações não ligadas à camada de negócio do sistema. Se estivesse construindo um framework em PHP, guardaria as classes do mesmo nesse diretório.
Diretório “models”
Aqui, guardarei as classes de dados diretamente abstraídas e ligadas às regras de negócio do sistema, como por exemplo, as classes Contato e Telefone, entre outras.
Diretório “views”
Esse é o diretório onde guardarei os arquivos HTML do sistema, tais arquivos representam a camada de visualização (view), da qual foi falada no artigo de introdução ao MVC.
Arquivo “index.php”
É um arquivo índex como qualquer outro, é nele que a execução do sistema irá começar.
Implementação das Classes das Camadas
Classes Primárias ou Genéricas
Depois de todos os diretórios explicados, mostrarei a implementação de algumas classes necessárias para a separação do sistemas em camadas (Modelo – Model, Visão – View , Controlador – Controller). A primeira delas que mostrarei, é a de controlador genérico, não diretamente ligado às regras de negócio do sistema de Agenda Telefônica. Veja o exemplo abaixo.
<?php /** * Controlador que deverá ser chamado quando não for * especificado nenhum outro * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Controladores ou Controllers * Diretório Pai - controllers * Arquivo - IndexController.php */ class IndexController { /** * Ação que deverá ser executada quando * nenhuma outra for especificada, do mesmo jeito que o * arquivo index.html ou index.php é executado quando nenhum * é referenciado */ public function indexAction() { //redirecionando para a pagina de lista de contatos header('Location: ?controle=Contato&acao=listarContato'); } } ?>
Conforme vocês devem ter visto acima, usei o sufixo Controller no nome da classe e Action no nome do método. Esse é o padrão de nomenclatura que irei utilizar em todas as outras classes da camada Controle e seus métodos. Além disso, nomearei os arquivos que irão conter as classes com o mesmo nome da própria 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 método da classe (Action) o usuário deseja executar. Dê uma olhada no código abaixo, espero que ele seja simples o suficiente para esclarecer seu modo de funcionamento.
<?php /** * Camada - Controller * Diretório Pai - lib * Arquivo - Application.php **/ /** * Essa função garante que todas as classes * da pasta lib serão carregadas automaticamente */ function __autoload($st_class) { if(file_exists('lib/'.$st_class.'.php')) require_once 'lib/'.$st_class.'.php'; } /** * Verifica qual classe controlador (Controller) o usuário deseja chamar * e qual método dessa classe (Action) deseja executar * Caso o controlador (controller) não seja especificado, o IndexControllers será o padrão * Caso o método (Action) não seja especificado, o indexAction será o padrão * * @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 parâmetros de controlador (Controller) e ação (Action) foram * passados via parâmetros "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 padrão o controller 'IndexController'; */ $this->st_controller = isset($_REQUEST['controle']) ? $_REQUEST['controle'] : 'Index'; /* * Se a action nao for passada por GET, * assume-se como padrão a action 'IndexAction'; */ $this->st_action = isset($_REQUEST['acao']) ? $_REQUEST['acao'] : 'index'; } /** * * Instancia classe referente ao Controlador (Controller) e executa * método referente e acao (Action) * @throws Exception */ public function dispatch() { $this->loadRoute(); //verificando se o arquivo de controle existe $st_controller_file = 'controllers/'.$this->st_controller.'Controller.php'; if(file_exists($st_controller_file)) require_once $st_controller_file; else throw new Exception('Arquivo '.$st_controller_file.' nao encontrado'); //verificando se a classe existe $st_class = $this->st_controller.'Controller'; if(class_exists($st_class)) $o_class = new $st_class; else throw new Exception("Classe '$st_class' nao existe no arquivo '$st_controller_file'"); //verificando se o metodo existe $st_method = $this->st_action.'Action'; if(method_exists($o_class,$st_method)) $o_class->$st_method(); else throw new Exception("Metodo '$st_method' nao existe na classe $st_class'"); } /** * Redireciona a chamada http para outra página * @param string $st_uri */ static function redirect( $st_uri ) { header("Location: $st_uri"); } } ?>
Apesar da classe acima não ter semelhança alguma com a classe IndexController que escrevi, ela também faz parte da camada de controle (Controller), mas está num nível mais acima. O fato dela ser responsável por verificar qual controlador (Controller) e qual método (Action) irá executar, faz da mesma o coração do sistema.
A terceira classe que irei implementar será responsável por cuidar da camada de visualização. Apesar de código um pouco extenso, ela é uma classe sem muitas funcionalidade, mas poderá ser enriquecida de acordo com nossas necessidades futuras.
<?php /** * Essa classe é responsável por renderizar os arquivos HTML * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - lib * Arquivo - View.php */ class View { /** * Armazena o conteúdo HTML * @var string */ private $st_contents; /** * Armazena o nome do arquivo de visualização * @var string */ private $st_view; /** * Armazena os dados que devem ser mostrados ao reenderizar o * arquivo de visualização * @var Array */ private $v_params; /** * É possivel efetuar a parametrização do objeto ao instanciar o mesmo, * $st_view é o nome do arquivo de visualização a ser usado e * $v_params são os dados que devem ser utilizados pela camada de visualização * * @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 exists"); } /** * Retorna o nome do arquivo que deve ser renderizado * @return string */ public function getView() { return $this->st_view; } /** * Define os dados que devem ser repassados à view * @param Array $v_params */ public function setParams(Array $v_params) { $this->v_params = $v_params; } /** * Retorna os dados que foram ser repassados ao arquivo de visualização * @return Array */ public function getParams() { return $this->v_params; } /** * Retorna uma string contendo todo * o conteudo do arquivo de visualiza√ß√£o * * @return string */ public function getContents() { ob_start(); if(isset($this->st_view)) require_once $this->st_view; $this->st_contents = ob_get_contents(); ob_end_clean(); return $this->st_contents; } /** * Imprime o arquivo de visualização */ public function showContents() { echo $this->getContents(); exit; } } ?>
Os dados de algumas classes da camada de modelo deverão persistir no banco de dados, para isso, será necessário a implementação de pequeno bloco de código responsável pela conexão 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 deverão ser armazenados no banco de dados. Veja código da classe abstrata a seguir.
<?php /** * Classe Abstrata responsável por centralizar a conexão * com o banco de dados * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - lib * Arquivo - PersistModelAbstract.php */ abstract class PersistModelAbstract { /** * Variável responsável por guardar dados da conexão do banco * @var resource */ protected $o_db; function __construct() { // Inicio de conexão com SQLite $this->o_db = new PDO("sqlite:./databases/db.sq3"); $this->o_db->setAttribute ( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION ); // Fim de conexão com SQLite /* //Inicio de conexão com MySQL $st_host = 'ip ou host'; $st_banco = 'bancodedados'; $st_usuario = 'usuario'; $st_senha = 'senha'; $st_dsn = "mysql:host=$st_host;dbname=$st_banco"; $this->o_db = new PDO ( $st_dsn, $st_usuario, $st_senha ); //Fim de conexão com MySQL */ } } ?>
Conforme Vocês já devem ter visto, a classe PersistModelAbstract faz referência ao arquivo db.sq3 dentro do diretório databases, esse arquivo será o responsável por guardar nossos dados. Irei mostrar isso na prática com o continuar desse artigo.
Implementarei também, 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.
<?php /** * Classe designada a filtragem de dados * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - lib * Arquivo - DataFilter.php */ class DataFilter { /** * Retira pontuacao da string * @param string $st_data * @return string */ static function alphaNum( $st_data ) { $st_data = preg_replace("([[:punct:]]| )",'',$st_data); return $st_data; } /** * Retira caracteres nao numericos da string * @param string $st_data * @return string */ static function numeric( $st_data ) { $st_data = preg_replace("([[:punct:]]|[[:alpha:]]| )",'',$st_data); return $st_data; } /** * * Retira tags HTML / XML e adiciona "\" antes * de aspas simples e aspas duplas * @param string $st_string */ static function cleanString( $st_string ) { return addslashes(strip_tags($st_string)); } } ?>
<?php /** * Classe designada a validacao de formato de dados * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - lib * Arquivo - DataValidator.php */ class DataValidator { /** * Verifica se o dado passado esta vazio * @param mixed $mx_value * @return boolean */ static function isEmpty( $mx_value ) { if(!(strlen($mx_value) > 0)) return true; return false; } /** * 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 código do arquivo “index.php”, o mesmo será extremamente simples. Veja abaixo.
<?php /** * Primeiro arquivo a ser executado. * É aqui que tudo começa * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - raiz do site * Arquivo - index.php */ //configurando o PHP para mostrar os erros na tela ini_set('display_errors', 1); //configurando o PHP para reportar todo e qualquer erro error_reporting(E_ALL); require_once 'lib/Application.php'; $o_Application = new Application(); $o_Application->dispatch(); ?>
Nesse estágio de desenvolvimento, a hierarquia de diretórios e arquivos deve se dar como a Imagem 2 mostrada abaixo.
A Camada de Negócios
Agora, com as classes do sistema implementadas, irei finalmente me preocupar com as regras de negócio da Agenda Telefônica. Veja a documentação abaixo.
Lista de requisitos
- Permitir ao usuário visualizar a lista de contatos na tela principal do sistema.
- Permitir ao usuário administrar o cadastro de contatos.
- Permitir ao usuário, selecionar o contato e visualizar os telefones do mesmo.
- Permitir ao usuário administrar o cadastro de telefones do contato selecionado.
- Permitir ao usuário cadastrar “n” telefones para o contato selecionado.
Diagrama de Casos de Uso
Diagrama de Classe
Nesse diagrama, apenas documentarei as classes diretamente ligadas ao sistema de Agenda Telefônica, a diagramação das classes que implementei acima não será abordada.
Diagrama de Entidade Relacionamento
Pelo fato da necessidade da persistir os dados de contato e dos telefones, será necessário duas tabelas para guardar os dados das classes. Veja o Diagrama de Entidade Relacionamento (DER) abaixo.
Agora que já tenho a arquitetura da camada de negócio já definida, o proximo passo é implementar as classes responsáveis 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.
<?php //incluindo o arquivo contendo a classe TelefoneModel require_once 'models/TelefoneModel.php'; /** * Responsável por gerenciar e persistir os dados dos * Contatos da Agenda Telefônica * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Modelo ou Model. * Diretório Pai - models * Arquivo - ContatoModel.php **/ class ContatoModel extends PersistModelAbstract { private $in_id; private $st_nome; private $st_email; function __construct() { parent::__construct(); //executa método de criação 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() { return $this->st_email; } /** * Retorna um array contendo os contatos * @param string $st_nome * @return Array */ public function _list( $st_nome = null ) { if(!is_null($st_nome)) $st_query = "SELECT * FROM tbl_contato WHERE con_st_nome LIKE '%$st_nome%';"; else $st_query = 'SELECT * FROM tbl_contato;'; $v_contatos = array(); try { $o_data = $this->o_db->query($st_query); while($o_ret = $o_data->fetchObject()) { $o_contato = new ContatoModel(); $o_contato->setId($o_ret->con_in_id); $o_contato->setNome($o_ret->con_st_nome); $o_contato->setEmail($o_ret->con_st_email); array_push($v_contatos, $o_contato); } } catch(PDOException $e) {} return $v_contatos; } /** * Retorna os dados de um contato referente * a um determinado Id * @param integer $in_id * @return ContatoModel */ public function loadById( $in_id ) { $v_contatos = array(); $st_query = "SELECT * FROM tbl_contato WHERE con_in_id = $in_id;"; $o_data = $this->o_db->query($st_query); $o_ret = $o_data->fetchObject(); $this->setId($o_ret->con_in_id); $this->setNome($o_ret->con_st_nome); $this->setEmail($o_ret->con_st_email); return $this; } /** * Salva dados contidos na instancia da classe * na tabela de contato. Se o ID for passado, * um UPDATE será executado, caso contrário, um * INSERT será executado * @throws PDOException * @return integer */ public function save() { if(is_null($this->in_id)) $st_query = "INSERT INTO tbl_contato ( con_st_nome, con_st_email ) VALUES ( '$this->st_nome', '$this->st_email' );"; else $st_query = "UPDATE tbl_contato SET con_st_nome = '$this->st_nome', con_st_email = '$this->st_email' WHERE con_in_id = $this->in_id"; try { if($this->o_db->exec($st_query) > 0) if(is_null($this->in_id)) { /* * verificando se o driver usado é sqlite e pegando o ultimo id inserido * por algum motivo, a função nativa do PDO::lastInsertId() não funciona com sqlite */ if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') { $o_ret = $this->o_db->query('SELECT last_insert_rowid() AS con_in_id')->fetchObject(); return $o_ret->con_in_id; } else return $this->o_db->lastInsertId(); } else return $this->in_id; } catch (PDOException $e) { throw $e; } return false; } /** * Deleta os dados persistidos na tabela de * contato usando como referencia, o id da classe. */ public function delete() { if(!is_null($this->in_id)) { $st_query = "DELETE FROM tbl_contato WHERE con_in_id = $this->in_id"; if($this->o_db->exec($st_query) > 0) return true; } return false; } /** * Cria tabela para armazernar os dados de contato, caso * ela ainda não exista. * @throws PDOException */ private function createTableContato() { /* * No caso do Sqlite, o AUTO_INCREMENT é automático na chave primaria da tabela * No caso do MySQL, o AUTO_INCREMENT deve ser especificado na criação do campo */ if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') $st_auto_increment = ''; else $st_auto_increment = 'AUTO_INCREMENT'; $st_query = "CREATE TABLE IF NOT EXISTS tbl_contato ( con_in_id INTEGER NOT NULL $st_auto_increment, con_st_nome CHAR(200), con_st_email CHAR(100), PRIMARY KEY(con_in_id) )"; //executando a query; try { $this->o_db->exec($st_query); } catch(PDOException $e) { throw $e; } } } ?>
Agora é a vez da classe TelefoneModel ser implementada.
<?php /** * Responsável 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 * Diretório 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 método de criação da tabela de Telefone $this->createTableTelefone(); } /** * 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(); try { $o_data = $this->o_db->query($st_query); while($o_ret = $o_data->fetchObject()) { $o_telefone = new TelefoneModel(); $o_telefone->setId($o_ret->tel_in_id); $o_telefone->setDDD($o_ret->tel_in_ddd); $o_telefone->setTelefone($o_ret->tel_in_telefone); $o_telefone->setContatoId($o_ret->con_in_id); array_push($v_telefones,$o_telefone); } } catch(PDOException $e) {} return $v_telefones; } /** * Retorna os dados de um telefone referente * a um determinado Id * @param integer $in_id * @return TelefoneModel */ public function loadById( $in_id ) { $v_contatos = array(); $st_query = "SELECT * FROM tbl_telefone WHERE tel_in_id = $in_id;"; try { $o_data = $this->o_db->query($st_query); $o_ret = $o_data->fetchObject(); $this->setId($o_ret->tel_in_id); $this->setDDD($o_ret->tel_in_ddd); $this->setTelefone($o_ret->tel_in_telefone); $this->setContatoId($o_ret->con_in_id); return $this; } catch(PDOException $e) {} return false; } /** * Salva dados contidos na instancia da classe * na tabela de telefone. Se o ID for passado, * um UPDATE será executado, caso contrário, um * INSERT será executado * @throws PDOException * @return integer */ public function save() { if(is_null($this->in_id)) $st_query = "INSERT INTO tbl_telefone ( con_in_id, tel_in_ddd, tel_in_telefone ) VALUES ( $this->in_contato_id, '$this->in_ddd', '$this->in_telefone' );"; else $st_query = "UPDATE tbl_telefone SET tel_in_ddd = '$this->in_ddd', tel_in_telefone = '$this->in_telefone' WHERE tel_in_id = $this->in_id"; try { if($this->o_db->exec($st_query) > 0) if(is_null($this->in_id)) { /* * verificando se o driver usado é sqlite e pegando o ultimo id inserido * por algum motivo, a função nativa do PDO::lastInsertId() não funciona com sqlite */ if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') { $o_ret = $this->o_db->query('SELECT last_insert_rowid() AS tel_in_id')->fetchObject(); return $o_ret->tel_in_id; } else return $this->o_db->lastInsertId(); } else return $this->in_id; } catch (PDOException $e) { throw $e; } return false; } /** * Deleta os dados persistidos na tabela de * telefone usando como referencia, o id da classe. */ public function delete() { if(!is_null($this->in_id)) { $st_query = "DELETE FROM tbl_telefone WHERE tel_in_id = $this->in_id"; if($this->o_db->exec($st_query) > 0) return true; } return false; } /** * Cria tabela para armazernar os dados de telefone, caso * ela ainda não exista. * @throws PDOException */ private function createTableTelefone() { /* * No caso do Sqlite, o AUTO_INCREMENT é automático na chave primaria da tabela * No caso do MySQL, o AUTO_INCREMENT deve ser especificado na criação do campo */ if($this->o_db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite') $st_auto_increment = ''; else $st_auto_increment = 'AUTO_INCREMENT'; $st_query = "CREATE TABLE IF NOT EXISTS tbl_telefone ( tel_in_id INTEGER NOT NULL $st_auto_increment, con_in_id INTEGER NOT NULL, tel_in_ddd CHAR(5), tel_in_telefone CHAR(12), PRIMARY KEY(tel_in_id) )"; //executando a query; try { $this->o_db->exec($st_query); } catch(PDOException $e) { throw $e; } } } ?>
Depois das classes da camada de modelo implementadas, o próximo passo é escrever o código das classes de controle referente ao fluxo de gerenciamento das classes acima. Elas também serão duas, ContatoController e TelefoneController.
<?php //incluindo classes da camada Model require_once 'models/ContatoModel.php'; /** * Responsável por gerenciar o fluxo de dados entre * a camada de modelo e a de visualização * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Controladores ou Controllers * Diretório Pai - controllers * Arquivo - ContatoController.php * */ class ContatoController { /** * Efetua a manipulação dos modelos necessários * para a aprensentação da lista de contatos */ public function listarContatoAction() { $o_Contato = new ContatoModel(); //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 código HTML $o_view->showContents(); } /** * Gerencia a requisiçães de criação * e edição 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=listarContato'); } $o_view = new View('views/manterContato.phtml'); $o_view->setParams(array('o_contato' => $o_contato)); $o_view->showContents(); } /** * Gerencia a requisições de exclusão dos contatos */ public function apagarContatoAction() { if( DataValidator::isNumeric($_GET['in_con']) ) { //apagando o contato $o_contato = new ContatoModel(); $o_contato->loadById($_GET['in_con']); $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=listarContato'); } } } ?>
e por sua vez a classe TelefoneController
<?php //incluindo classes da camada Model require_once 'models/TelefoneModel.php'; require_once 'models/ContatoModel.php'; /** * Responsável por gerenciar o fluxo de dados entre * a camada de modelo e a de visualização * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Camada - Controladores ou Controllers * Diretório Pai - controllers * Arquivo - TelefoneController.php */ class TelefoneController { /** * Efetua a manipulação dos modelos necessários * para a aprensentação da lista de telefones do contato */ public function listarTelefonesAction() { if( isset($_REQUEST['in_con']) ) if( DataValidator::isNumeric($_REQUEST['in_con']) ) { $o_contato = new ContatoModel(); $o_contato->loadById($_REQUEST['in_con']); $o_telefone = new TelefoneModel(); $v_telefones = $o_telefone->_list($_GET['in_con']); $o_view = new View('views/listarTelefones.phtml'); $o_view->setParams(array('o_contato' => $o_contato,'v_telefones' => $v_telefones)); $o_view->showContents(); } } /** * Gerencia a requisiçães de criação * e edição dos telefones do contato */ public function manterTelefoneAction() { $o_contato = new ContatoModel(); $o_telefone = new TelefoneModel(); if( isset($_REQUEST['in_con']) ) if( DataValidator::isInteger($_REQUEST['in_con']) ) $o_contato->loadById($_REQUEST['in_con']); if( isset($_REQUEST['in_tel']) ) if( DataValidator::isInteger($_REQUEST['in_tel']) ) $o_telefone->loadById($_REQUEST['in_tel']); if(count($_POST) > 0) { $o_telefone->setDDD(DataFilter::numeric($_POST['in_ddd'])); $o_telefone->setTelefone(DataFilter::numeric($_POST['in_telefone'])); $o_telefone->setContatoId($o_contato->getId()); if($o_telefone->save() > 0) Application::redirect('?controle=Telefone&acao=listarTelefones&in_con='.$o_contato->getId()); } $o_view = new View('views/manterTelefone.phtml'); $o_view->setParams(array('o_contato' => $o_contato,'o_telefone' => $o_telefone)); $o_view->showContents(); } /** * Gerencia a requisições de exclusão de telefones do contato */ public function apagarTelefoneAction() { if( isset($_GET['in_tel']) ) if( DataValidator::isInteger($_GET['in_tel'])) { $o_telefone = new TelefoneModel(); $o_telefone->loadById($_GET['in_tel']); $o_telefone->delete(); Application::redirect('?controle=Telefone&acao=listarTelefones&in_con='.$_GET['in_con']); } } } ?>
Arquivos de visualização ou views
As classes implementadas acima estão fazendo referência à arquivos HTML contidos na camada de visualização, para esse programa funcionar, será preciso implementá-los. Isso é o que irei fazer agora.
Arquivo – listarContatos.phtml
Diretório Pai – views
<?php $v_params = $this->getParams(); $v_contatos = $v_params['v_contatos']; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Agenda Telefônica - Exemplo de MVC com PHP </title> <link rel="stylesheet" type="text/css" href="template/css/default.css" /> </head> <body> <div align="center"> <table width="80%" border="1"> <tr> <th> ID </th> <th> Nome </th> <th> E-mail </th> <th colspan="3"> Ações </th> </tr> <?php foreach($v_contatos AS $o_contato) { ?> <tr> <td> <?php echo $o_contato->getId()?> </td> <td> <?php echo $o_contato->getNome()?> </td> <td> <?php echo $o_contato->getEmail()?> </td> <td align="center"> <a href='?controle=Telefone&acao=listarTelefones&in_con=<?php echo $o_contato->getId()?>'>Telefones</a> </td> <td align="center"> <a href='?controle=Contato&acao=manterContato&in_con=<?php echo $o_contato->getId()?>'>Editar</a> </td> <td align="center"> <a href='?controle=Contato&acao=apagarContato&in_con=<?php echo $o_contato->getId()?>'>Apagar</a> </td> </tr> <?php } ?> </table> <br /> <a href='?controle=Contato&acao=manterContato'>Novo Contato</a> </div> </body> </html>
Arquivo – listarTelefones.phtml
Diretório Pai – views
<?php $v_params = $this->getParams(); $o_contato = $v_params['o_contato']; $v_telefones = $v_params['v_telefones']; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Agenda Teleônica - Exemplo de MVC com PHP </title> <link rel="stylesheet" type="text/css" href="template/css/default.css" /> </head> <body> <div align="center"> <table width="80%" border="1"> <tr> <th colspan='3'> Contato </th> </tr> <tr> <th> ID </th> <th> Nome </th> <th> E-mail </th> </tr> <tr> <td> <?php echo $o_contato->getId()?> </td> <td> <?php echo $o_contato->getNome()?> </td> <td> <?php echo $o_contato->getEmail()?> </td> </tr> </table> <br /> <table width="50%" border="1"> <tr> <th colspan="5"> Telefones </th> </tr> <tr> <th> ID </th> <th> DDD </th> <th> Telefone </th> <th colspan="2"> Ações </th> </tr> <?php foreach($v_telefones AS $o_telefone) { ?> <tr> <td align="center"> <?php echo $o_telefone->getId()?> </td> <td align="center"> <?php echo $o_telefone->getDDD()?> </td> <td align="center"> <?php echo $o_telefone->getTelefone()?> </td> <td align="center"> <a href='?controle=Telefone&acao=apagarTelefone&in_con=<?php echo $o_contato->getId()?>&in_tel=<?php echo $o_telefone->getId()?>'>Apagar</a> </td> <td align="center"> <a href='?controle=Telefone&acao=manterTelefone&in_con=<?php echo $o_contato->getId()?>&in_tel=<?php echo $o_telefone->getId()?>'>Editar</a> </td> </tr> <?php } ?> </table> <br /> <a href='?controle=Index'>Voltar</a> <a href='?controle=Telefone&acao=manterTelefone&in_con=<?php echo $o_contato->getId()?>'>Novo Telefone</a> </div> </body> </html>
Arquivo – manterContato.phtml
Diretório Pai – views
<?php $v_params = $this->getParams(); $o_contato = $v_params['o_contato']; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Agenda Telefônica - Exemplo de MVC com PHP </title> <link rel="stylesheet" type="text/css" href="template/css/default.css" /> </head> <body> <div align="center"> <form method='post'> <table width="300" border="1"> <tr> <th> Nome </th> <th> E-mail </th> <th colspan="2"> Ações </th> </tr> <tr> <td> <input type='text' name='st_nome' value='<?php echo $o_contato->getNome()?>'> </td> <td> <input type='text' name='st_email' value='<?php echo $o_contato->getEmail()?>'> </td> <td align="center"> <a href='?controle=Contato&acao=listarContato'>Cancelar</a> </td> <td align="center"> <input type='hidden' name='controle' value='Contato'> <input type='hidden' name='acao' value='manterContato'> <input type='hidden' name='in_con' value='<?php echo $o_contato->getId()?>'> <button type='submit'>Salvar</button> </td> </tr> </table> </form> </div> </body> </html>
Arquivo – manterTelefone.phtml
Diretório Pai – views
<?php $v_params = $this->getParams(); $o_contato = $v_params['o_contato']; $o_telefone = $v_params['o_telefone']; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title> Agenda Telefônica - Exemplo de MVC com PHP </title> <link rel="stylesheet" type="text/css" href="template/css/default.css" /> </head> <body> <div align="center"> <table width="80%" border="1"> <tr> <th colspan='3'> Contato </th> </tr> <tr> <th> ID </th> <th> Nome </th> <th> E-mail </th> </tr> <tr> <td> <?php echo $o_contato->getId()?> </td> <td> <?php echo $o_contato->getNome()?> </td> <td> <?php echo $o_contato->getEmail()?> </td> </tr> </table> <br /> <form method='post'> <table width="40%" border="1"> <tr> <th colspan="4"> Telefones </th> </tr> <tr> <th> DDD </th> <th> Telefone </th> <th> Ações </th> </tr> <tr> <td align="center"> <input type='text' name='in_ddd' value='<?php echo $o_telefone->getDDD()?>' size="5" maxlength="3"> </td> <td align="center"> <input type='text' name='in_telefone' value='<?php echo $o_telefone->getTelefone()?>'> </td> <td align="center"> <input type='hidden' name='controle' value='Telefone'> <input type='hidden' name='acao' value='manterTelefone'> <input type='hidden' name='in_con' value='<?php echo $o_contato->getId()?>'> <input type='hidden' name='in_tel' value='<?php echo $o_telefone->getId()?>'> <button type='submit'>Salvar</button> </td> </tr> </table> </form> <br /> <a href='?controle=Telefone&acao=listarTelefones&in_con=<?php echo $o_contato->getId()?>'>Cancelar</a> </div> </body> </html>
Cada método (Action) da classe de controle (Controller) faz referência à um arquivo HTML (View). Assim se cumpre a profecia de intermédio entre a camada de controle e a camada de visualização. Onde o controle faz consultas à camada de modelo e repassa a resposta para a camada de visualização.
Após o código implementado, para selecionar o Controlador e a Ação que quer executar, basta envia-las via GET ou POST, como por exemplo ?controle=Contato&acao=listarContato. Com esses parâmetros, o método listarContatoAction da classe ContatoController será executado.
Para entender um pouco melhor o funcionamento desse programa, visualize o Diagrama de Sequência da funcionalidade Listar Contatos mostrado na Imagem 6 abaixo. Ele será executado por padrão quando nenhum outro for requisitado pelo usuário.
Quer ver isso funcionando? Acesse o link http://digitaldev.com.br/exemploMVC/
Quer fazer download do código para estudar? Acesse o link http://digitaldev.com.br/downloads/exemploMVC.zip
Conectando a agenda com o MySQL
Caso você queira conectar a aplicação desenvolvida com o MySQL, basta editar o arquivo lib/PersistModelAbstract.php e deixá-lo como abaixo, preenchendo apenas os dados da sua conexão e credenciais referentes.
<?php /** * Classe Abstrata responsável por centralizar a conexão * com o banco de dados * * @package Exemplo simples com MVC * @author DigitalDev * @version 0.1.1 * * Diretório Pai - lib * Arquivo - PersistModelAbstract.php */ abstract class PersistModelAbstract { /** * Variável responsável por guardar dados da conexão do banco * @var resource */ protected $o_db; function __construct() { /* // Inicio de conexão com SQLite $this->o_db = new PDO("sqlite:./databases/db.sq3"); $this->o_db->setAttribute ( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION ); // Fim de conexão com SQLite */ //Inicio de conexão com MySQL $st_host = 'ip ou host'; $st_banco = 'bancodedados'; $st_usuario = 'usuario'; $st_senha = 'senha'; $st_dsn = "mysql:host=$st_host;dbname=$st_banco"; $this->o_db = new PDO ( $st_dsn, $st_usuario, $st_senha ); //Fim de conexão com MySQL } } ?>
Mais sobre MVC com PHP
Há um tempo atrás, encontrei um material em vídeo muito bom sobre como montar um Mini-Framework no padrão MVC com PHP. Para acessar esse videos, clique em Criando um Mini Framework PHP 5 com MVC
Mais sobre Padrões de Projeto
O MVC é um dos Padrões de Projetos mais importantes hoje em dia, mas não está sozinho. Existem vários outros padrões por ai, eles nos ajudam a organizar o código, resolver problemas que costumamos enfrentar no dia-dia da Programação Orientada a Objeto. Sendo assim, vale muito a pena estudar sobre eles, pois um bom programador não é aquele que apenas codifica soluções excepcionais, mas também o que consegue organizar com clareza seu código. Veja uma lista de alguns padrões organizados por suas categorias abaixo, já é um pontapé inicial para se estudar. Espero ter ajudado.
Padrões de criação
Abstract Factory, Builder, Factory Method, Prototype, Singleton
Padrões estruturais
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
Padrões comportamentais
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
76 comentários
1 menção
Pular para o formulário de comentário
Parabéns amigo!! Muito bom
artigo!! nota 1000.
Cara!!!! Muito bom mesmo sua abordagem. Estou criando um aplicativo, porém, o código já está muito bagunçado e de difícil assimilação, estou começando a perder o controle do mesmo.
Observando a forma com que vc lidou com seu exemplo, hummmm, fique mais entusiasmado em refazer o meu apoiado em sua ótima explicação. Fico alegre por vc compartilhar seu conhecimento a favor de outros. Novamente, muito obrigado e até.
Autor
Obrigado Jorge. Se tiver alguma dúvida, entre em contato que eu tentarei ajudar.
Faço das palavras do Jorge Sampaio as minhas. Obrigado por compartilhar tal informação que, muitos dos que estão começando OOP tem muita dificuldade.
Olá… Tarcísio obrigado pelo belo material publicado, parabéns, foi muito esclarecedor e didático.
Nunca trabalhei com phpoo(tirando os web services) nem mvc, mas tenho uma noção. Mas mesmo depois de ler sua publicação e varias outras que já li, não consigo visualizar eu trabalhando com mvc. Talvez por falta de conhecimento, ou falta daquele estalo que da na nossa mente pra cair a fixa. Mesmo conseguindo compreender o que foi proposto. =/.
Estou nessa busca de conseguir entender o suficiente pra mudar minha rotina de trabalho sem que caia a produção. Mas talvez, só usando mesmo pra sacar a ideia.
Teria algo referente a isso pra me dizer/recomendar?
Mais uma vez, parabéns pelo material e obrigado por compartilhar.
o/
Parabéns, empaquei no meu projeto, acredito que o estudo desse código irá me auxiliar!
Como tenho MySql e não SQLlite, não consegui rodar aqui :S
Vou tentar colocar no MySQL
Autor
Boa tarde Cassio, eu revisei o código e alterei o mesmo para possibilitar tanto a conexão com MySQL, quanto com SQLite. Se houver mais alguma dúvida, entre em contato.
Olá Tarcísio,
Tentei refazer o seu exemplo igualzinho mas usando mysql, não me ocorre erro algum, mas ao correr o projecto (http://localhost:8888/mvcExemplo/?controle=Usuario&acao=listarUsuario) o browser fica todo em branco, como se não ocorresse nada. Ainda acredito que o erro está na forma como lidei para tratar os dados com mysql. Pode me ajudar com esta dúvida? Como estaria a minha classe PersistModelAbstract.php usando mysql?
Ok! Já consegui resolver. Obrigado!
Fala Amarildo to tentando usar o MySQL tb e aparece o mxm erro como vc corrigiu ? vlw
Deixa quieto já resolvi tb era besteira uma variável escrita errado.
Achei q tinha resolvido mas continua, minha tela está td branca
Autor
Boa tarde Amarildo, eu revisei o código e alterei o mesmo para possibilitar tanto a conexão com MySQL, quanto com SQLite. Se houver mais alguma dúvida, entre em contato.
Boa tarde Tarcísio!
Acabei de ler o seu artigo e ficou muito bom, parabéns!
Apenas a classe “TelefoneController” esta com o codigo da “ContatoController”.
[]’s
Almir
Autor
Opa Almir, vou verificar os códigos trocados…Obrigado
Autor
Almir, revisei o código e coloquei as classes em seus devidos lugares. Obrigado pela observação. Qualquer outro problema, só falar.
Não estou conseguindo fazer funcionar com o MySQL, na hora de salvar a tela fica td branca e não salva.
Alguem conseguiu fazer funcionar com MySQL ?
Autor
Não ta mostrando mensagem de erro alguma leandro? Você consegue fazer selects ou tá quebrando tudo?
Autor
Boa tarde Leandro.
Revisei e alterei o código, agora ele se conecta tanto com MySQL quanto com SQLite.
Se houver mais alguma dúvida, entre em contato
Cara. Show de bola a sua explicação. A melhor e mais clara que achei na net. Parabéns.
Parabens , otimo post, conseg converte para outro banco de dados de uma forma extremamente simples, show de boa
Para mim aparece o seguinte erro qd eu aponto para o mysql:
Erro no servidor
O site encontrou um erro ao recuperar http://www.polesystem.com.br/mvc/?controle=Telefone&acao=manterTelefone&in_con=0. Ele pode estar em manutenção ou configurado incorretamente.
Veja algumas sugestões:
Recarregue esta página da web mais tarde.
Erro HTTP {500 (Internal Server Error): Ocorreu uma condição inesperada enquanto o servidor tentava completar a solicitação.
Pode acessar aí o link para ver tb:
http://www.polesystem.com.br/mvc
o que eu mudei do arquivos foi somente a function no arquivo PersistModelAbstract.php para :
function __construct()
{
$mysql = “host”;
$username = “user”;
$passwd = “senha”;
$sqlite = “sqlite:./databases/db.sq3″;
try{
//Conectando ao banco de dados
//$this->o_db = new PDO($sqlite);
//$this->o_db->setAttribute ( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION );
$this->o_db = new PDO($mysql, $username, $passwd) or print (mysql_error());
$this->o_db->setAttribute ( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION );
} catch (PDOException $e) {
echo $e->getMessage();
}
}
e mudei tb no arquivo Application.php a function loadRoute() onde estava ” index ” para ” Index ” pq não abria a pagina principal.
Ola, bom artigo,muito esclarecedor. Sou novado em OO e baixei os arquivos e na hora de executar, quando rodo a pagina index.php estou com o seguinte erro:
Uncaught exception ‘Exception’ with message ‘Arquivo controllers/indexController.php nao encontrado’ in /home/u810533697/public_html/mvc/lib/Application.php:75 Stack trace: #0 /home/u810533697/public_html/mvc/index.php(7): Application->dispatch() #1 {main} thrown in /home/u810533697/public_html/mvc/lib/Application.php on line 75
Alguém aqui teve esse problema?
Grato
Autor
Edson, o código foi revisado e o bug foi corrigido. Alem disso, adicionei algumas alterações que permitem conectar a aplicação tanto com MySQL quanto com SQLite. Se achar mais alguma coisa errada ai, entre em contato. Obrigado…
Oi, parabéns pelo artigo! Apena uma dúvida… *Model não deveria chamar *DB e a calsse DataBase realizar as operações no banco? Então no *Model eu poderia ter o conceito de transação interessante para algumas aplicações. O que você acha?
Autor
Bom dia Zegildo.
Tentei usar classes mais simples para demonstrar o funcionamento, evitando assim a divisão da Camada Model em mais Subcamadas. Mas, conforme a experiência do programador, é válido a divisão da camada de Model em várias outras. Eu geralmente costumo criar classes com sufixo DAO (Data Access Object), onde as mesmas são responsáveis pela persistência dos dados das classes com sufixo Entity (Entidades como contato, telefone, endereço).
Tarcísio, primeiramente, parabéns pelo código.
Estou tentando rodar esse código, mas estou tendo o seguinte erro:
Fatal error: Class ‘PersistModelAbstract’ not found in D:\wamp\www\maisumteste\models\TelefoneModel.php on line 15
O fato é que não consegue encontrar a classe ‘PersistModelAbstract’, porém ela existe. A única alteração realizada foi fazer a conexão com mysql:
abstract class PersistModelAbstract {
protected $o_db;
function __construct() {
try{
$this->o_db = new PDO(‘mysql:host=localhost;dbname=testemvc’, ‘root’, ”, array(PDO::ATTR_PERSISTENT => true)) or print (mysql_error());
$this->o_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch (PDOException $e) {
echo $e->getMessage();
}
}
}
Poderia me dar uma dica do que está acontecendo? O uso da classe PDO está correto?
Obrigado.
Autor
Vou testar o código com o mysql e te dou retorno.
Autor
Sergio, eu retestei e revisei o código para corrigir alguns pequenos erros. Esse erro relatado por você foi corrigido. Obrigado.
Boa noite Tarcísio, fico no aguardo do código repetido. (TelefoneController)… Tentei fazer mas não consegui muito sucesso. Abraços e valeu pelo grande post!
Autor
Roberto, revisei o código e alterei o post. Agora a classe está no seu devido lugar.
Se achar mais alguma inconsistência, entre em contato.
Gostei muito da estrutura MVC que você desenvolveu. 🙂
Tem algo em mente pra colocar .htaccess nisso pra deixar com query string e url amigável também?
Esse método de jogar instruções SQL também não gostei, mas não sei uma forma de usar meta queries como o CakePHP faz. Alguma idéia?
Autor
Esse código é um exemplo focado em MVC…apenas para que programadores iniciantes entendam como funciona a estrutura. Não é um projeto de um framework ou algo do tipo. O SQL no meio do Model é apenas para diminuir o número de classes e divisões da camada de Modelo, mantendo assim, o foco no entendimento do MVC. Por esse motivo, não implementei url amigável.
Tarcísio programadores iniciantes?
Essa foi boa hahaha.
Sua estrutura está até melhor que a de muitos frameworks de ponta que diversas comunidades os usam.
Precisamos criar um parse no html como templates, para evitar usar tanto php junto com o html, criar rotas de url e deixando as urls amigávels ou não, como o wordpress e joomla tem, acesso a url com query string ou com modo rewrite tudo ativo.
Estou interessado em continuar essa estrutura em projeto, quem sabe não criamos uma estrutura em que muitos poderão seguir. 😉
Se estiver interessado, entre em contato.
Abraço.
Autor
Quando digo Iniciantes, quero dizer que tentei deixar a explicação mais clara possível, sem ir muito alem nas estruturas. Porque se eu deixasse o modelo completo, do jeito que eu trabalho, ficaria mais difícil pro pessoal entender.
Entendi.
Mas podemos subir isso num repositório como projeto.
Considerando depois como uma arquitetura MVC open de sua autoria. 😀
Tarcísio show de bola seu post!! parabéns mesmo!!!
a questão foi que tive a mesma ideia do Francis Rodrigues
e dei uma fuçada no seu código!!!
adicionei o .htaccess pra gerenciar as url amigável, e algumas outras mudanças pra isso funcionar!!!
se o Francis Rodrigues ainda nao fez essas mudanças
segue o link para download
https://copy.com/OBL46UmwxyIC
Fala Tarcísio, tudo bem? Muito bom esse material, tudo bem comentado, ótimo para mim que sou iniciante em MVC.
Tem um erro aqui:
Fatal error: Class ‘PersistModelAbstract’ not found in /home/ubuntu/projeto/models/TelefoneModel.php on line 16
Autor
Vou verificar
É que faltou incluir require_once(‘lib/PersistModelAbstract.php’);
e View.php também.
Vai dar um monte de erro seguindo o exemplo.
Vou ver se no código do download está tudo normal. 😉
Autor
Francis, o post foi corrigido…
Foi adicionado uma função __autoload no arquivo Application.php, com ela todas as classes da pasta lib serão automaticamente carregadas, inclusive essa em questão.
Obrigado pela observação.
Tarciso, foste extremamente fundamental para a mimha aprendizagem… abraços!
Autor
Obrigado JKC, estou a disposição.
Parabéns pelo artigo, está perfeito!
Curti muito sua explicação!
Tarcisio
Cara sou iniciante e estou com uma duvida pois estou utilizando o seu projeto para estudo contudo não estou conseguindo passar os valores dos campos e resgatalos no lado do server dá um erro dizendo que não existe, estou utilizando $POST[‘xxx’] teria como me dar uma força ou me explicar como realmente funciona o set e get params?? obrigado e realmente muito bom o projeto
Gostei muito do artigo!!! gostaria de saber como implementar autenticação usando esse modelo mvc?
Muito bom mesmo, parabéns!
São de posts assim que muitos programadores PHP estruturais no Brasil precisam para
crescer nos padrões de projetos atuais. Muitos têm medo, mas com este post vemos que não é bicho de sete cabeças
Didaticamente seu post está ótimo, se alguém criticar ou disser que esta faltando ajustes é porque está em outro nível de conhecimento
Este link vai para meu blog
Muito bom o tutorial, o padrão de projeto GRASP MVC está bem definido no php, facilita muito a vida programar orientado a objetos, principalmente web, muitos programadores realmente precisam aprender com soluções simples e práticas, parabens.
muito bom o seu artigo.
Muito bom mesmo cara, parabéns. Vou iniciar um novo projeto e vou basear em MVC. Achei sua explicação muito boa.
Opa, muito bom, isso é quase um framework a parte. Estou usando num projeto. E se eu quiser passar parâmetro para uma view redirecionada por exemplo: header(‘Location: ?controle=Contato&acao=listarContato’); Se eu precisar passar parametros para o listarContato, como procedo?
Muito obrigado por compartilhar seu conhecimento. O tutorial é um dos melhores e mais didáticos que eu já li.
Olá e em que camada coloco as exceções personalizadas que criar?
Autor
Boa tarde Rafael.
As exceções personalizadas podem ficar em qualquer camada. Por exemplo, se você quiser lançar uma exceção por um erro de banco de dados, provavelmente isso ficará na camada de modelo, já se você quiser lançar uma exceção porque o cara chamou um comando / método que não existe, essa ficaria na camada de controle. O que você tem que fazer é tratar as exceções camada a camada, ou seja, se você chamar a camada de modelo dentro da camada de controle e a mesma lançar uma exceção personalizada, o certo é tratar com try{}catch( Exception ){} para ignorar a mesma ou até mesmo mostrar uma mensagem de erro ao usuário.
Obrigado pela atenção, outra questão, eu coloco os arquivos/classes de exceção na pasta da camada ou dentro da camada eu coloco uma pasta de exception própria
tipo
models/exception/ClasseException.php
ou
models/ClasseExceptions.php
Qual o mais “correto”?
Olá Tarcísio, tudo bom???
Seguinte, comecei a pouco numa nova empresa e o pessoal daqui criou nosso sistema inteiramente baseado neste seu exemplo… Porem, ninguém da equipe tinha muita experiência com o modelo MVC… Então alguns problemas surgiram…
O maior deles para mim agora esta no layout… Todos os demais membros da equipe implementaram seus HTML/CSS em cada uma das views de suas telas… Porem isso é insano!!! Quando precisarmos, e vamos precisar, dar manutenção no layout teremos um ENORME problema!!!
Eu ainda sou novo no MVC tbm… Mas já trabalhei em outra empresa com um já montado.
Enfim, minha dúvida é quanto a criação de uma view master com o layout padrão do site. Para que as demais views tenham APENAS o conteúdo interno de cada aplicação.
Será q vc poderia me dar uma luz em como procedo nisto?? Nem que seja me indicar algum outro site com alguma luz sobre o assunto!
Desde já agradeço pela força! Seu MVC foi fundamental para nós aqui! 😉
Grande abraço
Danilo
Olha é muito simples divida sua view em três arquivos config.php (define constantes com as pastas usadas), header.php( cabeçalho), footer.php(rodapé) então na view independente só chame o conteúdo da view incluindo sempre o config, header, o conteúdo da view atual e footer se necessários.. estou fazendo assim…
Hmmm,
Eu já estou fazendo assim nas minhas aplicações e alguns outros membros tbm fizeram isso. Entretanto eles ainda fizeram arquivos (topo, rodape, etc) proprios. Mas com a mesma cara…
Mas isso não é o ideal. Eu queria mesmo era criar um template master e aplicar as views dentro dele… Invertendo essa logica. Ou seja, ao inves deu colocar o include dos arquivos em cada view, eu incluiria cada view num template.
Mas enfim, eu ja convenci o pessoal a migrar para um framework MCV mais completo. Pois creio que teremos mais desafios la pra frente…
Abs
Olá Tarcísio, tudo bem? Legal sua iniciativa!
Mas lembrando que MVC não é um padrão de projeto e sim um padrão de arquitetura de software.
Uma outra dica legal é generalizar os diagramas de casos de uso e de classes para que estes fiquem mais compreensíveis, isto é, ao invés de colocar cada etapa do CRUD (criar, consultar, alterar e excluir), colocar apenas uma que identifique todas (por exemplo, caso de uso “Manter Contato”, método “manterContato()”).
Parabéns pelo artigo,
sucesso e
abraços!
Autor
Bruno, o MVC é apresentado em diversas bibliografias como um Padrão de Projeto. Sobre generalizar os diagramas, fiz assim exatamente para que as pessoas que estão lendo o texto não se confundirem, é um texto para iniciante em desenvolvimento. Obrigado pelo seu feedback.
Para este modelo rodar diretamente no mysql, quais as mudanças necessárias. Um ótima explanação do assunto.
Erro que esta acontecendo. Ao colocar o MYSQL.
Coloquei um usuário e ip fictício.
Fatal error: Uncaught exception ‘PDOException’ with message ‘SQLSTATE[28000] [1045] Access denied for user ‘root’@’1.1.1.1′ (using password: YES)’ in /var/www/amt/agenda/lib/PersistModelAbstract.php:50 Stack trace: #0 /var/www/amt/agenda/lib/PersistModelAbstract.php(50): PDO->__construct(‘mysql:host=1.1…’, ‘root’, ‘######’) #1 /var/www/amt/agenda/models/ContatoModel.php(25): PersistModelAbstract->__construct() #2 /var/www/amt/agenda/controllers/ContatoController.php(26): ContatoModel->__construct() #3 /var/www/amt/agenda/lib/Application.php(89): ContatoController->listarContatoAction() #4 /var/www/amt/agenda/index.php(22): Application->dispatch() #5 {main} thrown in /var/www/amt/agenda/lib/PersistModelAbstract.php on line 50
o banco pode ser criando em um mysql normal
Ótimo artigo!
Olá Tarcisio, eu tenho uma duvida. Como uso url amigável nesse exemplo?
Estou me sentindo uma ameba, não entendi nada. rsrs.
Tem um exemplo mais simples?
Autor
Oi Elias. Talvez o ideal seja você se aprofundar um pouco mais em PHP e Orientação a Objetos. Esse exemplo é bem simples no que se diz respeito a MVC, com desenhos e tudo mais. Será que o que você está buscando é realmente exemplos de MVC ou tá querendo apenas se aprofundar em PHP?
muito bom mesmo seu artigo. vou usar o modelo para desenvolver o meu.
CARACA!!! Era tudo oq eu precisava para aprender mvc! Obrigado por compartilhar esse material tao interessante. Funcionou perfeitamente no meu localhost com mysql, bastou alterar o arquivo indicado.
Amigo so tenho uma duvida como faço relações de N:N com as models para buscar um id um exemplo é uma table de categoria que tem um id que faz referencia a uma outra tabela ex subcategoria.
Bem explicativo o artigo, vc tem alguma recomendação de um livro que trata sobre MVC ?
Exemplo espetacular. Tinha algumas dúvidas quanto a implementação de MVC no PHP. Acostumado com Java ainda estou batendo cabeça no PHP mas essa AULA foi perfeita! Obrigado
Ótimo artigo
Só tenho uma duvida. Se caso queira executar um metodo como por exemplo saida do campo input como devo proceder por esse padrao? Como fazer a chamada lá no controller? Já que o padrao é executar atraves da URL?
[…] Exemplo de MVC com PHP » […]