CRUD Bootstrap 5 Mysql e Codeigniter 4 Lista de tarefas parte 3

Parte 3 - Criação da tabela e testes de login

Para testarmos o login vamos criar uma pequena table como abaixo. Todas as senhas de teste são 123456

Não se esqueça de configurar o banco de dados. 

-- phpMyAdmin SQL Dump
-- version 4.9.5deb2
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Tempo de geração: 23-Jul-2023 às 00:09
-- Versão do servidor: 5.7.34
-- versão do PHP: 7.4.33

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Banco de dados: `todo`
--

-- --------------------------------------------------------

--
-- Estrutura da tabela `users`
--

CREATE TABLE `users` (
  `id` int(11) UNSIGNED NOT NULL,
  `name` varchar(128) NOT NULL,
  `email` varchar(240) NOT NULL,
  `password` varchar(240) NOT NULL,
  `status` tinyint(1) NOT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `deleted_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Extraindo dados da tabela `users`
--

INSERT INTO `users` (`id`, `name`, `email`, `password`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES
(1, 'gilberto 1', 'email1@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL),
(2, 'pedro 2', 'email2@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL),
(3, 'maria 3', 'email3@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL),
(4, 'jose 4', 'email4@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL),
(5, 'ana 5', 'email5@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL),
(6, 'Bastião 6', 'email6@teste.com', '$2y$10$9wQKrMuhGMZ85H6oCBQssORWOsf1w/Fy5DF.Igo7Zm/t6TFTd/Sq.', 1, '2023-07-23 02:30:02', NULL, NULL);

--
-- Índices para tabelas despejadas
--

--
-- Índices para tabela `users`
--
ALTER TABLE `users`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `email` (`email`);

--
-- AUTO_INCREMENT de tabelas despejadas
--

--
-- AUTO_INCREMENT de tabela `users`
--
ALTER TABLE `users`
  MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Acesse modules/Auth/Controllers/Auth.php e cole este código

<?php

namespace Modules\Auth\Controllers;

use Modules\Todo\Main\Controllers\BaseController;

class Auth extends BaseController
{
    protected $helpers = ['form', 'html'];

    public function index()
    {
        return view('Modules\Auth\Views\login');
    }
    
    /*
    // teste para encriptar a senha...
    public function encriptar($str)
    {
        return password_hash($str, PASSWORD_DEFAULT);
    }
    */


    public function login()
    {
        if ($_POST) {

            $authModel = model('Modules\Auth\Models\AuthModel', false);

            $post = $this->request->getPost();            

            $auth = $authModel->chkLogin([
                'email' => $post['email'],
                'password' => $post['password'],
            ]);

            if ($auth != false && $auth->id > 0) {
                // O login foi efetuado com sucesso.
                session()->set([
                    'isLoggedIn' => true,
                    'ip_login' => \Config\Services::request()->getIPAddress(),
                    'key' => PRIVATE_KEY, // Constants
                    'userData' => [
                        'name' => (string) $auth->name,
                        'id' => (int) $auth->id,
                        'email' => $auth->email,
                        'isAdmin' => false
                    ]
                ]);

                return redirect()->to(base_url('todo/dashboard'));
            }
        }
    }

    public function logout()
    {        
        session()->destroy();
        return redirect()->to(site_url('auth'));
    }
}

Acesse modules/Auth/Views/login.php e atualize a view login.php

<?= $this->extend('Modules\Todo\Main\Views\external_template'); ?>

<?= $this->section('css'); ?>
<?= $this->endSection(); ?>

<?= $this->section('content'); ?>

<?= form_open(site_url('auth/login')); ?>
<div class="row">
    <div class="col-12 col-md-6 mx-auto">
        <h1 class="h3 mb-3 fw-normal">Login</h1>
        <div class="form-floating mb-1">
            <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com">
            <label for="email">E-mail</label>
        </div>
        <div class="form-floating bm-1">
            <input type="password" class="form-control" name="password" id="password" placeholder="Password">
            <label for="password">Password</label>
        </div>
        <button class="w-100 btn btn-lg btn-primary mt-2" type="submit">Login</button>
        <p class="mt-5 mb-3 text-muted">© 2023</p>
    </div>
</div>
<?= form_close() ?>

<?= $this->endSection(); ?>

<?= $this->section('scripts'); ?>
<?= $this->endSection(); ?>

Acesse modules/Auth/Models/AuthModel.php e cole o código da model.

<?php

namespace Modules\Auth\Models;

use CodeIgniter\Model;

class AuthModel extends Model
{
    protected $table = 'users';
    protected $primaryKey = 'id';
    protected $returnType = 'object';

    public function chkLogin($data)
    {
        $password = $data['password'];

        $query = $this->query("SELECT id, name,email,password,status FROM users WHERE (email = " . $this->escape($data['email']) . ")");

        if (count($query->getResult()) === 1 && password_verify($password, $query->getRow()->password)) { // deve retornar apenas 1 resultado.
            unset($query->getRow()->senha);
            return $query->getRow();
        }
        return false;
    }
}

Acesse modules/Todo/Main/Controllers/BaseController.php  e atualize.

<?php

namespace Modules\Todo\Main\Controllers;

use CodeIgniter\Controller;
use CodeIgniter\HTTP\CLIRequest;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;

abstract class BaseController extends Controller
{
    /**
     * Instance of the main Request object.
     *
     * @var CLIRequest|IncomingRequest
     */
    protected $request;

    /**
     * An array of helpers to be loaded automatically upon
     * class instantiation. These helpers will be available
     * to all other controllers that extend BaseController.
     *
     * @var array
     */
    protected $helpers = [];
    protected $data = [];
   

    /**
     * Constructor.
     */
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        // Do Not Edit This Line
        parent::initController($request, $response, $logger);
        
        $this->data['userData'] = [            
            'name'=> $this->getUser('name'),
            'email'=> $this->getUser('email')
        ];


        // Preload any models, libraries, etc, here.

        // E.g.: $this->session = \Config\Services::session();
    }

    /**
     * retorna dados do usuario
     * @return string/array
     */
    protected function getUser(?string $field = null)
    {
        $user = service('session')->get('userData');

        if (null === $user) {
            return null;
        }
        return null === $field ? $user : ($user[$field] ?? null);
    }
}

Acesse modules/Todo/Config/Routes.php e atualize as rotas

<?php

// Páginas fora do controle de login

$routes->group('/', ['namespace' => '\Modules\Auth\Controllers'], function ($routes) {

    $routes->get('', 'Auth::index');

    $routes->group('auth', function ($routes) {
        $routes->get('', 'Auth::index');

        // login e logout
        $routes->get('logout', 'Auth::logout');
        $routes->get('login', 'Auth::login');
        $routes->post('login', 'Auth::login');
    });
});

// rotas protegidas
$routes->group('todo', ['namespace' => '\Modules\Todo'], function ($routes) {

    // painel principal
    $routes->get('/', 'Dashboard\Controllers\Dashboard::index');
    $routes->get('dashboard', 'Dashboard\Controllers\Dashboard::index');

    // nossa lista de tarefas
    $routes->group('tasks', function ($routes) {
        $routes->get('', 'Auth::index');
    });
});

Acesse modules/Todo/Dashboard/Controllers/Dashboard.php e atualize o código do dashboard.

<?php

namespace Modules\Todo\Dashboard\Controllers;

use Modules\Todo\Main\Controllers\BaseController;

class Dashboard extends BaseController
{
    protected $helpers = ['form', 'html'];

    public function index()
    {   
        // $this->getUser('id')
        return view('Modules\Todo\Dashboard\Views\index',$this->data);
    }
}

Se você ainda não criou, Crie uma view para o Dashboard.

Acesse ou crie o arquivo em modules/Todo/Dashboard/Views/index.php

<?= $this->extend('Modules\Todo\Main\Views\internal_template'); ?>

<?= $this->section('css'); ?>
<?= $this->endSection(); ?>

<?= $this->section('content'); ?>
<h1>
    Dashboard
</h1>
últimas tarefas...
<?= $this->endSection(); ?>

<?= $this->section('scripts'); ?>
<?= $this->endSection(); ?>

Acesse o template interno em modules/Todo/Main/Views/internal_template.php e atualize.

<!DOCTYPE html>
<html lang="pt-br">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lista de Tarefas</title>
    <?= link_tag(base_url('assets/css/bootstrap.min.css')); ?>
    <?= $this->renderSection('css'); ?>
</head>

<body>
    <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">Tarefas</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="true" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="navbar-collapse collapse " id="navbarCollapse" style="">
                <ul class="navbar-nav me-auto mb-2 mb-md-0">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="#">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Nova tarefa</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Lista de tarefas</a>
                    </li>

                </ul>
                <form class="d-flex" role="search">
                    <input class="form-control me-2" type="search" placeholder="Buscar" aria-label="Search">
                    <button class="btn btn-outline-success" type="submit">buscar</button>
                </form>
            </div>
        </div>
    </nav>

    <div class="container" style="margin-top:100px;">
        <div class="row">
            <div class="d-flex justify-content-between">
                <div>
                    Olá: <strong><?= $userData['name']; ?></strong>
                </div>
                <div class="text-end">
                    Codeigniter Versão <?= CodeIgniter\CodeIgniter::CI_VERSION ?> - <a href="<?= site_url('auth/logout'); ?>">Sair</a>
                </div>
            </div>
            <div class="col-12">
                <hr>
            </div>
        </div>
        <!-- cada view renderiza uma row -->
        <?= $this->renderSection('content'); ?>
    </div>
    <?= script_tag(site_url('assets/js/bootstrap.bundle.min.js')); ?>
    <?= $this->renderSection('scripts'); ?>
</body>

</html>

Resultado esperado ao acessar a rota auth

Após o login (email2@teste.com.br e senha 123456)

Agora podemos partir para o formuláro de cadastro das tarefas...

Ante, porém, vamos criar um helper para nos auxiliar na exibição das mensagens de info, success e error. Utilizaremos as #classes do Bootstrap.

Acesse app/Helpers/ e crie um arquivo alert_helper.php

Vamos criar um helper, chamar ele no controller. Criar a mensagem de erro de login no método login e chamar a função do helper na view de login

<?php
function renderAlerts($arr)
{   
    if ($arr !== null) {
        return '<div class="alert alert-' . $arr['class'] . '">' . $arr['text'] . '</div>';
    }
}

Nosso controller rece mais um helper, o alert

E no else da falha de login colocamos a mensagem.

<?php

namespace Modules\Auth\Controllers;

use Modules\Todo\Main\Controllers\BaseController;

class Auth extends BaseController
{
    protected $helpers = ['form', 'html', 'alert'];

    public function index()
    {
        return view('Modules\Auth\Views\login');
    }   

    public function login()
    {
        if ($_POST) {

            $authModel = model('Modules\Auth\Models\AuthModel', false);

            $post = $this->request->getPost();

            $auth = $authModel->chkLogin([
                'email' => $post['email'],
                'password' => $post['password'],
            ]);

            if ($auth != false && $auth->id > 0) {
                // O login foi efetuado com sucesso.
                session()->set([
                    'isLoggedIn' => true,
                    'ip_login' => \Config\Services::request()->getIPAddress(),
                    'key' => PRIVATE_KEY, // Constants
                    'userData' => [
                        'name' => (string) $auth->name,
                        'id' => (int) $auth->id,
                        'email' => $auth->email,
                        'isAdmin' => false
                    ]
                ]);

                return redirect()->to(base_url('todo/dashboard'));
            } else {
                // em caso de erro.
                session()->setFlashdata('message', ['class' => 'danger', 'text' => 'Ocorreu um erro, verifique seus dados!']);
                //  return view('Modules\Auth\Views\login');
                return redirect()->to(base_url('auth'));
            }
        }
    }

    public function logout()
    {
        session()->destroy();
        return redirect()->to(site_url('auth'));
    }
}

Alguns ajustes na view de login.

<?= $this->extend('Modules\Todo\Main\Views\external_template'); ?>

<?= $this->section('css'); ?>
<?= $this->endSection(); ?>

<?= $this->section('content'); ?>

<?= form_open(site_url('auth/login')); ?>
<div class="row mt-5">
    <div class="col-12 col-md-6 mx-auto">       
        <?php echo renderAlerts(session('message')); ?>
        <h1 class="h3 mb-3 fw-normal">Login</h1>
        <div class="form-floating mb-1">
            <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com" required />
            <label for="email">E-mail</label>
        </div>
        <div class="form-floating bm-1">
            <input type="password" class="form-control" name="password" id="password" placeholder="Password" required />
            <label for="password">Password</label>
        </div>
        <button class="w-100 btn btn-lg btn-primary mt-2" type="submit">Login</button>
        <p class="mt-5 mb-3 text-muted">© 2023</p>
    </div>
</div>
<?= form_close() ?>

<?= $this->endSection(); ?>

<?= $this->section('scripts'); ?>
<?= $this->endSection(); ?>

E o resultado será este.

Próximo passo é a parte 4 - Criar o formulário de cadastro de tarefas, a tabela e validações