Se você desenvolve há algum tempo em PHP, provavelmente já deve ter pensado em qual seria a melhor forma para organizar seus arquivos. Talvez até tenha encontrado uma solução e já está utilizando sua própria estrutura de pastas e arquivos, ou um framework que faça isso por você para beneficiar o seu sistema.

Nesse artigo vou explicar como criar seu próprio framework com padrão MVC em PHP. Claro que não será um sistema extremamente avançado, mas será uma grande introdução para lhe ajudar a desenvolver aplicativos de maneira mais organizada e utilizando “Programação Orientada a Objetos“.

Então vamos lá!

O que é MVC?

MVC significa Model – View – Controller (Modelo – Visão – Controlador)  e é um modelo da arquitetura de software que tem a função de separar front-end (que o usuário vê) do back-end (que é o motor da aplicação).

A estrutura MVC funciona da seguinte maneira:

  • Model (modelo) – O Model é responsável por tratar de tudo que é relacionado com os dados, como criar, ler, atualizar e excluir valores da base de dados (CRUD), tratar das regras de negócios, da lógica e das funções. Apesar de fazer isso tudo, o Model não apresenta nada na tela e não executa nada por si. Normalmente, um View requisita que determinado Model execute uma ação e a mesma é executada dentro do View.
  • View (Visão) – O View é a parte que o usuário vê na tela, como HTML, JavaScript, CSS, Imagens e assim por diante. O View não tem nenhuma ação, mas requisita que o Model execute qualquer ação e mostra os valores retornados para o usuário. É importante ressaltar que um View não depende de nenhum Model, por exemplo, se você vai apenas exibir dados HTML na tela, e não vai precisar de base de dados, talvez um Model não seja necessário.
  • Controller (Controlador) – O Controller é responsável por resolver se um Model e/ou um View é necessário. Caso positivo; ele incluirá os arquivos e funções necessárias para o sistema funcionar adequadamente.

Veja uma imagem representando como o modelo MVC funciona:

Model-view-controller (MVC)

Model-view-controller (MVC)

Na imagem acima, as linhas sólidas exemplificam partes que têm ligações diretas; as linhas tracejadas mostram ligações indiretas. Isso é maleável e pode variar dependendo da sua aplicação e ação que está sendo executada.

Nosso projeto

Nosso projeto será criar um sistema de notícias com área administrativa, portanto, é obrigatório que tenhamos o seguinte:

  • Sistema de login para os administradores;
  • Sistema de registro de usuários (CRUD);
  • Sistema de permissões;
  • Sistema de cadastro de notícias (CRUD);

Faremos tudo no modelo MVC, mas para atingir nosso objetivo teremos que criar várias outras pastas e arquivos.

Basicamente, nossa estrutura ficará como na imagem abaixo:

Fluxograma da nossa aplicação MVC em PHP

Fluxograma da nossa aplicação MVC em PHP

Na imagem acima temos uma apresentação de como a informação vai passar pelo nosso sistema. Veja uma descrição:

  1. O usuário acessa o site;
  2. O arquivo index.php apenas inclui o arquivo config.php;
  3. O arquivo config.php é responsável por registrar nossas configurações e carregar o arquivo loader.php;
  4. O arquivo loader.php carrega o arquivo global-functions.php, que é responsável por manter todas as funções globais. Na verdade, a função mais importante que temos ali é a _autoload, para carregar classes automaticamente. Ele também é responsável por instanciar a classe “TutsupMVC” que vai controlar todo o início da aplicação.
  5. A classe “TutsupMVC” vai verificar se um controlador foi requisitado (pela URL) e incluir o mesmo. Ela também vai verificar se alguma ação do controlador foi requisitada (ainda pela URL). Caso contrário, a ação “index” do controlador será executada. Sendo assim, todo controlador tem que ter pelo menos uma ação, chamada de “index“.
  6. O arquivo controlador (controller) é responsável por ter todas as ações daquela sessão. Cada ação irá diferenciar os Models e/ou Views que forem requisitados. Às vezes uma ação pode utilizar um View e vários Models, ou vice-versa;
  7. O arquivo modelo (model) terá todas os métodos necessários para executar as ações do View. Este arquivo não é obrigatório;
  8. O arquivo de visão (view) irá simplesmente mostrar tudo ao usuário que requisitou a ação.

Estrutura de pastas

Nossas pastas ficarão da seguinte maneira:

│   .htaccess
│   config.php
│   index.php
│   loader.php
│
├───classes
│       class-MainController.php
│       class-MainModel.php
│       class-PasswordHash.php
│       class-TutsupDB.php
│       class-TutsupMVC.php
│       class-UserLogin.php
│
├───controllers
│       home-controller.php
│       login-controller.php
│       noticias-controller.php
│       user-register-controller.php
│
├───functions
│       global-functions.php
│
├───includes
│       404.php
│
├───models
│   ├───noticias
│   │       noticias-adm-model.php
│   │
│   └───user-register
│           user-register-model.php
│
├───views
│   ├───home
│   │       home-view.php
│   │
│   ├───login
│   │       login-view.php
│   │
│   ├───noticias
│   │       noticias-adm-view.php
│   │       noticias-view.php
│   │
│   ├───user-register
│   │       user-register-view.php
│   │
│   ├───_css
│   │       style.css
│   │
│   ├───_images
│   ├───_includes
│   │       footer.php
│   │       header.php
│   │       menu.php
│   │
│   ├───_js
│   │       html5.js
│   │       scripts.js
│   │
│   └───_uploads
└───_utilitarios
        db.sql
        fluxograma.jpg
        mvc.jpg

As pastas que têm um _sublinhado antes do nome, são pastas que incluem arquivos que os views utilizam, mas que não são views. Normalmente são arquivos que não são acessados diretamente, como header.php, footer.php e outros. Eles são incluídos nos arquivos que precisamos.

Ao decorrer desse curso, pode ser que eu adicione ou remova recursos, mas não se preocupe, vou lhe dizer quando algo for alterado.

Modelo da URL e parâmetros

Vamos obter todos os nossos parâmetros por HTTP GET no seguinte formato:

http://www.exemplo.com/index.php?url=controlador/ação/parametro1/parametro2/etc…

Se você perceber, temos apenas um parâmetro na URL acima, o $_GET[‘url’]. Isso porque vamos utilizar o arquivo .htaccess do Apache para reescrever a URL.

A mesma URL acima ficará assim:

http://www.exemplo.com/controlador/ação/parametro1/parametro2/etc…

Nossa classe TutsupMVC vai tratar de separar o controlador, a ação, e enviar o restante dos parâmetros para os métodos dos controladores.

Base de dados do modelo MVC em PHP

Precisaremos de uma base de dados chamada Tutsup:

CREATE DATABASE IF NOT EXISTS `tutsup` CHARACTER SET utf8;

Uma tabela chamada users:

CREATE TABLE IF NOT EXISTS `tutsup`.`users` (
  `user_id` INT(11) NOT NULL AUTO_INCREMENT,
  `user` VARCHAR(255) COLLATE utf8_bin NOT NULL,
  `user_password` VARCHAR(255) COLLATE utf8_bin NOT NULL,
  `user_name` VARCHAR(255) COLLATE utf8_bin DEFAULT NULL,
  `user_session_id` VARCHAR(255) COLLATE utf8_bin DEFAULT NULL,
  `user_permissions` LONGTEXT COLLATE utf8_bin,
  PRIMARY KEY (`user_id`)
) ENGINE=MYISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Uma tabela chamada noticias:

CREATE TABLE IF NOT EXISTS `tutsup`.`noticias` (
  `noticia_id` INT (11) NOT NULL AUTO_INCREMENT,
  `noticia_data` DATETIME DEFAULT '0000-00-00 00:00:00',
  `noticia_autor` VARCHAR (255),
  `noticia_titulo` VARCHAR (255),
  `noticia_texto` TEXT,
  `noticia_imagem` VARCHAR (255),
  PRIMARY KEY (`noticia_id`)
) ENGINE = MYISAM CHARSET = utf8 ;

Precisaremos inserir um usuário chamado “Admin” na base de dados.

Os dados do usuário inicial serão:

Usuário: Admin
Senha: admin

Nosso sistema faz distinção entre letras maiúsculas e minúsculas.

INSERT INTO `tutsup`.`users` (
  `user_id`,
  `user`,
  `user_password`,
  `user_name`,
  `user_session_id`,
  `user_permissions`
) 
VALUES
  (
    NULL,
    'Admin',
    '$2a$08$2sGQinTFe3GF/YqAYQ66auL9o6HeFCQryHdqUDvuEVN0J1vdhimii',
    'Admin',
    'ljfp99gvqm2hg2bj6jjpu4ol64',
	'a:2:{i:0;s:13:"user-register";i:1;s:18:"gerenciar-noticias";}'
  ) ;

Se você quiser o script completo, basta criar um arquivo com a extensão “.sql“, com o seguinte texto:

CREATE DATABASE IF NOT EXISTS `tutsup` CHARACTER SET utf8;

CREATE TABLE IF NOT EXISTS `tutsup`.`noticias` (
  `noticia_id` INT (11) NOT NULL AUTO_INCREMENT,
  `noticia_data` DATETIME DEFAULT '0000-00-00 00:00:00',
  `noticia_autor` VARCHAR (255),
  `noticia_titulo` VARCHAR (255),
  `noticia_texto` TEXT,
  `noticia_imagem` VARCHAR (255),
  PRIMARY KEY (`noticia_id`)
) ENGINE = MYISAM CHARSET = utf8 ;

CREATE TABLE IF NOT EXISTS `tutsup`.`users` (
  `user_id` INT(11) NOT NULL AUTO_INCREMENT,
  `user` VARCHAR(255) COLLATE utf8_bin NOT NULL,
  `user_password` VARCHAR(255) COLLATE utf8_bin NOT NULL,
  `user_name` VARCHAR(255) COLLATE utf8_bin DEFAULT NULL,
  `user_session_id` VARCHAR(255) COLLATE utf8_bin DEFAULT NULL,
  `user_permissions` LONGTEXT COLLATE utf8_bin,
  PRIMARY KEY (`user_id`)
) ENGINE=MYISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `tutsup`.`users` (
  `user_id`,
  `user`,
  `user_password`,
  `user_name`,
  `user_session_id`,
  `user_permissions`
) 
VALUES
  (
    NULL,
    'Admin',
    '$2a$08$2sGQinTFe3GF/YqAYQ66auL9o6HeFCQryHdqUDvuEVN0J1vdhimii',
    'Admin',
    'ljfp99gvqm2hg2bj6jjpu4ol64',
	'a:2:{i:0;s:13:"user-register";i:1;s:18:"gerenciar-noticias";}'
  ) ;

Utilize qualquer programa de gerenciamento de base de dados MySQL para importar este script.

Se quiser utilizar o phpMyAdmin, já criar um tutorial pra você:

Veja uma imagem da minha base de dados já pronta:

Tabela para nossa aplicação

Tabela para nossa aplicação

É bem simples.

Criando o arquivo .htaccess

Crie uma pasta com o nome do nosso aplicativo (no meu caso “crud“), em seguida abra seu editor de textos preferido e crie um arquivo chamado .htaccess:

Nele adicione o seguinte:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l

RewriteRule ^(.+)$ index.php?path=$1 [QSA,L]

Este arquivo deverá ficar na pasta principal da nossa aplicação. Veja um exemplo do meu sistema (já pronto):

.htaccess

.htaccess

Este arquivo vai permitir que nossas URLs sejam escritas dessa maneira:

http://www.exemplo.com/index.php?url=controlador/ação/parametro1/parametro2/etc…

Para:

http://www.exemplo.com/controlador/ação/parametro1/parametro2/etc…

Se você não quiser utilizar o recurso do Apache, basta seguir o primeiro exemplo da URL.

Veja uma imagem do arquivo .htaccess:

.htaccess

.htaccess

index.php

Nosso arquivo principal, o index.php, terá apenas o seguinte:

<?php
// Config
require_once 'config.php';
?>

Mais nada…

config.php

Nosso arquivo config.php terá as configurações que você pode alterar para cada um de seus projetos, como configurações de URL, base de dados, debug, e assim por diante, veja:

<?php
/**
 * Configuração geral
 */

// Caminho para a raiz
define( 'ABSPATH', dirname( __FILE__ ) );

// Caminho para a pasta de uploads
define( 'UP_ABSPATH', ABSPATH . '/views/_uploads' );

// URL da home
define( 'HOME_URI', 'http://127.0.0.1/Cursos/crud' );

// Nome do host da base de dados
define( 'HOSTNAME', 'localhost' );

// Nome do DB
define( 'DB_NAME', 'tutsup' );

// Usuário do DB
define( 'DB_USER', 'root' );

// Senha do DB
define( 'DB_PASSWORD', '' );

// Charset da conexão PDO
define( 'DB_CHARSET', 'utf8' );

// Se você estiver desenvolvendo, modifique o valor para true
define( 'DEBUG', true );

/**
 * Não edite daqui em diante
 */

// Carrega o loader, que vai carregar a aplicação inteira
require_once ABSPATH . '/loader.php';
?>

Perceba que este arquivo também carrega o arquivo loader.php (veremos seu conteúdo abaixo).

loader.php

O arquivo loader.php inicia a sessão, configura os erros (dependendo da constante DEBUG) e inclui um arquivo com funções globais.

Veja:

<?php
// Evita que usuários acesse este arquivo diretamente
if ( ! defined('ABSPATH')) exit;
 
// Inicia a sessão
session_start();

// Verifica o modo para debugar
if ( ! defined('DEBUG') || DEBUG === false ) {

	// Esconde todos os erros
	error_reporting(0);
	ini_set("display_errors", 0); 
	
} else {

	// Mostra todos os erros
	error_reporting(E_ALL);
	ini_set("display_errors", 1); 
	
}

// Funções globais
require_once ABSPATH . '/functions/global-functions.php';

// Carrega a aplicação
$tutsup_mvc = new TutsupMVC();

Veja que este arquivo também inicia a classe “TutsupMVC“, ela vai procurar o controlador e a ação. Você vai ver seu conteúdo na próxima aula.

O loader.php carrega o arquivo /functions/global-functions.php, vamos ver seu conteúdo.

functions/global-functions.php

Este arquivo carrega duas funções muito importantes para nossa aplicação, veja:

<?php
/**
 * Verifica chaves de arrays
 *
 * Verifica se a chave existe no array e se ela tem algum valor.
 * Obs.: Essa função está no escopo global, pois, vamos precisar muito da mesma.
 *
 * @param array  $array O array
 * @param string $key   A chave do array
 * @return string|null  O valor da chave do array ou nulo
 */
function chk_array ( $array, $key ) {
	// Verifica se a chave existe no array
	if ( isset( $array[ $key ] ) && ! empty( $array[ $key ] ) ) {
		// Retorna o valor da chave
		return $array[ $key ];
	}
	
	// Retorna nulo por padrão
	return null;
} // chk_array

/**
 * Função para carregar automaticamente todas as classes padrão
 * Ver: http://php.net/manual/pt_BR/function.autoload.php.
 * Nossas classes estão na pasta classes/.
 * O nome do arquivo deverá ser class-NomeDaClasse.php.
 * Por exemplo: para a classe TutsupMVC, o arquivo vai chamar class-TutsupMVC.php
 */
function __autoload($class_name) {
	$file = ABSPATH . '/classes/class-' . $class_name . '.php';
	
	if ( ! file_exists( $file ) ) {
		require_once ABSPATH . '/includes/404.php';
		return;
	}
	
	// Inclui o arquivo da classe
    require_once $file;
} // __autoload

As ações das funções estão descritas no código acima.

Download

Se você quiser baixar tudo o que já foi feito até agora, segue o link:

Caso queira contribuir com o projeto, acesso no Github:

Perceba que o arquivo que você baixou acima está muito mais adiantado do que este artigo, porém, ainda vamos chegar lá, basta você acompanhar o Tutsup.

Outras partes do artigo

Este artigo está dividido em várias partes, pois seu conteúdo é muito extenso. Também existe uma categoria exclusiva para todo seu conteúdo, chamada de MVC em PHP.

Veja todas as suas partes já publicas nos links abaixo:

Em caso de dúvidas, comente-a!