PHP DO BÁSICO AO AVANÇADO – AULA 15 – Nosso primeiro CONTROL e VIEW principais

Nesta aula, vamos criar o nosso primeiro CONTROL, e nossas primeiras VIEWS para poder utilizar nosso sistema com nossos recursos e também para poder ir evoluindo e acrescentando novas funcionalidades e usando novos recursos da linguagem PHP como conexão a banco de dados, e outras funções do PHP. Este primeiro controle simplesmente verifica se o usuário está logado, e monta um menu de acordo com o perfil de acesso do usuário, e direciona para a view principal, que tem a função de ser um índice de acesso ao site.
Se você vem acompanhando as aulas sequencialmente, deve ter reparado que na semana passada, após colocar todos os arquivos no diretório onde você está rodando seu projeto, ao abrir o browser, deve ter se deparado com uma tela similar a esta:
Que é uma função de saída do framework Brasap que estamos desenvolvendo, e se o ambiente for o de desenvolvimento, ele faz o DUMP das variáveis superglobais do PHP para facilitar o rastreamento e o debug, e mostra que o arquivo do control principal não está disponível.
Antes de apresentar o conteúdo deste arquivo, vamos criar 1 tabela em nosso banco de dados, e essa tabela vai conter todas as possíveis permissões de acesso dos usuários, de acordo com o perfil que estiver fazendo o login, ou todas as páginas que sejam acessíveis aos usuários que não estejam logados no sistema, ou as páginas do site, que serão acessíveis a todos, inclusive a tela de login. Vamos chamar essa tabela de permissões_acessos, e o comando para cria-la e incluir os dados básicos é:
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET AUTOCOMMIT = 0; START TRANSACTION; SET time_zone = "+00:00"; CREATE TABLE IF NOT EXISTS `permissoes_acesso` ( `id` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Identificador do Item do Menu', `id_pai` smallint(5) UNSIGNED NOT NULL COMMENT 'Identificador do PAI para multiníveis', `menu` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Texto do Menu (apresentado)', `endpoint` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'EndPoint chamado (ALIAS) para a URL', `ordem` smallint(6) NOT NULL DEFAULT '1' COMMENT 'Ordenação Dentro do MENU', `descricao` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Texto Explicativo sobre o menu', `icone` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Icone Padrão FontAwesome (fa)', `perfis` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'IDs dos tipos de perfil que possuem acesso, 0 todos', PRIMARY KEY (`id`), UNIQUE KEY `menu` (`menu`), UNIQUE KEY `endpoint` (`endpoint`), KEY `paiXfilho` (`id_pai`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Permissões de Acesso (menus) do Sistema'; INSERT INTO `permissoes_acesso` (`id`, `id_pai`, `menu`, `endpoint`, `ordem`, `descricao`, `icone`, `perfis`) VALUES (1, 0, 'Home', 'main', 1, 'Página Inicial', 'fas fa-home', '0'), (2, 0, 'Login', 'login/login', 3, 'Acesse com seus dados', 'fas fa-sign-in-alt', '0'), (3, 0, 'Logout', 'login/logout', 3, 'Sair do sistema (logout)', 'fas fa-sign-out-alt', '1'), (4, 0, 'Minha Conta', 'minhaconta', 4, 'Minha Conta', 'fas fa-house-user', '1'), (5, 0, 'Sobre', 'main/sobre', 2, 'Sobre Nossa Empresa', 'fas fa-building', '0'); COMMIT;
Este comando vai criar uma tabela no banco de dados, chamada permissões_acesso e vai inserir 5 registros nessa tabela que serão os menus iniciais de nível 0 do nosso sistema, que serão usados no nosso control para criar um menu de acesso, inicialmente, vamos usar somente o nível 0 de menu, ou seja, o nosso menu superior, ou principal do sistema. Com essa estrutura é possível você criar diversos níveis de menu e submenu, bem como delimitar se ele será um nível raiz e definir links para qualquer um dos níveis, mais futuramente explicaremos melhor como faremos isso.
Para que isso funcione, vamos inserir uma outra função no arquivo do Framework, que registra na sessão do usuário quais menus ele terá acesso, para que nossa view possa trata-lo e exibi-lo de acordo com o layout escolhido, como isso é uma das funções básicas do Framework, e necessária sempre, vamos incluir essas funções no arquivo _base.php dentro da pasta _framework, e seu conteúdo é:
/** * Cria um Array de menu de acesso para o usuário/perfil vigente * * @param Integer $id_pai Identificador para o menu_pai (se recursivo) * * @return Array Menus/Submenus que o usuário tem acesso **/ function menu($id_pai = 0) { $menu=array(); $perfil = logado(); $sql = "SELECT id, menu, endpoint, descricao, icone FROM permissoes_acesso WHERE id_pai='$id_pai' and perfis LIKE '%$perfil,%' order by ordem"; $rs = sqlQuery($sql); while ($r = $rs->fetch(PDO::FETCH_ASSOC)) { $menu[$r['id']]=$r; $sql = "SELECT count(id) as filhos FROM permissoes_acesso WHERE id_pai=".$r['id']." and perfis LIKE '%$perfil,%'"; $rf = sqlQuery($sql); $filhos = $rf->fetch(PDO::FETCH_ASSOC); if ($filhos['filhos']>=1) { $menu[$r['id']]['filhos'] = 1; } else { $menu[$r['id']]['filhos'] = 0; } } return $menu; } /** * Cria um Array de menu de acesso para o usuário/perfil vigente * * @param Integer $id_pai Identificador para o menu_pai (se rescursivo) * @param String $submenu Classe para um submenu * * @return Array Menus/Submenus que o usuário tem acesso **/ function submenu($id_pai = 0, $submenu = ' ') { echo "<ul class='$submenu dropdown-menu'>"; $perfil = logado(); $sql = "SELECT id, menu, endpoint, descricao, icone FROM permissoes_acesso WHERE id_pai='$id_pai' and perfis LIKE '%$perfil,%' order by ordem"; $rs = sqlQuery($sql); while ($r = $rs->fetch(PDO::FETCH_ASSOC)) { $sql = "SELECT count(id) as filhos FROM permissoes_acesso WHERE id_pai='".$r['id']."' and perfis LIKE '%$perfil,%' order by ordem"; $rf = sqlQuery($sql); $filhos = $rf->fetch(PDO::FETCH_ASSOC); if ($filhos['filhos']>0) { echo "<li><a class='dropdown-item' href='".URL.$r['endpoint']."'> <i class='".$r['icone']."'></i> ".$r['menu']. "    <i class='fas fa-caret-right'></i> </a>"; submenu($r['id'],'submenu'); } else { echo "<li><a class='dropdown-item' href='".URL.$r['endpoint']."'> <i class='".$r['icone']."'></i> ".$r['menu']."</a></li>"; } echo "</li>"; } echo "</ul>"; return true; }
No index.php também incluimos o suporte aos TITLES de páginas, a DEPENDÊNCIAS EXTERNAS como Bootstrap, Jquery, e outras bibliotecas visuais, para isso incluimos entre as tags <HEADER> e </HEADER>
o seguinte conteúdo:
<?php require_once "_dependencies.php"; ?> <title><?php echo $_SESSION['title']; ?></title>
E dentro da tag <BODY> e </BODY>
incluimos o conteúdo abaixo:
<nav class="navbar sticky-top navbar-expand-lg navbar-light bg-light"> <?php include "_menu.php"; ?> </nav>
O conteúdo do arquivo _dependencies.php é somente um conteúdo HTML que importa todos os CSS e JS externos das respectivas CDNs ou de qualquer lugar, facilitando alterações de versão e manutenção futura, e consiste inicialmente em:
<?php /** * BRASAP FRAMEWORK DEPENDENCIE FILE * - INSIRA AS DEPENDÊNCIAS DE JAVASCRIPT / CSS GLOBAIS * php version 7.4.3 * * @category PHP_SimplifiedFrameworkNoOOP * @package BRASAP_AUTOLOADING * @author Romeu Gomes de Sousa <[email protected]> * @license GNU GPL 3.0 - Livre uso e Distribuição * @version GIT: * @link https://brasap.com.br */ ?> <link rel="shortcut icon" href="favicon.png" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700&display=swap"/> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" type="text/css"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.19/css/dataTables.bootstrap4.min.css" integrity="sha256-F+DaKAClQut87heMIC6oThARMuWne8+WzxIDT7jXuPA=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/7.33.1/sweetalert2.min.css" integrity="sha512-LhZScx/m/WBAAHyiPnM+5hcsmCMjDDOgOqoT9wyIcs/QUPm6YxVNGZjQG5iP8dhWMWQAcUUTE3BkshtrlGbv2Q==" crossorigin="anonymous" /> <link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.css"> <link rel="stylesheet" href="<?php echo URL; ?>css/bootstrap-custom.css"> <link href="<?php echo URL; ?>css/global.css" rel="stylesheet"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.bundle.min.js" type="text/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@9"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.mask/1.14.16/jquery.mask.min.js" integrity="sha256-Kg2zTcFO9LXOc7IwcBx1YeUBJmekycsnTsq2RuFHSZU=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js" integrity="sha256-sPB0F50YUDK0otDnsfNHawYmA5M0pjjUf4TvRJkGFrI=" crossorigin="anonymous"></script> <script type="text/javascript" src="<?php echo URL; ?>js/main.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.19/js/jquery.dataTables.min.js" integrity="sha256-t5ZQTZsbQi8NxszC10CseKjJ5QeMw5NINtOXQrESGSU=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.19/js/dataTables.bootstrap4.min.js" integrity="sha256-hJ44ymhBmRPJKIaKRf3DSX5uiFEZ9xB/qx8cNbJvIMU=" crossorigin="anonymous"></script>
Criando um MENU MULTI-NÍVEL com o PHP e BOOTSTRAP
O Arquivo que produz o MENU chama-se _menu.php e o seu conteúdo é o mais importante neste momento, pois para que ele funcione, usamos um recurso novo para nós que é a RECURSIVIDADE, muitos de vocês podem comentar que a função submenu()
possui saídas HTML dentro dela, e que isso não é uma boa prática, porém, para entender-mos como funciona a recursividade, este exemplo funciona melhor, seu código é:
<?php /** * BRASAP FRAMEWORK MENU * Cria um MENU DE NAVEGAÇÃO com base nas credenciais do Usuário * php version 7.4.3 * * @category PHP_SimplifiedFrameworkNoOOP * @package BRASAP_MAIN * @author Romeu Gomes de Sousa <[email protected]> * @license GNU GPL 3.0 - Livre uso e Distribuição * @version GIT: * @link https://brasap.com.br */ ?> <a class="navbar-brand" href="#">ServJa</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#main_nav"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="main_nav"> <ul class="navbar-nav"> <?php $menus = menu(0); foreach ($menus as $menu) { if ($menu['filhos']==0) { //SE NÃO TEM FILHO echo "<li class='nav-item'><a class='nav-link' href='".URL.$menu['endpoint']."'>". "<i class='".$menu['icone']."'></i> ".$menu['menu']."</a></li>"; } else { echo "<li class='nav-item dropdown'> <a class='nav-link dropdown-toggle' href='".URL.$menu['endpoint']."' data-toggle='dropdown'>"."<i class='".$menu['icone']."'></i> ". $menu['menu']."</a>"; submenu($menu['id']); } } ?> </ul> </div>
A função menu()
nos devolve um ARRAY contendo os endpoints que o usuário ou perfil possui acesso, no nosso caso, estamos controlando somente por PERFIL e não pelo usuário em sí, porém, é simples mudar isso, bastando-se modificar o campo perfil da tabela para acomodar os dados de ID do usuário, e as chamadas passaram a responder dessa maneira. Quando o tipo de menu, possui um submenu, este é incluído pela chamada a função submenu()
que permite que seja passado um parâmetro para interpretar aquela chamada como sendo um menu de nível 0, ou um submenu, dessa maneira, ela acrescenta a CLASS do Bootstrap que abre os submenus de maneira automática, e permite, que pelo ID passado, mesmo um submenu possa ser incluído em uma página como um menu de nível 0 na lateral ou qualquer outra forma de uso.
Uma característica dessa função, é que ela pode ser chamada de dentro dela mesma, criando um menu aninhado de multiníveis, bastando-se que a tabela onde os menus são salvos, tenha as referências de qual é o “PAI” dequele menu e pronto.
Como o objetivo deste é só um curso de PHP, não vamos entrar em detalhes dos arquivos CSS e JS contidos nas respectivas pastas, mais no GitHub você pode baixar o projeto no estado completo, e ler o seu conteúdo. Acesse o projeto, CLICANDO AQUI.
Entendendo e criando um CONTROLE MAIN
O main_control.php é nosso controque principal, e é chamado, caso algum método ou controle não existente seja chamado, ou caso nenhum método seja chamado, no arquivo de index.php temos a função que faz a checagem se o CONTROL requisitado na URL existe, se ele existe, passamos o controle para ele, se ele não existe, verificamos se estamos no ambiente de desenvolvimento ou produção/homologação e caso esteja no desenvolvimento, mostramos uma mensagem de ausência de um CONTROL e paramos a execução. Porém, caso esteja na produção ou homologação, passamos para o nosso controle MAIN informando que ele deve devolver uma mensagem de erro 404 – Não Encontrado, também poderíamos tratar outros erros e exceções, e vamos fazendo isso no decorrer das próximas aulas. O conteúdo do arquivo main_control.php na pasta _controls é o seguinte:
<?php /** * BRASAP FRAMEWORK MAIN_CENTROL * Controle Principal, controle para acesso geral/index * php version 7.4.3 * * @category PHP_SimplifiedFrameworkNoOOP * @package BRASAP_MAIN * @author Romeu Gomes de Sousa <[email protected]> * @license GNU GPL 3.0 - Livre uso e Distribuição * @version GIT: * @link https://brasap.com.br */ $perfil_usuario = logado(); if (isset($route[1]) && file_exists(_FS."_views/_".$_SESSION['control'].'/_'.$route[1]."_view.php") ) { $_SESSION['view'] = $route[1]; } else { $_SESSION['view'] = 'index'; }
Simplesmente, verificamos se o arquivo da view requisitada existe, e se ela existir, passamos para o model (index.php) que ele deve continuar e abrir aquela view, caso ela não exista, abrimos a view padrão que é a index, que contém a página inicial, e que é acessível por qualquer usuário do site.
O conteúdo da view main_index_view.php é somente um código HTML no nosso caso, é somente um texto apresentando o projeto, e seu conteúdo atual é:
<h1>ServJa - Plataforma de Serviços</h1> <p>Este projeto está sendo desenvolvido, como parte do CURSO DE PHP do site <a href='https://brasap.com.br'>Brasap.com.br</a> e quando concluído estara disponível para dowload ou implementação neste endereço. Qualquer dúvida ou sugestão, entre em contato conosco.<br> Caso tenha interesse em patrocinar este projeto, acesse a página dele no <a href='https://github.com/RomeuTMC/ServJa'>ServJa no GitHub</a> e consiste em um portal onde <b>Prestadores de Serviço</b> podem anunciar seus serviços gratuitamente, e o publico pode ver os prestadores em formato de um guia, ou solicitar um orçamento descrevendo as necessidades, ou colocar cotações publicas para os profissionais enviarem propostas.<br> A ideia é ser um sistema completo para aproximar os prestadores de serviço dos consumidores, permitindo uma fácil localização destes conforme a região onde o prestador e o cliente atuam.<br> <h2>Maiores detalhes</h2> <p>Se você quiser acompanhar o curso e ver todas as etapas para a construção deste e de outros sistemas, baseados na linguagem PHP acesse <a href='https://www.brasap.com.br/categorias/cursos-online/curso-php7/'> CURSO PHP 7 - Do Básico ao Avançado</a>.<br> Para manual de uso ou de instalação deste projeto acesse a página do GitHub e acesse o WIKI do Projeto com todos os detalhes.
Para finalizar, inserimos o arquivo _footer.php que remove as variáveis não usadas da memória, fecha as conexões com o banco de dados, e faz o log de saída em um arquivo, gerando o _out() com o status 200 de que foi finalizado com sucesso. Neste ponto, inserimos uma função gc_collect_cycles()
que força a limpeza de memória do PHP pela coleta automática, destruindo variáveis não mais usadas e dados desnecessários da memória do servidor, seu conteúdo é:
<?php /** * BRASAP FRAMEWORK CLOSEUP FILE * Fecha as conexões ao Banco de dados, faz a saída de todo o conteúdo * com a saída OUT, que gera o LOG de saída e dados de DEBUG/ESTATISTICA * php version 7.4.3 * * @category PHP_SimplifiedFrameworkNoOOP * @package BRASAP_STARTUP * @author Romeu Gomes de Sousa <[email protected]> * @license GNU GPL 3.0 - Livre uso e Distribuição * @version GIT: * @link https://brasap.com.br */ $db->prepare('KILL CONNECTION_ID()'); $db->conection = null; $db = null; unset($db); if (AMBIENTE != 'DEVELOPER') { unset($_POST); unset($_GET); unset($_FILES); unset($_REQUEST); unset($_SERVER); unset($_ENV); } gc_collect_cycles(); _out("Finalizado com Sucesso", 200); ?>
Se você estiver com o modo DEVELOPER habilitado, a saída vai mostrar um resumo com estatística básica de performance do PHP e do FRAMEWORK, como tempos de execução da request, dados mantidos na sessão, uso de memória, e a saída fica após a saída da nossa view, após o término do script e a saída do browser é similar a esta, sendo diferente conforme o uso e o estádo do servidor:
Finalizado com Sucesso Request Method:GET MEMORY USAGE: 475.27Kb / 2.10Mb SESSION================= Array ( [login] => 0 [server] => http://127.0.0.1/ServJa/ [SSID] => SERVJA5db6208d67ff9972e4a43fe236be0146 [rtStart] => 1611514253.7711 [memoryStart] => MEMORY USAGE: 467.66Kb / 2.10Mb [route] => Array ( [0] => 0 ) [control] => main [title] => ServJa - Portal de Serviços [view] => index [rtStop] => 1611514253.7927 [memoryStop] => MEMORY USAGE: 473.94Kb / 2.10Mb [exec_time] => 0.022 - Segundos [HTTP_ERRO] => 200 [MENSAGEM] => Finalizado com Sucesso )
Esses dados, mostram que o PHP está usando um total de 2.1Mb de memória, e nosso script usa em média 475.27Kb e demorou cerca de 0.022 segundos para ser processado (este é o tempo de processamento do PHP) otempo da REQUEST total pode ser visto no browser e depende de recursos como velocidade de conexão ao servidor, e congestionamento da rede, não tendo muito valor como estatística de uso do script, pois os tempos das CDNs e de outros conteúdos externos como imagens, css, js, entre outros também é considerado.
Na próxima aula, vamos aprender outras funções do PHP como funções específicas para conexão ao banco de dados, e funções de HASH para manipulação de senhas e controles de acesso. Até lá.