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

Criando o #formulario de cadastro de tarefas

Seguindo com nosso #crud #todolist #codeigniter . Vamos criar o formulário de #cadastro de #tarefas com #controller , model e view. Validações (frontend e backend) e a tabela necessária. Você pode obter alguma ajuda ou solicitar o código fonte pelo email: contato@codesnippets.dev.br ou no Twiter @BrCodeSnippets

Para acessar o código fonte: https://www.codesnippets.dev.br/post/crud-bootstrap-5-mysql-e-codeigniter-4-lista-de-tarefas-download

Nossa tabela.

CREATE TABLE `tasks` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) NOT NULL,
 `description` text,
 `hash` varchar(50) DEFAULT NULL,
 `deadline` date DEFAULT NULL,
 `priority` tinyint(4) DEFAULT NULL,
 `status` tinyint(4) DEFAULT '0',
 `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `deleted_at` timestamp NULL DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8

Nosso resultado será algo próximo a isso.

 

How to create a CRUD witn Codeigniter 4

 

Vamos utlizar as classes de validação. Language, Paginação Entidade.

Primeiro vamos atualizar nossas rotas.

Acesse modules/Todo/Config/Routes.php. E cole o código abaixo.

<?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');
 // nossas tarefas
 $routes->group('tasks', function ($routes) {
 $routes->get('', 'Tasks\Controllers\Tasks::index');
 $routes->get('all', 'Tasks\Controllers\Tasks::all');
 $routes->get('new', 'Tasks\Controllers\Tasks::new', ['as' => 'task.new']);
 $routes->post('save', 'Tasks\Controllers\Tasks::save');

 $routes->get('edit/(:num)', 'Tasks\Controllers\Tasks::edit/$1');
 $routes->get('delete/(:num)', 'Tasks\Controllers\Tasks::delete/$1');
 $routes->get('view/(:num)', 'Tasks\Controllers\Tasks::view/$1');
 $routes->get('close/(:num)', 'Tasks\Controllers\Tasks::close/$1');
 });
});

O controller de tarefas

Acesse modules/Todo/Tasks/Controllers/Tasks.php. E cole o código abaixo.

<?php

namespace Modules\Todo\Tasks\Controllers;

use CodeIgniter\Entity\Entity;
use Modules\Todo\Main\Controllers\BaseController;
use Modules\Todo\Tasks\Models\TaskModel;
use Modules\Todo\Entities\Task;

class Tasks extends BaseController
{
 protected $taskModel;

 public function __construct()
 {
 array_push($this->helpers, 'custom', 'form', 'dates');
 $this->taskModel = new taskModel();
 $this->data['validation'] = \Config\Services::validation();
 }

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

 public function all()
 {
 $this->data['tasks'] = $this->taskModel->paginate();
 $this->data['pager'] = $this->taskModel->pager;
 return view('Modules\Todo\Tasks\Views\all', $this->data);
 }

 public function save()
 {
 if ($this->request->is('post')) {

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

 unset($post['btn_salvar']);

 if ($this->data['validation']->run($post, 'tasks_save')) {

 if ((int) $post['id'] > 0) {
 $task = $this->getByIdOr404($post['id']);
 $task->fill($post);

 if ($task->hasChanged() === false) {
 return redirect()->to('todo/tasks/new')
 ->with('message', lang('FlashMessages.sem_alteracao'));
 }
 }

 $taskNew = new Task($post); 

 /*
 echo '<pre>';
 print_r($taskNew);
 exit;
 */
 if ($this->taskModel->save($taskNew)) {
 //$lastId = $this->taskModel->getInsertID();
 // app/Language/pt-BR/FlashMessages.php
 return redirect()->to('todo/tasks/new')->with('message', lang('FlashMessages.sucesso'));
 }

 /*
 var_dump($post, $task->hasChanged());
 die();
 */
 }

 $this->lastTasks();
 return view('Modules\Todo\Tasks\Views\new', $this->data);
 }
 }

 public function new()
 {
 $this->lastTasks();
 return view('Modules\Todo\Tasks\Views\new', $this->data);
 }

 public function edit($id)
 {
 $id = (int) $id;
 //$this->taskModel->where('id',1)->first(1);
 //string(78) "SELECT * FROM `tasks` WHERE `id` = 1 AND `tasks`.`deleted_at` IS NULL LIMIT 1"

 $this->data['results'] = $this->taskModel->find($id);

 //$this->taskModel->getLastQuery()->getQuery()
 //string(77) "SELECT * FROM `tasks` WHERE `tasks`.`deleted_at` IS NULL AND `tasks`.`id` = 1"

 $this->lastTasks();
 return view('Modules\Todo\Tasks\Views\new', $this->data);
 }

 public function view($id)
 {
 $id = (int) $id;
 $this->data['task'] = $this->getByIdOr404($id);
 return view('Modules\Todo\Tasks\Views\view', $this->data);
 }

 public function delete($id)
 {
 $id = (int) $id;
 $this->getByIdOr404($id);
 // app/Language/pt-BR/FlashMessages.php
 $msg = lang('FlashMessages.deleted_ok');

 if (!$this->taskModel
 ->where(['id' => $id])
 ->delete()) {
 // app/Language/pt-BR/FlashMessages.php
 $msg = lang('FlashMessages.deleted_error');
 }

 return redirect()->to('todo/tasks/new')->with('message', $msg);
 }

 public function close($id)
 {
 $id = (int) $id;
 $this->getByIdOr404($id);
 // app/Language/pt-BR/FlashMessages.php
 $msg = lang('FlashMessages.sucesso');

 if (!$this->taskModel->update($id, ['status' => 1])) {
 // app/Language/pt-BR/FlashMessages.php
 $msg = lang('FlashMessages.close_error');
 }

 return redirect()->to('todo/tasks/new')->with('message', $msg);
 }

 private function lastTasks()
 {
 $this->data['tasks'] = $this->taskModel
 ->orderBy('id')
 ->limit(10)
 ->findAll();
 }

 /**
 * Recupera task por id
 * @param integer $id
 * @return Exception|object
 */
 private function getByIdOr404(int $id = null)
 {

 if (!$id || !$task = $this->taskModel->withDeleted(true)->find($id)) {
 throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound("A tarefa $id não foi encontrada.");
 }

 return $task;
 }
}

A View compartilhada _tasks_table

Acesse modules/Todo/Main/Views/_tasks_table.php. E cole o código abaixo.

<div class="row">
 <div class="col-12">
 <?php if ($tasks) : ?>
 <hr>
 Últimas tarefas...
 <div class="table-responsive">
 <table class="table table-striped">
 <thead class="bg-light">
 <tr>
 <th>
 id
 </th>
 <th>
 nome
 </th>
 <th>
 Prazo final
 </th>
 <th>
 Prioridade
 </th>
 <th class="text-center">
 status
 </th>
 <th class="text-center">
 ações
 </th>
 </tr>
 </thead>
 <tbody>
 <?php foreach ($tasks as $task) : ?>
 <tr>
 <td><?= $task->id; ?></td>
 <td><?= $task->name; ?></td>
 <td><?= isset($task->deadline) ? dataDBtoBRL($task->deadline) : '---'; ?></td>
 <td><?= isset($task->priority) ? priority($task->priority) : ''; ?></td>
 <td class="text-center"><?= isset($task->status) ? status($task->status) : '---'; ?></td>
 <td class="text-center bg-light text-nowrap">
 <?= anchor(site_url('todo/tasks/delete/' . $task->id . ''), 'Excluir', ['class' => 'btn btn-outline-danger btn-sm', 'onClick' => "return confirm('Deseja mesmo excluir este arquivo?');"]); ?>
 <?= anchor(site_url('todo/tasks/edit/' . $task->id . ''), 'Editar', ['class' => 'btn btn-outline-info btn-sm']); ?>
 <?= anchor(site_url('todo/tasks/view/' . $task->id . ''), 'detalhes', ['class' => 'btn btn-outline-secondary btn-sm']); ?>
 <?= anchor(site_url('todo/tasks/close/' . $task->id . ''), 'Finalizar', ['class' => 'btn btn-outline-success btn-sm']); ?>

 </td>
 </tr>
 <?php endforeach; ?>
 </tbody>
 </table>
 </div>
 <?php endif; ?>
 </div>
</div>

As 4 views de tasks (index.php, all.php, new.php e view.php).

Códigos abaixo

modules/Todo/Tasks/Views/all.php

 

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

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

<?= $this->section('content'); ?>
<h1>
 Todas as tarefas...
</h1>

<?= $pager->links('default') ?>

<div class="text-right">
 <small> Páginas: <?= $pager->getPageCount(); ?> - registros <?= $pager->getTotal(); ?>
 </small>
</div>

<?php echo $this->include('Modules\Todo\Main\Views\_tasks_table'); ?>

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

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

 

modules/Todo/Tasks/Views/index.php

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

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

<?= $this->section('content'); ?>
<h1>
 Lista de tarefas
</h1>
tarefas...
<?= $this->endSection(); ?>

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

modules/Todo/Tasks/Views/new.php

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

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

<?= $this->section('content'); ?>
<h1>
 Nova Tarefa
</h1>
<?php
/*
echo '<pre>';
var_dump($results->id);
die();
*/
?>
<?php echo form_open(site_url('todo/tasks/save'), ['id' => 'form', 'autocomplete' => 'off'], ['id' => $results->id ?? set_value('id')]) ?>
<div class="row">
 <div class="col-10">
 <?= form_label(renderLabel($validation->getError('name'), '*Nome')); ?>
 <?= form_input([
 'class' => 'form-control form-control-sm ' . toggleCSSValidOrInvalid($validation->getError('name')),
 'placeholder' => 'Informe nome',
 'type' => 'text',
 'name' => 'name',
 'id' => 'name',
 // 'required' => 'required',
 'value' => $results->name ?? set_value('name')
 ]);
 ?>
 </div>

 <div class="col-2">
 <?= form_label(renderLabel($validation->getError('hash'), '*Auto')); ?>
 <?= form_input([
 'class' => 'form-control form-control-sm ' . toggleCSSValidOrInvalid($validation->getError('hash')),
 'placeholder' => 'Informe nome',
 'type' => 'text',
 'name' => 'hash',
 'id' => 'hash',
 // 'required' => 'required',
 'value' => $results->hash ?? set_value('hash')
 ]);
 ?>
 </div>

 <div class="col-12">
 <?= form_label(renderLabel($validation->getError('description'), '*Descrição')); ?>
 <?= form_textarea([
 'class' => 'form-control form-control-sm' . toggleCSSValidOrInvalid($validation->getError('description')),
 'name' => 'description',
 'placeholder' => 'Descrição da tarefa',
 'id' => 'description',
 // 'required' => 'required',
 'rows' => 4,
 'value' => $results->description ?? set_value('description')
 ]);
 ?>
 </div>

 <div class="col-12 col-sm-6">
 <?= form_label(renderLabel($validation->getError('deadline'), '*Prazo final')); ?>
 <?= form_input([
 'class' => 'form-control form-control-sm' . toggleCSSValidOrInvalid($validation->getError('deadline')),
 'name' => 'deadline',
 'type' => 'date',
 'placeholder' => 'Prazo final',
 'id' => 'deadline',
 // 'required' => 'required', 
 'value' => $results->deadline ?? set_value('deadline')
 ]);
 ?>
 </div>

 <div class="col-12 col-sm-6 ">
 <?= form_label(renderLabel($validation->getError('priority'), '*Prioridade')); ?>
 <?= form_dropdown('priority', [
 '' => '(Selecione)',
 1 => 'Urgente',
 2 => 'Importante',
 3 => 'Media',
 4 => 'Baixa',
 ], $results->priority ?? set_value('priority'), ['id' => 'priority', 'class' => 'form-select form-select-sm' . toggleCSSValidOrInvalid($validation->getError('priority'))]); ?>
 </div>



 <div class="col-12 col-sm-10">
 <?= form_submit('btn_salvar', (isset($results->id) && $results->id > 0) ? 'Atualizar' : 'Salvar', ['class' => 'btn btn-success mt-2 w-100 btn-sm']); ?>
 </div>
 <div class="col-12 col-sm-2">
 <a href="<?= site_url('todo/tasks/new'); ?>" class="btn w-100 btn-sm border mt-2">Nova</a>
 </div>

</div>
<?= form_close() ?>

<div class="row">
 <div class="col-12">
 <?php if ($tasks) : ?>
 <hr>
 Últimas tarefas...
 <div class="table-responsive">
 <table class="table table-striped">
 <thead class="bg-light">
 <tr>
 <th>
 id
 </th>
 <th>
 nome
 </th>
 <th>
 Prazo final
 </th>
 <th>
 Prioridade
 </th>
 <th class="text-center">
 status
 </th>
 <th class="text-center">
 ações
 </th>
 </tr>
 </thead>
 <tbody>
 <?php foreach ($tasks as $task) : ?>
 <tr>
 <td><?= $task->id; ?></td>
 <td><?= $task->name; ?></td>
 <td><?= isset($task->deadline) ? dataDBtoBRL($task->deadline) : '---'; ?></td>
 <td><?= isset($task->priority) ? priority($task->priority) : ''; ?></td>
 <td class="text-center"><?= isset($task->status) ? status($task->status) : '---'; ?></td>
 <td class="text-center bg-light text-nowrap">
 <?= anchor(site_url('todo/tasks/delete/' . $task->id . ''), 'Excluir', ['class' => 'btn btn-outline-danger btn-sm', 'onClick' => "return confirm('Deseja mesmo excluir este arquivo?');"]); ?>
 <?= anchor(site_url('todo/tasks/edit/' . $task->id . ''), 'Editar', ['class' => 'btn btn-outline-info btn-sm']); ?>
 <?= anchor(site_url('todo/tasks/view/' . $task->id . ''), 'detalhes', ['class' => 'btn btn-outline-secondary btn-sm']); ?>
 <?= anchor(site_url('todo/tasks/close/' . $task->id . ''), 'Finalizar', ['class' => 'btn btn-outline-success btn-sm']); ?>

 </td>
 </tr>
 <?php endforeach; ?>
 </tbody>
 </table>
 </div>
 <?php endif; ?>
 </div>
</div>
<?= $this->endSection(); ?>
<?= $this->section('scripts'); ?>
<?= $this->endSection(); ?>

modules/Todo/Tasks/Views/view.php

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

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

<?= $this->section('content'); ?>
<div class="row">
 <div class="col-12">

 <h1>
 Código: <?php echo $task->id; ?>
 </h1>
 <div class="card">
 <div class="card-header">
 Detalhes da tarefa
 </div>
 <div class="card-body">
 <h5 class="card-title"><?php echo $task->name; ?></h5>
 <p class="card-text">
 <?php echo $task->description; ?>
 </p>
 <div class="text-end">
 <?= anchor(site_url('todo/tasks/edit/' . $task->id . ''), 'Editar', ['class' => 'btn btn-outline-info btn-sm']); ?>
 <?= anchor(site_url('todo/tasks/delete/' . $task->id . ''), 'Excluir', ['class' => 'btn btn-outline-danger btn-sm', 'onClick' => "return confirm('Deseja mesmo excluir este arquivo?');"]); ?>

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

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

Entidade Task

modules/Todo/Entities/Task.php

<?php

namespace Modules\Todo\Entities;

use CodeIgniter\Entity\Entity;

class Task extends Entity
{
 protected $dates = ['created_at', 'updated_at', 'deleted_at'];

}

Model taskModel

modules/Todo/Tasks/Models/TaskModel.php

<?php

namespace Modules\Todo\Tasks\Models;

use CodeIgniter\Model;
use Modules\Todo\Entities\Task;

class TaskModel extends Model
{
 protected $table = 'tasks';
 protected $primaryKey = 'id';
 protected $returnType = Task::class; // 'array' ou 'object';
 protected $useSoftDeletes = true;
 protected $protectFields = true;
 protected $allowedFields = [
 'name',
 'description',
 'hash',
 'deadline',
 'priority',
 'status'
 ];

 // Dates
 protected $useTimestamps = false;

 // Validation 
 protected $skipValidation = false;

 // Callbacks
 protected $allowCallbacks = true;
}

Validações

Tradução das mensagens de erro. Precisa baixar o pacote pt-BR para a versão do Codeigniter 4 

app/Language/pt-BR/Validation.php

<?php

/**
 * This file is part of the CodeIgniter 4 framework.
 *
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

// Validation language settings
return [
	// Core Messages
	'noRuleSets' => 'Nenhum conjunto de regras especificado na configuração de Validação.',
	'ruleNotFound' => '{0} não é uma regra válida.',
	'groupNotFound' => '{0} não é um grupo de regras de validação.',
	'groupNotArray' => 'O grupo de regras {0} deve ser um array.',
	'invalidTemplate' => '{0} não é um template de Validation válido.',

	// Rule Messages
	'alpha' => 'O campo {field} pode conter apenas caracteres alfabéticos.',
	'alpha_dash' => 'O campo {field} pode conter apenas caracteres alfa-numéricos, sublinhados, e traços.',
	'alpha_numeric' => 'O campo {field} pode conter apenas caracteres alfa-numéricos.',
	'alpha_numeric_punct' => 'O campo {field} pode conter apenas caracteres alfa-numéricos, espaços, e ~ !# $ % & * - _ + = | : . caracteres.',
	'alpha_numeric_space' => 'O campo {field} pode conter apenas caracteres alfa-numéricos e espaços.',
	'alpha_space' => 'O campo {field} pode conter apenas caracteres alfabéticos e espaços.',
	'decimal' => 'O campo {field} deve conter um número decimal.',
	'differs' => 'O campo {field} deve ser diferente do campo {param}.',
	'equals' => 'O campo {field} deve ser exatamente: {param}.',
	'exact_length' => 'O campo {field} deve conter exatamente {param} caracteres no tamanho.',
	'greater_than' => 'O campo {field} deve conter um número maior que {param}.',
	'greater_than_equal_to' => 'O campo {field} deve conter um número maior ou igual a {param}.',
	'hex' => 'O campo {field} pode conter apenas caracteres hexadecimais.',
	'in_list' => 'O campo {field} deve ser um desses: {param}.',
	'integer' => 'O campo {field} deve conter um número inteiro.',
	'is_natural' => 'O campo {field} deve conter apenas dígitos.',
	'is_natural_no_zero' => 'O campo {field} deve conter apenas dígitos e deve ser maior que zero.',
	'is_not_unique' => 'O campo {field} deve conter um valor já existente no banco de dados.',
	'is_unique' => 'O campo {field} deve conter um valor único.',
	'less_than' => 'O campo {field} deve conter um número menor que {param}.',
	'less_than_equal_to' => 'O campo {field} deve conter um número menor ou igual a {param}.',
	'matches' => 'O campo {field} não é igual ao campo {param}.',
	'max_length' => 'O campo {field} não pode exceder {param} caracteres no tamanho.',
	'min_length' => 'O campo {field} deve conter pelo menos {param} caracteres no tamanho.',
	'not_equals' => 'O campo {field} não pode ser: {param}.',
	'not_in_list' => 'O campo {field} não deve ser um desses: {param}.',
	'numeric' => 'O campo {field} deve conter apenas números.',
	'regex_match' => 'O campo {field} não está no formato correto.',
	'required' => 'Campo obrigatório*', //'O campo {field} é requerido.',
	'required_with' => 'O campo {field} é requerido quando {param} está presente.',
	'required_without' => 'O campo {field} é requerido quando {param} não está presente.',
	'string' => 'O campo {field} deve ser uma string válida.',
	'timezone' => 'O campo {field} deve ser uma timezone válida.',
	'valid_base64' => 'O campo {field} deve ser uma string base64 válida.',
	'valid_email' => 'O campo {field} deve conter um endereço de e-mail válido.',
	'valid_emails' => 'O campo {field} deve conter todos os endereços de e-mails válidos.',
	'valid_ip' => 'O campo {field} deve conter um IP válido.',
	'valid_url' => 'O campo {field} deve conter uma URL válida.',
	'valid_date' => 'O campo {field} deve conter uma data válida.',
	'isValidDateAndFuture' => 'Data inválida',

	// Credit Cards
	'valid_cc_num' => '{field} não parece ser um número de cartão de crédito válido.',

	// Files
	'uploaded' => '{field} não é um arquivo de upload válido.',
	'max_size' => '{field} é um arquivo muito grande.',
	'is_image' => '{field} não é um arquivo de imagem válida do upload.',
	'mime_in' => '{field} não tem um tipo mime válido.',
	'ext_in' => '{field} não tem uma extensão de arquivo válida.',
	'max_dims' => '{field} não é uma imagem, ou ela é muito larga ou muito grande.',
];

Validações comuns de tarefas

app/Config/Validation.php

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Validation\StrictRules\CreditCardRules;
use CodeIgniter\Validation\StrictRules\FileRules;
use CodeIgniter\Validation\StrictRules\FormatRules;
use CodeIgniter\Validation\StrictRules\Rules;

class Validation extends BaseConfig
{
 // --------------------------------------------------------------------
 // Setup
 // --------------------------------------------------------------------

 /**
 * Stores the classes that contain the
 * rules that are available.
 *
 * @var string[]
 */
 public array $ruleSets = [
 \Modules\Todo\Config\MyRules::class,
 Rules::class,
 FormatRules::class,
 FileRules::class,
 CreditCardRules::class,
 ];

 /**
 * Specifies the views that are used to display the
 * errors.
 *
 * @var array<string, string>
 */
 public array $templates = [
 'list' => 'CodeIgniter\Validation\Views\list',
 'single' => 'CodeIgniter\Validation\Views\single',
 ];

 // --------------------------------------------------------------------
 // Rules
 
 public $tasks_save = [
 'name' => 'required',
 'description' => 'required',
 'deadline'=> 'required|isValidDateAndFuture',
 'priority' => 'required|is_natural_no_zero'
 ];

 // --------------------------------------------------------------------
}

Regras customizadas

modules/Todo/Config/MyRules.php. Precisa criar o arquivo MyRules.php

<?php

namespace Modules\Todo\Config;

class MyRules
{
 function isValidDateAndFuture($date)
 {
 // Obtém a data de hoje (sem levar em conta os minutos)
 $today = new \DateTime('today');

 // Tenta criar um objeto DateTime a partir da data fornecida
 $inputDate = \DateTime::createFromFormat('Y-m-d', $date);

 // Verifica se a data foi criada corretamente e se é válida
 if (!$inputDate || $inputDate->format('Y-m-d') !== $date) {
 return false; // A data é inválida
 }

 // Compara a data de hoje com a data fornecida (apenas o dia é considerado)
 if ($inputDate < $today) {
 return false; // A data não é maior que hoje
 }

 return true; // A data é válida e maior que hoje
 }
 
}

Criamos um template padrão estilo #bootstrap para customizar a paginação me modules/Todo/Main/Views/Pagers/default.php

<?php

use CodeIgniter\Pager\PagerRenderer;

/**
 * @var PagerRenderer $pager
 */
$pager->setSurroundCount(3);
?>

<nav aria-label="<?= lang('Pager.pageNavigation') ?>" class="text-end">
 <ul class="pagination mt-2 justify-content-end">
 <?php if ($pager->hasPrevious()) : ?>
 <li class="page-item">
 <a class="page-link" href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>">
 <span aria-hidden="true"><?= lang('Pager.first') ?></span>
 </a>
 </li>
 <li class="page-item">
 <a class="page-link" href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang('Pager.previous') ?>">
 <span aria-hidden="true"><?= lang('Pager.previous') ?></span>
 </a>
 </li>
 <?php endif ?>

 <?php foreach ($pager->links() as $link) : ?>
 <li <?= $link['active'] ? 'class="disabled page-item active fw-bolder "' : '' ?>>
 <a class="page-link" href="<?= $link['uri'] ?>">
 <?= $link['title'] ?>
 </a>
 </li>
 <?php endforeach ?>
 <?php
 /*
 $pager->getNext() por -> getNextPage
 $pager->getPrevious() por -> getPreviousPage
 */
 ?>
 <?php if ($pager->hasNext()) : ?>
 <li class="page-item">
 <a class="page-link" href="<?= $pager->getNextPage() ?>" aria-label="<?= lang('Pager.next') ?>">
 <span aria-hidden="true"><?= lang('Pager.next') ?></span>
 </a>
 </li>
 <li class="page-item">
 <a class="page-link" href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>">
 <span aria-hidden="true"><?= lang('Pager.last') ?></span>
 </a>
 </li>
 <?php endif ?>
 </ul>
</nav>

Helpers para nos ajudar com as Views.

app/Helpers/alert_helper.php. Este Helper nos ajusa com os #alerts do #Boostrap .

<?php
function renderAlerts($arr)
{
 $return = '';

 if ($arr !== null) {
 $return .= '<div class="alert alert-' . $arr['class'] . ' alert-dismissible fade show">';
 $return .= '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>';
 $return .= $arr['text'] . '</div>';
 return $return;
 }
}

app/Helpers/custom_helper.php. este Helper auxila na customização de elementos como status e priodade.

<?php

/**
 * Render do label para exibir o erro
 * @param string string
 * @return string
 */
function renderLabel($erro, $field)
{
 return (empty($erro) ? '' . $field . '' : '<small class="text-danger">' . $erro . '</small>');
}

/**
 * classe do CSS is-valid ou is-invalid
 * @param string string
 * @return string
 */
function toggleCSSValidOrInvalid($erro)
{
 return (empty($erro) ? '' : ' is-invalid ');
}

function status($status)
{
 $st = [];

 switch ($status) {
 case 0:
 $st[0] = 'warning';
 $st[1] = 'Pendente';
 break;
 case 1:
 $st[0] = 'success';
 $st[1] = 'Finalizada';
 break;
 case 2:
 $st[0] = 'danger';
 $st[1] = 'Atrasada';
 break;
 case 3:
 $st[0] = 'danger';
 $st[1] = 'Cancelada';
 break;
 case 4:
 $st[0] = 'info';
 $st[1] = 'Indefinido';
 break;
 }
 return '<span class="d-block text-center text-' . $st[0] . '">' . $st[1] . '</span>';
}

function priority($priority)
{
 $st = [];

 switch ($priority) {

 case 1:
 $st[0] = 'danger';
 $st[1] = 'Urgente';
 break;
 case 2:
 $st[0] = 'danger';
 $st[1] = 'Importante';
 break;
 case 3:
 $st[0] = 'warning';
 $st[1] = 'Media';
 break;
 case 4:
 $st[0] = 'info';
 $st[1] = 'baixa';
 break;
 }
 return '<span class="badge text-bg-' . $st[0] . ' d-block text-center">' . $st[1] . '</span>';
}

app/Helpers/dates_helper.php. Um Helper para lidar com datas.

<?php
//app/Helpers/dates_helper.php
function dataDBtoBRL($str)
{
 if (strlen($str) >= 8 && trim($str) != "0000-00-00") {
 $dataArray = explode("-", $str);
 return $dataArray[2] . '/' . $dataArray[1] . '/' . $dataArray[0]; // 2016-01-30
 }

 return ""; // vazio

}

Ajustes no  modules/Todo/Dashboard/Controllers/Dashboard.php

<?php

namespace Modules\Todo\Dashboard\Controllers;

use Modules\Todo\Main\Controllers\BaseController;
use Modules\Todo\Tasks\Models\TaskModel;

class Dashboard extends BaseController
{
 protected $taskModel;

 public function __construct()
 {
 array_push($this->helpers,'text','dates','custom');
 $this->taskModel = new taskModel(); 
 }

 public function index()
 {
 // $this->getUser('id')
 
 $this->data['tasks'] = $this->taskModel
 ->orderBy('id')
 ->limit(20)
 ->findAll();

 return view('Modules\Todo\Dashboard\Views\index',$this->data);
 }
}

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...

<?php echo $this->include('Modules\Todo\Main\Views\_tasks_table'); ?>
<?= $this->endSection(); ?>

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

Com isso já podemos ver alguns registros da tabela de dados tasks.

Dump de tasks.

-- phpMyAdmin SQL Dump
-- version 4.9.5deb2
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Tempo de geração: 24-Jul-2023 às 16:47
-- 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 `tasks`
--

CREATE TABLE `tasks` (
 `id` int(11) NOT NULL,
 `name` varchar(255) NOT NULL,
 `description` text,
 `hash` varchar(50) DEFAULT NULL,
 `deadline` date DEFAULT NULL,
 `priority` tinyint(4) DEFAULT NULL,
 `status` tinyint(4) DEFAULT '0',
 `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `deleted_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Extraindo dados da tabela `tasks`
--

INSERT INTO `tasks` (`id`, `name`, `description`, `hash`, `deadline`, `priority`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES
(1, 'aaa 2 2 ', 'aaa2', '123', NULL, NULL, 0, '2023-07-24 12:08:19', '2023-07-24 17:11:37', '2023-07-24 17:11:37'),
(2, 'Teste 001', 'Teste 001', '123', '2023-07-25', 4, 1, '2023-07-24 12:09:04', '2023-07-24 18:57:18', NULL),
(3, 'Tarefa --', 'aaa', '123', '2023-08-01', 1, 1, '2023-07-24 12:20:31', '2023-07-24 19:04:51', NULL),
(4, 'Verificar servidor', 'Verificar servidor', '123', '2023-08-04', 3, 0, '2023-07-24 12:29:50', '2023-07-24 18:45:49', NULL),
(5, 'hfghfgh', 'fghfg', '', NULL, NULL, 0, '2023-07-24 12:29:55', '2023-07-24 16:41:27', NULL),
(6, 'fghfgh', 'gfhfg', '', NULL, NULL, 0, '2023-07-24 12:30:10', '2023-07-24 16:41:30', NULL),
(7, 'sdfsd', 'fsdfsd', '', NULL, NULL, 0, '2023-07-24 12:33:09', '2023-07-24 16:41:33', NULL),
(8, 'ghgf', 'hgfhfg', '', NULL, NULL, 0, '2023-07-24 13:21:31', '2023-07-24 16:41:36', NULL),
(9, 'Porque usamos?', 'É um fato conhecido de todos que um leitor se distrairá com o conteúdo de texto legível de uma página quando estiver examinando sua diagramação. A vantagem de usar Lorem Ipsum é que ele tem uma distribuição normal de letras, ao contrário de \"Conteúdo aqui, conteúdo aqui\", fazendo com que ele tenha uma aparência similar a de um texto legível. Muitos softwares de publicação e editores de páginas na internet agora usam Lorem Ipsum como texto-modelo padrão, e uma rápida busca por \'lorem ipsum\' mostra vários websites ainda em sua fase de construção. Várias versões novas surgiram ao longo dos anos, eventualmente por acidente, e às vezes de propósito (injetando humor, e coisas do gênero).', '', NULL, NULL, 0, '2023-07-24 14:32:53', '2023-07-24 15:11:40', NULL),
(10, '222', '333', '', NULL, NULL, 0, '2023-07-24 14:38:40', '2023-07-24 14:38:40', NULL),
(11, 'aaa2 a', 'aaa', '123', NULL, NULL, 0, '2023-07-24 14:39:16', '2023-07-24 14:39:16', NULL),
(12, 'O que é Lorem Ipsum?', 'Lorem Ipsum é simplesmente uma simulação de texto da indústria tipográfica e de impressos, e vem sendo utilizado desde o século XVI, quando um impressor desconhecido pegou uma bandeja de tipos e os embaralhou para fazer um livro de modelos de tipos. Lorem Ipsum sobreviveu não só a cinco séculos, como também ao salto para a editoração eletrônica, permanecendo essencialmente inalterado. Se popularizou na década de 60, quando a Letraset lançou decalques contendo passagens de Lorem Ipsum, e mais recentemente quando passou a ser integrado a softwares de editoração eletrônica como Aldus PageMaker.', '', NULL, NULL, 0, '2023-07-24 15:05:29', '2023-07-24 15:05:29', NULL),
(13, 'Tarefa teste 001', 'Tarefa teste 001', '', NULL, NULL, 0, '2023-07-24 17:24:49', '2023-07-24 17:24:49', NULL);

--
-- Índices para tabelas despejadas
--

--
-- Índices para tabela `tasks`
--
ALTER TABLE `tasks`
 ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de tabelas despejadas
--

--
-- AUTO_INCREMENT de tabela `tasks`
--
ALTER TABLE `tasks`
 MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=14;
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 */;

 

Você pode obter alguma ajuda ou solicitar o código fonte pelo email: contato@codesnippets.dev.br ou no Twiter @BrCodeSnippets

 

 

 


Veja nossa página de indicação de cursos https://www.codesnippets.dev.br/cursos