Até agora, criamos as partes mais simples do sistema, tais como criar um controller, model e view e exibir o conteúdo na tela, mas e se eu precisar de uma área restrita?

Nossa estrutura MVC foi criada pensando nisso, ou seja, além de você ter a possibilidade de criar partes restritas no seu sistema, também há a possibilidade de criar permissões de acesso para determinados M, V e C.

Além disso, você também pode restringir apenas pedaços de uma página, ou bloquear o acesso completo, fica a seu critério.

Neste artigo você vai aprender a criar o sistema de login, sistema de registro de usuários e MVCs com acesso restrito.

Artigos anteriores

Antes que você continue a ler, é necessário que você tenha visto os artigos anteriores, assim você entenderá melhor o conteúdo que verá aqui:

 

Nos artigos acima eu guio você por toda a estrutura do sistema, detalhando como cada área funciona e interage uma com a outra.

Download

Como estamos criando uma aplicação inteira, são vários arquivos que se encaixam uns nos outros. Se você quiser baixar os dados e ir acompanhando com eles em seu computador, segue o link abaixo:

Caso queira contribuir com o projeto, acesso no Github:

No download acima, a aplicação está praticamente completa. Pode ser que algo seja alterado conforme nossa necessidade, mas vou detalhar tudo no artigo.

Criando o controller de login

Vamos seguir o mesmo modelo da nossa última aula para criar o controller do nosso sistema de login. Felizmente, precisaremos apenas de um controller e um view, já que nossa classe “UserLogin” fará o resto pra gente.

controllers/login-controller.php

Para o controller do nosso login teremos o seguinte:

<?php
/**
 * LoginController - Controller de exemplo
 *
 * @package TutsupMVC
 * @since 0.1
 */
class LoginController extends MainController
{

    /**
    * Carrega a página "/views/login/index.php"
    */
    public function index() {
	// Título da página
	$this->title = 'Login';
		
	// Parametros da função
	$parametros = ( func_num_args() >= 1 ) ? func_get_arg(0) : array();
	
	// Login não tem Model
		
	/** Carrega os arquivos do view **/
		
	// /views/_includes/header.php
        require ABSPATH . '/views/_includes/header.php';
		
	// /views/_includes/menu.php
        require ABSPATH . '/views/_includes/menu.php';
		
	// /views/home/login-view.php
        require ABSPATH . '/views/login/login-view.php';
		
	// /views/_includes/footer.php
        require ABSPATH . '/views/_includes/footer.php';
		
    } // index
	
} // class LoginController

Este controller tem apenas uma ação – index – que será carregada automaticamente quando acessarmos a URL /login/ do nosso sistema. Dentro dessa ação, simplesmente incluímos os arquivos dos views que exibem HTML.

São eles:

/views/_includes/header.php

Este é um cabeçalho HTML comum, veja seu código:

<?php if ( ! defined('ABSPATH')) exit; ?>

<!DOCTYPE html>
<!--[if IE 7]>
<html class="ie ie7" lang="pt-BR">
<![endif]-->
<!--[if IE 8]>
<html class="ie ie8" lang="pt-BR">
<![endif]-->
<!--[if !(IE 7) & !(IE 8)]><!-->
<html lang="pt-BR">
<!--<![endif]-->

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width">

	<link rel="stylesheet" href="<?php echo HOME_URI;?>/views/_css/style.css">

	<!--[if lt IE 9]>
	<script src="<?php echo HOME_URI;?>/views/_js/scripts.js"></script>
	<![endif]-->

	<title><?php echo $this->title; ?></title>
</head>
<body>

<div class="main-page">

 /views/_includes/menu.php

Outro arquivo HTML comum:

<?php if ( ! defined('ABSPATH')) exit; ?>

<?php if ( $this->login_required && ! $this->logged_in ) return; ?>

<nav class="menu clearfix">
	<ul>
		<li><a href="<?php echo HOME_URI;?>">Home</a></li>
		<li><a href="<?php echo HOME_URI;?>/login/">Login</a></li>
		<li><a href="<?php echo HOME_URI;?>/user-register/">User Register</a></li>
		<li><a href="<?php echo HOME_URI;?>/noticias/">Notícias</a></li>
		<li><a href="<?php echo HOME_URI;?>/noticias/adm/">Notícias Admin</a></li>
		<li><a href="<?php echo HOME_URI;?>/exemplo/">Exemplo básico</a></li>
	</ul>
</nav>

/views/_includes/footer.php

Este tem puramente HTML:

<?php if ( ! defined('ABSPATH')) exit; ?>

</div> <!-- .main-page (header.php) -->

</body>
</html>

 Restringindo acesso direto aos arquivos

É importante que você tenha em mente que usuários mais entendidos sobre PHP e HTML, poderão entender como o sistema funciona e tentar executar arquivos diretamente. Para restringir o acesso a direto a qualquer conteúdo do seu sistema de MVC, adicione a seguinte linha no cabeçalho do seu código:

<?php if ( ! defined('ABSPATH')) exit; ?>

 Criando o view do login

O view do nosso login terá apenas um formulário HTML, veja:

views/login/login-view.php

<?php if ( ! defined('ABSPATH')) exit; ?>

<div class="wrap">

	<form method="post">
		<table class="form-table">
			<tr>
				<td>User</td> 
				<td><input name="userdata[user]"></td>
			</tr>
			<tr>
				<td>Password </td>
				<td><input type="password" name="userdata[user_password]"></td>
			</tr>
			<tr>
				<td colspan="2">
					<input type="submit" value="Enter"> 
				</td>
			</tr>
		</table>
	</form>
	
</div> <!-- .wrap -->

Só isso já é suficiente para que o usuário faça login no sistema e seja redirecionado para a URL que tentou acessar antes do login. O problema é que não temos feedback para o usuário, nenhuma mensagem é apresentada na tela. Para resolver isto, podemos utilizar as propriedades retornadas pela classe UserLogin, veja:

<?php if ( ! defined('ABSPATH')) exit; ?>

<div class="wrap">

<?php
if ( $this->logged_in ) {
	echo '<p class="alert">Logado</p>';
}
?>

	<form method="post">
		<table class="form-table">
			<tr>
				<td>User</td> 
				<td><input name="userdata[user]"></td>
			</tr>
			<tr>
				<td>Password </td>
				<td><input type="password" name="userdata[user_password]"></td>
			</tr>
			<?php
			if ( $this->login_error ) {
				echo '<tr><td colspan="2" class="error">' . $this->login_error . '</td></tr>';
			}
			?>
			<tr>
				<td colspan="2">
					<input type="submit" value="Enter"> 
				</td>
			</tr>
		</table>
	</form>
	
</div> <!-- .wrap -->

Agora estamos verificando as propriedades $this->login_error para ver se existe algum erro no login, e $this->logged_in, para saber se o usuário está logado.

O formulário acima deverá exibir uma mensagem caso a senha do usuário estiver incorreta, se o usuário não existir, se ele está logado e coisas do tipo.

Formulário de login

Formulário de login

As mensagens estão em inglês porque a classe UserLogin era de outro sistema que eu havia criado anteriormente. Você pode alterar conforme preferir.

Criando o controller para registro de usuários

O controle de usuários é essencial para nossa aplicação, sem ele não poderíamos bloquear ou permitir nada.

Este controller só irá permitir que o view seja acessado por usuários logados. Além disso, ele também só irá permitir que um usuário visualize seu conteúdo se o mesmo tiver a permissão necessária.

Como funcionam as permissões?

Na verdade, você é quem escolhe como funcionam as permissões.

O formulário de cadastro terá um campo onde você separa as permissões por vírgula, por exemplo:

  • acessar-home, gerenciar-noticias, abrir-modelo-adm, e assim por diante…

Em seguida, é só verificar no controller se o usuário tem aquela permissão, por exemplo:

// Verifica se o usuário tem a permissão para acessar essa página
if (!$this->check_permissions('permissao-necessaria', $this->userdata['user_permissions'])) {

	// Exibe uma mensagem
	echo 'Você não tem permissões para acessar essa página.';
	
	// Finaliza aqui
	return;
}

Simples assim!

controllers/user-register-controller.php

Vejamos agora o código do nosso controller para registro de usuários:

<?php
/**
 * UserRegisterController - Controller de exemplo
 *
 * @package TutsupMVC
 * @since 0.1
 */
class UserRegisterController extends MainController
{

   /**
    * $login_required
    *
    * Se a página precisa de login
    *
    * @access public
    */
    public $login_required = true;

    /**
     * $permission_required
     *
     * Permissão necessária
     *
     * @access public
     */
    public $permission_required = 'user-register';

    /**
     * Carrega a página "/views/user-register/index.php"
     */
    public function index() {
		// Page title
		$this->title = 'User Register';
		
		// Verifica se o usuário está logado
		if ( ! $this->logged_in ) {
		
			// Se não; garante o logout
			$this->logout();
			
			// Redireciona para a página de login
			$this->goto_login();
			
			// Garante que o script não vai passar daqui
			return;
		
		}
		
		// Verifica se o usuário tem a permissão para acessar essa página
		if (!$this->check_permissions($this->permission_required, $this->userdata['user_permissions'])) {
		
			// Exibe uma mensagem
			echo 'Você não tem permissões para acessar essa página.';
			
			// Finaliza aqui
			return;
		}
	
		// Parametros da função
		$parametros = ( func_num_args() >= 1 ) ? func_get_arg(0) : array();
	
	// Carrega o modelo para este view
        $modelo = $this->load_model('user-register/user-register-model');
				
	/** Carrega os arquivos do view **/
		
	// /views/_includes/header.php
        require ABSPATH . '/views/_includes/header.php';
		
	// /views/_includes/menu.php
        require ABSPATH . '/views/_includes/menu.php';
		
	// /views/user-register/index.php
        require ABSPATH . '/views/user-register/user-register-view.php';
		
	// /views/_includes/footer.php
        require ABSPATH . '/views/_includes/footer.php';
		
    } // index
	
} // class UserRegisterController

Este controle verifica se o usuário está logado e se ele tem permissões para acessar tal página, veja o trecho:

public $permission_required = 'user-register';

/* ... */
		
// Verifica se o usuário está logado
if ( ! $this->logged_in ) {
	
	// Se não; garante o logout
	$this->logout();
	
	// Redireciona para a página de login
	$this->goto_login();
			
	// Garante que o script não vai passar daqui
	return;
		
}
		
// Verifica se o usuário tem a permissão para acessar essa página
if (!$this->check_permissions($this->permission_required, $this->userdata['user_permissions'])) {
	
	// Exibe uma mensagem
	echo 'Você não tem permissões para acessar essa página.';
		
	// Finaliza aqui
	return;
}

Depois dessa verificação, incluímos o model e os views.

Criando o model para registro de usuários

Vamos analisar o código do model.

models/user-register/user-register-model.php

Este é um modelo bem grande, pois já inclui todo o sistema de CRUD (create, read, update and delete). Mas não se assuste com o tamanho, pois ele fará praticamente a mesma coisa que todos os seus modelos irão fazer:

<?php 
/**
 * Classe para registros de usuários
 *
 * @package TutsupMVC
 * @since 0.1
 */

class UserRegisterModel
{

	/**
	 * $form_data
	 *
	 * Os dados do formulário de envio.
	 *
	 * @access public
	 */	
	public $form_data;

	/**
	 * $form_msg
	 *
	 * As mensagens de feedback para o usuário.
	 *
	 * @access public
	 */	
	public $form_msg;

	/**
	 * $db
	 *
	 * O objeto da nossa conexão PDO
	 *
	 * @access public
	 */
	public $db;

	/**
	 * Construtor
	 * 
	 * Carrega  o DB.
	 *
	 * @since 0.1
	 * @access public
	 */
	public function __construct( $db = false ) {
                // Carrega o BD
		$this->db = $db;
	}

	/**
	 * Valida o formulário de envio
	 * 
	 * Este método pode inserir ou atualizar dados dependendo do campo de
	 * usuário.
	 *
	 * @since 0.1
	 * @access public
	 */
	public function validate_register_form () {
	
		// Configura os dados do formulário
		$this->form_data = array();
		
		// Verifica se algo foi postado
		if ( 'POST' == $_SERVER['REQUEST_METHOD'] && ! empty ( $_POST ) ) {
		
			// Faz o loop dos dados do post
			foreach ( $_POST as $key => $value ) {
			
				// Configura os dados do post para a propriedade $form_data
				$this->form_data[$key] = $value;
				
				// Nós não permitiremos nenhum campos em branco
				if ( empty( $value ) ) {
					
					// Configura a mensagem
					$this->form_msg = '<p class="form_error">There are empty fields. Data has not been sent.</p>';
					
					// Termina
					return;
					
				}			
			
			}
		
		} else {
		
			// Termina se nada foi enviado
			return;
			
		}
		
		// Verifica se a propriedade $form_data foi preenchida
		if( empty( $this->form_data ) ) {
			return;
		}
		
		// Verifica se o usuário existe
		$db_check_user = $this->db->query (
			'SELECT * FROM `users` WHERE `user` = ?', 
			array( 
				chk_array( $this->form_data, 'user')		
			) 
		);
		
		// Verifica se a consulta foi realizada com sucesso
		if ( ! $db_check_user ) {
			$this->form_msg = '<p class="form_error">Internal error.</p>';
			return;
		}
		
		// Obtém os dados da base de dados MySQL
		$fetch_user = $db_check_user->fetch();
		
		// Configura o ID do usuário
		$user_id = $fetch_user['user_id'];
		
		// Precisaremos de uma instância da classe Phpass
		// veja http://www.openwall.com/phpass/
		$password_hash = new PasswordHash(8, FALSE);
		
		// Cria o hash da senha
		$password = $password_hash->HashPassword( $this->form_data['user_password'] );
		
		// Verifica se as permissões tem algum valor inválido: 
		// 0 a 9, A a Z e , . - _
		if ( preg_match( '/[^0-9A-Za-z,.-_s ]/is', $this->form_data['user_permissions'] ) ) {
			$this->form_msg = '<p class="form_error">Use just letters, numbers and a comma for permissions.</p>';
			return;
		}		
		
		// Faz um trim nas permissões
		$permissions = array_map('trim', explode(',', $this->form_data['user_permissions']));
		
		// Remove permissões duplicadas
		$permissions = array_unique( $permissions );
		
		// Remove valores em branco
		$permissions = array_filter( $permissions );
		
		// Serializa as permissões
		$permissions = serialize( $permissions );
		
		
		// Se o ID do usuário não estiver vazio, atualiza os dados
		if ( ! empty( $user_id ) ) {

			$query = $this->db->update('users', 'user_id', $user_id, array(
				'user_password' => $password, 
				'user_name' => chk_array( $this->form_data, 'user_name'), 
				'user_session_id' => md5(time()), 
				'user_permissions' => $permissions, 
			));
			
			// Verifica se a consulta está OK e configura a mensagem
			if ( ! $query ) {
				$this->form_msg = '<p class="form_error">Internal error. Data has not been sent.</p>';
				
				// Termina
				return;
			} else {
				$this->form_msg = '<p class="form_success">User successfully updated.</p>';
				
				// Termina
				return;
			}
		// Se o ID do usuário estiver vazio, insere os dados
		} else {
		
			// Executa a consulta 
			$query = $this->db->insert('users', array(
				'user' => chk_array( $this->form_data, 'user'), 
				'user_password' => $password, 
				'user_name' => chk_array( $this->form_data, 'user_name'), 
				'user_session_id' => md5(time()), 
				'user_permissions' => $permissions, 
			));
			
			// Verifica se a consulta está OK e configura a mensagem
			if ( ! $query ) {
				$this->form_msg = '<p class="form_error">Internal error. Data has not been sent.</p>';
				
				// Termina
				return;
			} else {
				$this->form_msg = '<p class="form_success">User successfully registered.</p>';
				
				// Termina
				return;
			}
		}
	} // validate_register_form
	
	/**
	 * Obtém os dados do formulário
	 * 
	 * Obtém os dados para usuários registrados
	 *
	 * @since 0.1
	 * @access public
	 */
	public function get_register_form ( $user_id = false ) {
	
		// O ID de usuário que vamos pesquisar
		$s_user_id = false;
		
		// Verifica se você enviou algum ID para o método
		if ( ! empty( $user_id ) ) {
			$s_user_id = (int)$user_id;
		}
		
		// Verifica se existe um ID de usuário
		if ( empty( $s_user_id ) ) {
			return;
		}
		
		// Verifica na base de dados
		$query = $this->db->query('SELECT * FROM `users` WHERE `user_id` = ?', array( $s_user_id ));
		
		// Verifica a consulta
		if ( ! $query ) {
			$this->form_msg = '<p class="form_error">Usuário não existe.</p>';
			return;
		}
		
		// Obtém os dados da consulta
		$fetch_userdata = $query->fetch();
		
		// Verifica se os dados da consulta estão vazios
		if ( empty( $fetch_userdata ) ) {
			$this->form_msg = '<p class="form_error">User do not exists.</p>';
			return;
		}
		
		// Configura os dados do formulário
		foreach ( $fetch_userdata as $key => $value ) {
			$this->form_data[$key] = $value;
		}
		
		// Por questões de segurança, a senha só poderá ser atualizada
		$this->form_data['user_password'] = null;
		
		// Remove a serialização das permissões
		$this->form_data['user_permissions'] = unserialize($this->form_data['user_permissions']);
		
		// Separa as permissões por vírgula
		$this->form_data['user_permissions'] = implode(',', $this->form_data['user_permissions']);
	} // get_register_form
	
	/**
	 * Apaga usuários
	 * 
	 * @since 0.1
	 * @access public
	 */
	public function del_user ( $parametros = array() ) {

		// O ID do usuário
		$user_id = null;
		
		// Verifica se existe o parâmetro "del" na URL
		if ( chk_array( $parametros, 0 ) == 'del' ) {

			// Mostra uma mensagem de confirmação
			echo '<p class="alert">Tem certeza que deseja apagar este valor?</p>';
			echo '<p><a href="' . $_SERVER['REQUEST_URI'] . '/confirma">Sim</a> | 
			<a href="' . HOME_URI . '/user-register">Não</a> </p>';
			
			// Verifica se o valor do parâmetro é um número
			if ( 
				is_numeric( chk_array( $parametros, 1 ) )
				&& chk_array( $parametros, 2 ) == 'confirma' 
			) {
				// Configura o ID do usuário a ser apagado
				$user_id = chk_array( $parametros, 1 );
			}
		}
		
		// Verifica se o ID não está vazio
		if ( !empty( $user_id ) ) {
		
			// O ID precisa ser inteiro
			$user_id = (int)$user_id;
			
			// Deleta o usuário
			$query = $this->db->delete('users', 'user_id', $user_id);
			
			// Redireciona para a página de registros
			echo '<meta http-equiv="Refresh" content="0; url=' . HOME_URI . '/user-register/">';
			echo '<script type="text/javascript">window.location.href = "' . HOME_URI . '/user-register/";</script>';
			return;
		}
	} // del_user
	
	/**
	 * Obtém a lista de usuários
	 * 
	 * @since 0.1
	 * @access public
	 */
	public function get_user_list() {
	
		// Simplesmente seleciona os dados na base de dados 
		$query = $this->db->query('SELECT * FROM `users` ORDER BY user_id DESC');
		
		// Verifica se a consulta está OK
		if ( ! $query ) {
			return array();
		}
		// Preenche a tabela com os dados do usuário
		return $query->fetchAll();
	} // get_user_list
}

Como eu disse, isso é o que estamos acostumados a fazer com aplicações. Conectamos à base de dados e gerenciamos os dados.

É importante lembrar, que tudo o que está descrito no model acima, será acionado pelo view.

Por falar em view, vamos analisar como ele irá funcionar.

Criando um view para registro de usuários

O view é simplesmente um formulário HTML, mas também vamos executar as ações do model que precisamos, veja seu código:

views/user-register/user-register-view.php

<?php if ( ! defined('ABSPATH')) exit; ?>

<div class="wrap">

<?php
// Carrega todos os métodos do modelo
$modelo->validate_register_form();
$modelo->get_register_form( chk_array( $parametros, 1 ) );
$modelo->del_user( $parametros );
?>

<form method="post" action="">
	<table class="form-table">
		<tr>
			<td>Name: </td>
			<td> <input type="text" name="user_name" value="<?php 
				echo htmlentities( chk_array( $modelo->form_data, 'user_name') );
				?>" /></td>
		</tr>
		<tr>
			<td>User: </td>
			<td> <input type="text" name="user" value="<?php
				echo htmlentities( chk_array( $modelo->form_data, 'user') );
			?>" /></td>
		</tr>
		<tr>
			<td>Password: </td>
			<td> <input type="password" name="user_password" value="<?php
			echo htmlentities( chk_array( $modelo->form_data, 'user_password') );
			?>" /></td>
		</tr>
		<tr>
			<td>Permissions <br><small>(Separate permissions using commas)</small>: </td>
			<td> <input type="text" name="user_permissions" value="<?php
			echo htmlentities( chk_array( $modelo->form_data, 'user_permissions') );
			?>" /></td>
		</tr>
		<tr>
			<td colspan="2">
				<?php echo $modelo->form_msg;?>
				<input type="submit" value="Save" />
				<a href="<?php echo HOME_URI . '/user-register';?>">New user</a>
			</td>
		</tr>
	</table>
</form>

<?php 
// Lista os usuários
$lista = $modelo->get_user_list(); 
?>


<table class="list-table">
	<thead>
		<tr>
			<th>ID</th>
			<th>Usuário</th>
			<th>Name</th>
			<th>Permissões</th>
			<th>Edição</th>
		</tr>
	</thead>
			
	<tbody>
			
		<?php foreach ($lista as $fetch_userdata): ?>

			<tr>
			
				<td> <?php echo $fetch_userdata['user_id'] ?> </td>
				<td> <?php echo $fetch_userdata['user'] ?> </td>
				<td> <?php echo $fetch_userdata['user_name'] ?> </td>
				<td> <?php echo implode( ',', unserialize( $fetch_userdata['user_permissions'] ) ) ?> </td>
				
				<td> 
					<a href="<?php echo HOME_URI ?>/user-register/index/edit/<?php echo $fetch_userdata['user_id'] ?>">Edit</a>
					<a href="<?php echo HOME_URI ?>/user-register/index/del/<?php echo $fetch_userdata['user_id'] ?>">Delete</a>
				</td>

			</tr>
			
		<?php endforeach;?>
			
	</tbody>
</table>

</div> <!-- .wrap -->

Acessamos as ações que queremos do nosso model através da variável $modelo, criada no nosso controller.

Apenas com este view, temos a capacidade de criar, apagar, editar e visualizar todos os usuários.

Lembre-se que utilizamos um hash de senha bem seguro, portanto, não podemos obter os dados de senha dos usuários, somente modificar a mesma.

Formulário de registro

Formulário de registro

Download

Como estamos criando uma aplicação inteira, são vários arquivos que se encaixam uns nos outros. Se você quiser baixar os dados e ir acompanhando com eles em seu computador, segue o link abaixo:

Caso queira contribuir com o projeto, acesso no Github:

No download acima, a aplicação está praticamente completa. Pode ser que algo seja alterado conforme nossa necessidade, mas vou detalhar tudo no artigo.

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!