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