MVC na prática – PHP sem framework - parte 3 - Blog Erik Figueiredo - Dicas para desenvolvedores
MVC é um padrão de arquitetura, isso quer dizer que ele foca em resolver problemas de… bem… arquitetura. Os architectural patterns (padrão de arquitetura em inglês) fornecem soluções para problemas conhecidos durante a etapa de arquitetura do software, em outras palavras, eles fornecem um padrão de trabalho (ou regras) que ajuda na hora de organizar (o termo correto seria arquitetar) seu projeto.
Um padrão de arquitetura é uma solução geral e reutilizável para um problema que ocorre com frequência em arquitetura de software dentro de um determinado contexto
https://pt.wikipedia.org/wiki/Padr%C3%A3o_de_arquitetura
O MVC, por sua vez, tem foco em separar a camada lógica (dados) da camada de exibição (renderização), note que "camada lógica" é diferente de lógica de programação ou de regras de negócio, afinal você ainda vai precisar de lógica de programação na camada de renderização (ou em qualquer lugar que o código exista).
Um dos focos (se não o principal) do MVC no PHP é eliminar aqueles projetos em que se vê SQL no meio do HTML.
Gostaria de lembrar que você não pode se esquecer de se cadastrar na newsletter para acompanhar o lançamento desta série.
MVC é um acrônimo de Model, View e Controller e cada letra corresponde a uma camada.
M de Model
A primeira camada é responsável pela parte lógica do software e isso quer dizer que ela é responsável pelos dados da aplicação e nada além disso.
Na maioria das implementações que vejo o model lida com o banco de dados, mas também poderia ser responsável por armazenar dados vindos de uma API ou de arquivos no disco local.
Claro que eu prefiro a abordagem do banco de dados e "jogar" leitura de API e arquivos para uma camada separada que grava no banco de dados, mas isso não é uma regra (só uma observação minha).
V de View
A segunda camada é responsável pela renderização dos dados e isso não quer dizer que ele vá fornecer apenas HTML, CSS e Javascript, mas JSON, XML, PDF, XLS, DOC ou qualquer formato que vá ser lido por algo (como em uma API, para outras aplicações) ou alguém (pessoas) é de responsabilidade da view.
C de Controller
A terceira camada é responsável por ditar o fluxo entre as duas camadas anteriores, o próprio nome (controlador) já é bem auto-explicativo.
Normalmente o controller é o que fica mais próximo da camada HTTP no MVC (em um contexto web), ele recebe as informações da requisição e decide quando o model vai buscar os dados e quando repassar para a view, que irá renderizar estas informações.
Resumindo o MVC no PHP
Para ficar claro agora para você e quando ser questionado não acabar confuso, lembre-se:
Controller: Responsável pela ordem de execução do M e do V
Não! Mas é claro que não, um projeto não pode viver só de MVC e é aqui começam os problemas.
Muitos devs encaram o MVC como o alpha e o ômega de um projeto, tudo começa nele e tudo termina nele, isso é um erro.
Aonde você faz disparos de email? Aonde você faz a requisição para a API do Facebook para uma publicação que deverá ser postada automaticamente em uma página qualquer? Aonde você dispara dados para o Socket.io para ter um app real-time? Aonde você verifica as permissões do usuário? Aonde você verifica se o usuário já aceitou as novas politícas do site antes de renderizar uma página?
Tudo isso são incognitas que fizeram surgir teorias como as que ditam que o MVC vai morrer, quando a verdade é que nada disso é problema dele, o MVC não vai resolver tudo pra você, ele é a porta de entrada, a ponta do iceberg, só isso, lembre-se de seguir o princípio da responsabilidade única.
Responsabilidade única (Single Responsability Principle – SOLID)
Single Responsability Principle é uma das cinco regras do SOLID que te ajudam a ter um código SÓLIDO e mais fácil de ser reutilizado.
Ele diz que uma classe NÃO DEVE ter mais de uma razão de existência, se uma classe consulta os dados do banco, não deve renderizar nada, disparar email ou nada além de lidar com o banco de dados, simples assim.
Sempre se pergunte, pra que serve essa classe. Se tiver mais de uma resposta (ou uma separada por "e") então sua classe está errada.
E para fechar, algumas afirmações que estão erradas sobre MVC e PHP e o porque estão erradas.
Não podemos ter estruturas de controle (if e switch, por exemplo) nas views e controllers, isso porque existe uma confusão com lógica de programação, regras de negócio e lógica de dados. São coisas diferentes!
Validação deve ficar em uma camada separada. Existem possibilidades e diferentes tipos de visões, não quer dizer que uma proposta é melhor ou a única verdadeira, pensar que você achou a ferramenta definitiva é que é ruim. Conheça as ferramentas para saber quando escolher.
E a prática do PHP + MVC?
Este artigo é o terceiro de uma série e vou recomendar que você leia o artigo anterior (link a seguir), nele eu falo como criar o sistema de URLs Amigáveis que vamos usar aqui, mas não é obrigatório, apenas estou recomendando.
URLs amigáveis com PHP e OO – PHP sem framework – parte 2
Se você optou por NÃO LER o artigo, aqui os arquivos para você tomar como base:
https://github.com/erikfig/php-do-zero/tree/63ff2631ad1a5ec5ceb503e805df102b7e3d09b9
O sistema de rotas que construímos no artigo acima trabalha armazenando as possíveis URLs que o usuário pode acessar no sistema e retorna algum valor para fazermos algo com ele, no exemplo do artigo eram funções anônimas que podiamos executar, vamos alterar para que possamos adicionar QUALQUER tipo de dado (neste exemplo eu quero poder retornar nomes de classes).
No arquivo src/Router.php remova a tipagem callable dos métodos.
Tipagem define o tipo de dado que poderemos usar, por exemplo, podemos informar string para qu somente textos possam ser usados, um exemplo:
function (string $soPodeStringAqui) {
echo $soPodeStringAqui;
O resultado final da classe Router.
public function __construct($method, $path)
// aqui tinhaum callable, antes do $action
// remova dos outros métodos também
public function get(string $route, $action)
$this->add('GET', $route, $action);
public function post(string $route, $action)
$this->add('POST', $route, $action);
public function add(string $method, string $route, $action)
$this->routes[$method][$route] = $action;
if (empty($this->routes[$this->method])) {
if (isset($this->routes[$this->method][$this->path])) {
return $this->routes[$this->method][$this->path];
foreach ($this->routes[$this->method] as $route => $action) {
$result = $this->checkUrl($route, $this->path);
private function checkUrl(string $route, $path)
preg_match_all('/\{([^\}]*)\}/', $route, $variables);
$regex = str_replace('/', '\/', $route);
foreach ($variables[0] as $k => $variable) {
$replacement = '([a-zA-Z0-9\-\_\ ]+)';
$regex = str_replace($variable, $replacement, $regex);
$regex = preg_replace('/{([a-zA-Z]+)}/', '([a-zA-Z0-9+])', $regex);
$result = preg_match('/^' . $regex . '$/', $path, $params);
Note os comentários no arquivo.
Para usar este sistema de rotas apenas criamos uma instância da classe ErikFig\Framework\Router informando a url acessada e o método HTTP (GET, POST…).
// defino o método http e a url amigável
$path = $_SERVER['PATH_INFO'] ?? '/';
// instancio o Router
$router = new ErikFig\Framework\Router($method, $path);
Em seguida registramos as rotas e o que acontece quando elas forem acessadas:
E por fim executamos:
// encontra a rota
$result = $router->handler();
// executa a função e imprime o resultado
echo $result();
O exemplo imprime um Olá mundo no navegador.
Aqui tem um exemplo completo:
https://github.com/erikfig/php-do-zero/blob/63ff2631ad1a5ec5ceb503e805df102b7e3d09b9/bootstrap.php
Para testar você acessa a url do servidor seguido de index.php/, sendo que / é a URL registrada no segundo parâmetro $router->get('/AQUI', function (), em um servidor corretamente configurado você pode omitir o index.php.
Com isso entendido, vamos configurar o sistema para ler uma classe de controller.
Se não entendeu, por favor, volte no link que indiquei acima, para ver um artigo completo sobre este sistema de rotas.
Vamos pegar o exemplo completo e que eu já citei acima e partir daquele ponto:
Com as alterações que fizemos no Router, eu preciso:
Alterar também o arquivo bootstrap.php que está executando apenas closure.
Acrescentar as novas classes de controller
Como disse acima, eu quero ser capaz de informar classes e métodos de forma prática, de acordo com o exemplo abaixo que substitui as linhas 17 até 19 para:
$router->get('/ola-{nome}', 'App\Controllers\HomeController::hello');
Vamos precisar registrar um novo vendor name de namespace chamado App no Composer, é bem simples, apenas adicione o novo ao composer.json aonde eu marquei com <- aqui:
"App\\": "app", <- aqui
Por favor, não esqueça de remover o <- aqui, e rode composer dump para atualizar.
Crie um diretório app no mesmo nível do composer.json, dentro dele um Controllers e dentro deste último um arquivo chamado HomeController.php com o seguinte conteúdo:
public function hello($params)
return "Olá {$params[1]}";
Agora podemos organizar nossas rotas "por assunto".
Imagine um UsuariosController para incluir, ver, lista, atualizar e remover (CRUD) usuários, fica tudo organizado em um mesmo local.
Agora no bootstrap.php vamos fazer algumas alterações para que essa classe seja executada, eu já disse acima sobre a linha 17 até a 19, se você não fez, faça a edição.
Também altere o final, aonde está:
// imprimo a página atual
echo $result($router->getParams());
// verifico se é uma função anônima
if ($result instanceof Closure) {
// se não for uma função anônima e for uma string
} elseif (is_string($result)) {
// eu quebro a string nos dois-pontos, dois::pontos
// transformando em array
$result = explode('::', $result);
// instancio o controller
$controller = new $result[0];
// guardo o método a ser executado (em um controller ele se chama action)
$action = $result[1];
// finalmente executo o método da classe
echo $controller->$action($router->getParams());
Viu como foi simples a camada de controller.
A camada de model também vai ser simples, na verdade eu vou me aproveitar de um recurso que já existe chamado Eloquent.
Para instalar o Eloquent no projeto rode o seguinte comando na raiz do projeto:
Na sequência, crie um arquivo chamado database.php, eu até poderia fazer isso direto no bootstrap.php, mas aqui vai ficar mais organizado.
Carregue o arquivo database.php no bootstrap.php após a linha require __DIR__ . '/vendor/autoload.php';
Agora adicione o seguinte conteúdo ao novo arquivo:
Pronto, conectado ao banco de dados, não esqueça de alterar host, username e password para acessar o seu banco de dados.
Claro que vamos precisar de um banco de dados, aqui tem um SQL que cria um, adiciona uma tabela e preenche com informações falsas.
CREATE DATABASE `blog_php_sem_framework`;
USE `blog_php_sem_framework`;
INSERT INTO `users` (`name`, `email`, `password`) VALUES
("Erik", "erik@erik.com", "secret"),
("Erik 2", "erik2@erik.com", "12345678"),
("Erik 3", "erik3@erik.com", "654321"),
("Erik 4", "erik4@erik.com", "qwe123");
Agora vamos criar nosso primeiro model.
Dentro de app crie um diretório Models e dentro um arquivo chamado User.php, com o seguinte conteúdo:
Por convenção o Eloquent vai entender que a classe User se refere a tabela users do banco, mas você sempre pode informar o nome da tabela usando a propriedade $table.
protected $table = "nome_da_tabela";
Mas não é o nosso caso, no SQL que eu informi a tabela já se chama users.
Agora vamos criar uma nova action no controller HomeController para listar os usuários:
E vamos registrar uma rota para isso:
$router->get('/users', 'App\Controllers\HomeController::listUsers');
Quando você acessar a url http://endereco-do-servidor/index.php/users vai ver os dados do banco de dados. Bem simples e eficiente.
O Eloquent já converte os dados para JSON quando você imprime eles como string sem a necessidade de uma camada de view. Como eu disse, existem muitas abordagens para o MVC, essa é apenas uma delas.
Se quiser aprender mais sobre o Eloquent a documentação é uma excelente fonte (embora esteja em inglês):
Viu? Nada de reinventar a roda, vamos usar algo que funciona e tem MUITOS recursos disponíveis.
A última camada que vamos trabalhar, agora vamos renderizar HTML utilizando uma biblioteca simples e poderosa, o Twig.
Comece instalando a biblioteca com o comando composer require "twig/twig:^3.0" e criando um arquivo renderer.php na raiz do projeto com o seguinte conteúdo:
// aqui o local em que ficarão os templates no caso,
// um diretório chamado templates, no mesmo nível do renderer.php
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/templates');
// eu comentei a linha sobre o cache, se qusier usar,
// basta criar o diretório cache
// recomendo que use
// 'cache' => __DIR__ . '/cache',
E no bootstrap.php, logo após o bloco que verifica se encontrou algo:
Carregue o arquivo armazenando o valor do return em uma variável:
// a nova linha
$twig = require(__DIR__ . '/renderer.php');
Além disso, como bem observado pelo Renan Medeiros nos comentários, eu esqueci de alterar o bootstrap.php, na linha a seguir:
$controller = new $result[0]($twig);
Desta forma fazemos a injeção do Twig para ser usado no controller.
No HomeController.php, adicione uma variável de instância e um construtor injetando o twig, assim:
E finalmente o Twig está disponível, basta você informar o local do template (no diretório templates que configuramos no renderer.php) e um array com os valores que ficarão disponívels para renderização (a lista de usuários, por exemplo).
Essa ideia de informar quais valores poderão ser usados no template é muito importante, não queremos disponibilizar qualquer coisa e acabar cometendo um erro (de segurança ou outros), por exemplo.
Aqui o novo action listUsers:
Agora crie um arquivo a partir da raiz do projeto, em templates/users/index.html com o seguinte conteúdo:
<h1>Usuários</h1>
O Twig é um poderoso template engine, MUITO completo e simples, se quiser saber mais sobre ele:
Trabalhar com MVC é muito prático e facilita a organização, se engana quem pensa que é complicado, existe uma estrutura e regras a serem seguidas que facilitam MUITO o processo todo, além disso, novas camadas e padrões são SEMPRE bem vindos, desde que somem qualidade ao projeto.
NUNCA ESTEJA SATISFEITO COM SEU CÓDIGO, sempre busque melhorar.
Aqui os arquivos finais deste artigo:
https://github.com/erikfig/php-do-zero/tree/01e9e086ce202eb4007923a8f224af61a8b0f23b
Autor Erik FigueiredoPublicado em 16 de fevereiro de 2020 6 de outubro de 2020 Categorias PHP, PHP sem frameworkTags MVC, MVC na prática, PHP, PHP e MVC, série sem framework
5 comentários em "MVC na prática – PHP sem framework – parte 3"
Renan de Medeiros Porto disse:
15 de março de 2020 às 20:43
Olá Erik!!
Primeiramente agradeço o conteúdo compartilhado!! Estou achando muito legal essa sua série de artigos.
Só uma questão que eu acho que faltou no artigo e eu acabei resolvendo pegando diretamente do seu github:
Faltou informar que na linha 46 do arquivo bootstrap.php tem que alterar "$controller = new $result[0];" para "$controller = new $result[0]($twig);"
16 de março de 2020 às 12:59
Obrigado Renan, adicionei a correção com créditos!
Pingback: URLs amigáveis com PHP e Orientação a Objetos - PHP sem framework - parte 2
Pingback: Modularizando aplicações PHP – PHP sem framework - parte 4 - Blog Erik Figueiredo
Pingback: PHP e MVC - Tudo o que você precisa saber - Blog Erik Figueiredo - Dicas para desenvolvedores
Anteriores Post anterior: Composer: Como criar pacotes PHP
Próximo Próximo post: E-book gratuito do Eric Evans traduzido – DDD – Domain-Driven Design Referência