Exemplo de framework com PSR-0, PSR-1 e PSR-2 - Entendendo o padrão MVC na prática - Parte 01 Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 4 anos, 4 semanas, 1 dia atrás.

Este artigo é muito antigo ou seu conteúdo foi descontinuado, com certeza outro artigo foi escrito para substituí-lo, use o campo de pesquisa logo acima e desculpe o incomodo!

Para os programadores que estão tentando entender agora o padrão MVC no PHP e como estruturar o próprio framework ou projeto com este nível de organização associado as PSRs (e os que já caminham por essas águas a algum tempo e querem palpitar ou quem sabe até aprender alguma coisa), estou iniciando esta série de artigos sobre o assunto, nesta primeira aula vou falar um pouco sobre o MVC e criar um framework que mais vai servir para compreendermos como este padrão funciona, mas nas próximas aulas vamos criar um framework funcional e que pode até vir a crescer em recursos e funcionalidades.

Para quem gosta de usar seus próprios códigos este artigo vai ser muito útil.

Para ver todos os links desta série clique aqui.

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

Seria interessante também que já tivessem lido

O que é MVC e como ele funciona no PHP?

O MVC é um modelo de arquitetura de software que visa separar a aplicação em 3 partes distintas e responsáveis por diferentes tarefas, ele foi citado pela primeira vez em 1979 por Trygve Reenskaug, e suas 3 camadas são Model, Controller e View, ou em bom português brasileiro: Modelo, Controlador e Visão. O artigo original sobre MVC foi disponibilizado por Steve Burbeck em http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html.

O que é Model?

Segundo a Wikipedia:

Um modelo (model) notifica suas visões e controladores associados quando há uma mudança em seu estado. Esta notificação permite que as visões produzam saídas atualizadas e que os controladores alterem o conjunto de comandos disponíveis. Uma implementação passiva do MVC monta estas notificações, devido a aplicação não necessitar delas ou a plataforma de software não suportá-las. Wikipedia

O Model nada mais é do que a camada que recebe, trata e valida os dados da sua aplicação. Os dados aqui citados refe
re-se não somente a texto puro, mas todo o tipo de informação, como arquivo enviados, por exemplo.

Ele não exibe nada, ele não decide nada, apenas pega os dados, faz todo o tratamento necessário para que o controller decida o que fazer com ele.

Um bom exemplo aqui são as requisições ao banco de dados e o tratamento e salvamento de upload de imagens.

O que é Controller?

Um controlador (controller) pode enviar comandos para sua visão associada para alterar a apresentação da visão do modelo (por exemplo, percorrendo um documento). Ele também pode enviar comandos para o modelo para atualizar o estado do modelo (por exemplo, editando um documento). Wikipedia

O Controller é o cara que toma as decisões, ele decide o que vai ser requisitado do Model e o que vai para a view em determinado momento ou ao executarmos determinada ação (e aqui você entende o action do Controller no CakePHP, Zend e outros Frameworks).

E aqui alguns exemplos: O que acontece quando enviarmos um POST? Um arquivo foi enviado, o que fazer? Ignorar a validação? O Model deve checar se é uma imagem? A View tem que mostrar alguma mensagem de sucesso ou erro? E aqueles produtos da loja virtual, quero pegar 5 no Model e mostrar na View...

O que é View?

A visão (view) solicita do modelo a informação que ela necessita para gerar uma representação de saída. Wikipedia

Depois de requisitada, tratada e validada pelo Model e solicitada pelo Controller, a informação é enviada para View que cuida de exibir a informação corretamente, embora parece simples na teoria a view pode vir recheada de recursos, por exemplo, imprimir páginas em diferentes formatos (html, pdf, xml, json, xls, doc...), montar automaticamente a estrutura para Framworks Css como o Twitter Bootstrap 2.3.2 e 3, facilitar o uso do Jquery e Javascript Puro, criar Css dinamicamente e muito mais.

O que é Roteamento (Router)

O MVC original não foi escrito para a internet e aqui entra o Roteamento que cuida de analisar a URL e dizer o que deve ser usado. Ou seja, o router é a camada que define o MVC a se usar, em uma Loja Virtual podemos ter uma seção de produtos e outra de clientes, então, baseado na URL o Router carrega o MVC a se usar, por exemplo:

Na URL www.erikfigueiredo.com.br/produtos/ver/42 posso estar chamando o Model Produtos, o Controller Produtos e a View Ver, O controller vai pegar o 42, descobrir que ele é o id no banco de dados e carregar as informações do Model e enviar para a View.

Já a URL www.erikfigueiredo.com.br/clientes/telefone/15 vai fazer o mesmo, mas requisitando o Model Clientes, Action Clientes e a View Telefone.

E na prática, como funciona o MVC?

Antes de começar a trabalhar precisamos analisar como será nossa lógica de raciocínio, aqui vou definir que o Controller será chamado e em seguida ele irá carregar o Model e View, como vou trabalhar apenas com um MVC nesta primeira aula (depois vamos fazer algo mais elaborado) vou ignorar o Router:

  1. Carregamos o Controller
  2. O Model é chamado pelo Controller
  3. Os dados são requisitados ao Model pela View que irá imprimir na tela

Simples não é? Vamos por a mão na massa, primeiramente definindo no nosso arquivo uma codificação de caracter (UTF-8), HEADER (HTML) e também declarar duas constantes.

Abra uma nova pasta com o nome que quiser e dentro mais duas chamadas framework-1 e framework-2, na framework-1 vamos criar um diretório chamado v0.1.0 e dentro um arquivo index.php, tome o cuidado de usar UTF-8 sem BOM, falamos sobre isso aqui e vamos aos códigos.

No index.php adicione:

<?php
header('Content-Type: text/html; charset=utf-8');

define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__FILE__));

As constantes definidas já nos dão um separador de diretório compatível com qualquer sistema operacional (DS) e o caminho local completo até o index.php, em seguida vamos criar mais uma estrutura de diretórios:

  • Library
    • Erik
      • Core

Porque tudo isso? Tenho duas respostas para nossa questão:

  1. Organização, e essa resposta já é mais que suficiente.
  2. A PSR-0 diz que devemos ter um nome Vendor, ou seja, um dono para estes arquivos, por isso a pasta Erik, a pasta Core guarda os arquivos principais do Framework, imagine que você queira ulititários para sua aplicação, então teria outra pasta conseguindo assim organizar pacotes de scripts e a pasta Library que guarda os arquivos para nós.

Então dentro da pasta Core você vai criar um arquivo chamado AutoLoad.php com o seguinte código:

<?php

class AutoLoad
{

    function core($className)
    {
        $className = ltrim($className, '\');
        $fileName  = '';
        $namespace = '';
        if ($lastNsPos = strrpos($className, '\')) {
            $namespace = substr($className, 0, $lastNsPos);
            $className = substr($className, $lastNsPos + 1);
            $fileName  = str_replace('\', DS, $namespace) . DS;
        }
        $fileName .= str_replace('_', DS, $className) . '.php';

        require ROOT.DS.'Library'.DS.$fileName;

    }

}

Para quem conhece a PSR-0 já sabe que essa classe é um autoloader que vai se encarregar de incluir automaticamente os demais arquivos da nossa aplicação, volte no index.php e chame o AutoLoad.php nele, seu arquivo vai ficar assim:

<?php
header('Content-Type: text/html; charset=utf-8');

define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__FILE__));

require 'library'.DS.'Erik'.DS.'core'.DS.'AutoLoad.php';

$AutoLoad = new AutoLoad();

spl_autoload_register(array($AutoLoad, 'core'));

Incluímos o nosso AutoLoad com require, instanciamos a classe e registramos o autoloader com spl.

Note que temos 2 arquivos o index.php e o AutoLoad.php, e que já estamos seguindo diversas regras PSR, como por exemplo a PSR-1 que diz para usarmos arquivos com codificação UTF-8 sem BOM, para usarmos nomes de Classes com StudlyCaps e funções com lowerCamelCase, também separamos os arquivos, o index.php causa efeitos secundários e o AutoLoad.php declara símbolos... você seria capaz de identificar quais outras regras estamos usando? Encare como execício.

Agora vamos criar nosso MVC propriamente dito, para tal crie três arquivos dentro de Core:

  • Controller.php
  • Model.php
  • View.php

Para que o nosso AutoLoad.php funcione conforme o esperado precisamos seguir algumas regras (PSR, claro):

  • Devemos declara o Namespace dos arquivos
  • Ter uma classe por arquivo
  • Cada classe deve ter o mesmo nome do arquivo em questão, no padrão de escrita StudlyCaps

Com essas três regras em mente, nosso arquivo Controller.php deve ser parecido com:

<?php

namespace ErikCore;

class Controller
{
}

Que tal você mesmo criar os outros dois arquivos, não esquente, mais pra frente eu vou mostrá-los e também disponibilizar tudo pra Download.

Volte ao seu index.php e vamos carregar nosso Controller, adicione isso no fim do arquivo:

use ErikCoreController as Controller;

$controller = new Controller();

Bem, então usamos o AutoLoad.php para carregar automaticamente nosso Controller, show de bola, precisamos agora que o Controller carregue o Model e o Action, isso é fácil, abra seu Controller.php e crie duas variáveis protected (já que não queremos que elas estejam acessíveis em mais nenhum lugar) uma chamada $model e outra chamada $view e por último um método chamado __construct que vai instanciar as classes nestas variáveis.

protected $model;
protected $view;

public function __construct()
{
    $this->model = new Model();
    $this->view = new View($this->model);
}

O que acontece aqui é que o método __construct é a primeira coisa a ser chamada em uma classe, e isso é feito sem que precisemos fazer nada, apenas declará-lo na classe, então estamos fazendo com que ele carregue as classes Model() e View() automaticamente. Outra coisa é que eu não declarei o caminho das Classes, sabe porque, porque o AutoLoad reconhece essas classes com o mesmo Namespace da atual (Controller.php) então já carrega certinho cada uma.

Agora vamos criar nosso Model(), abra o Model.php.

<?php

namespace ErikCore;

class Model
{
    public $string;

    public function __construct()
    {
        $this->string = "MVC + PHP = Incrível, clique aqui!";
    }
}

Quando o controller chama nosso Model ele automaticamente seta na variável public (que pode ser acessada em qualquer lugar, mesmo fora da classe) o valor "MVC + PHP = Incrível, clique aqui!", então ele está manipulando os dados, mas só isso, ele não exibe nada e só retorna se for essa a vontade do todo poderoso Controller, claro, é essa a função dele.

Por fim vamos configurar nossa View:

<?php

namespace ErikCore;

class View
{
    private $model;

    public function __construct($model)
    {
        $this->model = $model;
    }

    public function __destruct()
    {
        echo '

<p>
  <a href="?action=clicked">' . $this->model->string . "</a>
</p>";
    }
}

Além de usar o __construct, também estou chamando o __destruct que é chamado no final de tudo, quando a classe é destruída, ou seja, a ultima coisa a se executar no script, é ela que vai imprimir a saída do Model na tela.

Neste momento seu Framework já exibe alguma coisa, acesse o index.php do framework no navegador, você deverá ver escrito a mensagem setada no Model, a saída é um link a ser clicado e o texto é uma clara chamada para que esta ação seja executada, mas claro, não faz nada ainda, vamos corrigir isso.

Quando você clica no link ele vai para index.php?action=clicked, o action ali vai definir a ação a ser tomada no Controller, ou seja, a função a ser executada, abra seu index.php:

if (!empty($_GET['action'])) {
    $controller->{$_GET['action']}();
}

Deixe uma linha em branco no fim do arquivo (para suprir mais uma recomendação PSR), salve e feche, você não vai mais precisar dele.

No Controller.php precisamos chamar a função que irá alterar os dados na camada Model, em baixo de __construct adicione:

public function clicked()
{
    $this->model->string = "Dados atualizados, obrigado ao MVC + PHP!";
}

E pronto, salve, feche o arquivo e clique no link do navegador e veja a nova mensagem.

Conclusão

Você conseguiria o mesmo efeito usando includes e chamando arquivos externos usando a url, por exemplo, usar o valor de action para incluir o clicked.php e se caso não existir incluir um home.php com a primeira mensagem, isso também funciona, mas agora estamos trabalhando com dados estruturados e seguros, ele chama uma função e não um arquivo, além disso ampliar os recursos é algo muito mais simples aqui, você cria cada tarefa ou recurso ou script (como queira chamar) independente, em um pacote (aqui usamos apenas o Core) que agrupe tudo, também é possível usar recursos de outros Frameworks, mas claro que precisaríamos ampliar esta aplicação, do jeito que está ela só serve para fins didáticos.

No próximo artigo vamos começar a criar um framework um pouco mais elaborado, não vai ser nenhum Zend ou Cake, mas vai trazer bastante dicas pra gente.

Espero que tenham entendido.

Baixe os arquivos desta aula.

Esta foi a 1ª aula de 11 da série Entendendo o padrão MVC na prática


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!