Nenhum produto encontrado nessa seleção.
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:
- Utilizando estrutura MVC em PHP – Parte 1
- Utilizando estrutura MVC em PHP – Parte 2
- Utilizando estrutura MVC em PHP – Parte 3
- Utilizando estrutura MVC em PHP – Parte 4
- Utilizando estrutura MVC em PHP – Parte 5
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:
- Download crud-1-0.zip
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.
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.
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:
- Download crud-1-0.zip
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:
- Utilizando estrutura MVC em PHP – Parte 1
- Utilizando estrutura MVC em PHP – Parte 2
- Utilizando estrutura MVC em PHP – Parte 3
- Utilizando estrutura MVC em PHP – Parte 4
- Utilizando estrutura MVC em PHP – Parte 5
Em caso de dúvidas, comente-a!