Índice de contenido
Bootstrap 5 es la última versión del popular framework web basado en componentes conocido como Bootstrap. Bootstrap es un conjunto de herramientas de diseño web que permite a los desarrolladores crear sitios web y aplicaciones web de manera rápida y eficiente y personalizar la experiencia con relativa facilidad. Bootstrap 5 es la versión más reciente de la biblioteca, y cuenta con nuevas funcionalidades y mejoras en comparación con las versiones anteriores.
Vamos a aprovechar los registros que creamos antes mediante un formulario con carga de archivos, para poder mostrar los registros en un listado.
Definir un estilo para los distintos módulos de nuestra aplicación es un proceso común, ya sea que quieras crear un estilo personalizado creado por ti desde cero o alguna toolkit existente como Tailwind o en este caso Bootstrap; en este libro, vamos a utilizar Bootstrap en lugar de otras soluciones similares ya que, es una solución con mucho tiempo en el mercado, con buena documentación y podemos utilizar componentes existentes; por lo tanto, implementar Bootstrap en nuestro sitio resulta muy fácil de aplicar y por lo tanto de entender y puedes utilizar los pasos que vamos a ver en este capítulo como plantilla en caso de que quiera usar otra solución para manejar el estilo en tu aplicación.
Recuerda que esto no es una guía sobre Bootstrap 5, se da por hecho de que el lector tiene conocimientos al menos básicos en el uso de esta tecnología.
Instalar
Comencemos instalando Bootstrap; al ser CodeIgniter un framework sin ninguna vinculación con Node, debemos de usar las CDN de Bootstrap la cual podemos obtener desde la página oficial:
Presionas sobre "Download" y luego nuevamente sobre "Download" en el siguiente apartado:

Bootstrap, al ser un framework web del lado del cliente, usa CSS y JavaScript para poder utilizar cualquier componente o funcionalidad de Bootstrap; una vez descargado el comprimido anterior y generada la carpeta (la cual, para simplificar el proceso, renombramos como bootstrap) necesitamos los siguientes archivos:
bootstrap/css/bootstrap.min.css
bootstrap/js/bootstrap.min.js
Que vamos a copiar dentro de la carpeta public del proyecto en CodeIgniter 4:

Que configuramos en nuestros layouts:
app\Views\Layouts\dashboard.php
app\Views\Layouts\web.php
<!DOCTYPE html>
<html lang="en">
<head>
***
<link rel="stylesheet" href="<?= base_url() ?>/bootstrap/css/bootstrap.min.css">
</head>
<body>
***
<script src="<?= base_url() ?>/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>Con estos archivos cargados en el layout, podemos utilizar Bootstrap en cualquier vista que use dicho layout, que, en el caso de nuestra aplicación, sería todo el módulo de gestión.
Ya con esto, podemos usar Bootstrap en nuestro proyecto; al tener un par de layouts que conforman toda la aplicación, podemos configurar los archivos necesarios de Bootstrap para poder usarlo en todas las vistas; que serían el CSS y el JS de Bootstrap.
Si vamos al navegador, deberíamos de ver cierto estilo aplicado:

IMPORTANTE: Recuerda verificar en la consola de desarrolladores si no tienes algún enlace roto:
Componentes de Bootstrap
Bootstrap es un framework basado en componentes, tenemos componentes para los elementos más importantes en el desarrollo web, como botones, contenedores, menús de navegación, modals entre otros:
https://getbootstrap.com/docs/5.3/components/
Estos componentes, los podemos personalizar a nivel de estilos, pero, por defecto ya existe un estilo aplicado; en este apartado, vamos a emplear varios componentes de Bootstrap para darle estilo al módulo de dashboard.
Container
Comencemos evitando que los formularios y listados ocupen toda la página, para esto, vamos a usar un componente de container:
app\Views\Layouts\dashboard.php
<div class="container">
<h1><?= $this->renderSection('header') ?></h1>
<?= view('partials/_session') ?>
<?= $this->renderSection('contenido') ?>
</div>Tabla
Vamos a definir un estilo para cada una de las tablas; para ello, tenemos un estilo base usando la clase table:
app\Views\dashboard\pelicula\index.php
app\Views\dashboard\etiqueta\index.php
app\Views\dashboard\categoria\index.php
<table class="table">
***Y tendremos:

Carta
El componente de carta, podemos utilizarlo para muchos desarrollos, como para mostrar detalles, listados o en este caso, como un contenedor; el componente de carta, define un color sólido por defecto blanco con las esquinas redondeadas y un sombreado, que podemos aplicar desde el layout. Para que las tablas y formularios tengan este contenedor; con la clase de card-body, se aplica un PADDING.
La mayoría de los componentes en Bootstrap funcionan de una manera similar, no son más que un conjunto de HTML anidados con unas clases específicas para que luzcan como nosotros queramos, el componente de carta también tiene otras clases que podemos usar por ejemplo la de card-footer para definir, por ejemplo, botones de acción, que en nuestro caso, no sería necesario:
app\Views\Layouts\dashboard.php
<div class="container">
<div class="card">
<div class="card-header">
<h1><?= $this->renderSection('header') ?></h1>
</div>
<div class="card-body">
<?= view('partials/_session') ?>
<?= $this->renderSection('contenido') ?>
</div>
</div>
</div>Y tendremos:

Menú de navegación
Ahora, vamos a crear un header para la navegación, que al igual que el componente de carta, tiene una organización específica para que funcione como se espera; al menos, debemos de definir las siguientes clases:
Emplearemos las clases típicas que tenemos en Bootstrap para crear un navbar básico como:
- navbar-brand para colocar el nombre o marca de la aplicación.
- navbar-nav y nav-link para los enlaces.
- collapse y navbar-collapse para el posicionamiento.
En la siguiente distribución:
app\Views\Layouts\dashboard.php
<body>
<nav class="navbar navbar-expand-lg mb-3">
<div class="container-fluid">
<a class="navbar-brand">Code4</a>
<div class="navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item">
<a href="<?= base_url() ?>/dashboard/categoria" class="nav-link">Categoría</a>
</li>
<li class="nav-item">
<a href="<?= base_url() ?>/dashboard/pelicula" class="nav-link">Películas</a>
</li>
<li class="nav-item">
<a href="<?= base_url() ?>/dashboard/etiqueta" class="nav-link">Etiquetas</a>
</li>
</ul>
</div>
</div>
</nav>
***Y tendremos:

Paginación
El componente de paginación de Bootstrap luce como el siguiente:
<nav>
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">Previous</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</nav>Tiene 3 bloques principales, el de la página previa, la numeración y la página siguiente.
La paginación, al ser generada internamente por CodeIgniter, tenemos que variar el formato actual; recordemos que la configuración de la paginacion se encuentra en:
app\Config\Pager.php
public $templates = [
'default_full' => 'App\Views\partials\pagination',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head',
];Por defecto, se usa de 'default_full' '; si por ejemplo, quisieras usar la paginación en el formato simple, desde la vista, tendrías que colocar:
<?= $pager->simpleLinks() ?>Si buscamos el código fuente de la página de 'default_full':
vendor\codeigniter4\framework\system\Pager\Views\default_full.php
<?php
use CodeIgniter\Pager\PagerRenderer;
/**
* @var PagerRenderer $pager
*/
$pager->setSurroundCount(2);
?>
<nav aria-label="<?= lang('Pager.pageNavigation') ?>">
<ul class="pagination">
<?php if ($pager->hasPrevious()) : ?>
<li>
<a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>">
<span aria-hidden="true"><?= lang('Pager.first') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getPrevious() ?>" 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="active"' : '' ?>>
<a href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNext()) : ?>
<li>
<a href="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<li>
<a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>">
<span aria-hidden="true"><?= lang('Pager.last') ?></span>
</a>
</li>
<?php endif ?>
</ul>
</nav>Tiene exactamente los mismos 3 bloques (previa, numeración y posterior) como mencionamos antes, por lo tanto, podamos adaptar directamente al componente de Bootstrap; vamos a replicarlo en una página nueva para no modificar archivos dentro de la carpeta vendor usando el estilo de Bootstrap:
app\Views\partials\pagination.php
<?php
use CodeIgniter\Pager\PagerRenderer;
/**
* @var PagerRenderer $pager
*/
$pager->setSurroundCount(2);
?>
<nav aria-label="<?= lang('Pager.pageNavigation') ?>">
<ul class="pagination">
<?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->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>
<?php foreach ($pager->links() as $link) : ?>
<li class="page-item" <?= $link['active'] ? 'class="active"' : '' ?>>
<a class="page-link" href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNext()) : ?>
<li class="page-item">
<a class="page-link" href="<?= $pager->getNext() ?>" 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>Y lo utilizamos:
app\Config\Pager.php
public $templates = [
'default_full' => 'App\Views\partials\pagination',
// 'default_full' => 'CodeIgniter\Pager\Views\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head',
];Con esto, tendremos una paginación como la siguiente:

Creando un listado en CodeIgniter 4
En esta entrada vamos a ver una de las operaciones CRUD fundamentales, la que centraliza el resto de las operaciones CRUD que sería la de listado; puedes emplear cualquier tipo de componente HTML en conjunto con CSS y JavaScript para darle el diseño que tu quieras; pero en nuestro caso de interés nos importa es la parte de atrás, nuestro backend para obtener toda la data que queramos manipular.
En esta entrada vamos a crear una sencilla tabla paginada para ejemplificar cómo podemos crear un listado paginado en CodeIgniter 4; pero puede ser cualquier cosa, aunque el elemento empleado por excelencia para la paginación es una tabla.
En nuestro controlador de Movie que creamos anteriormente para procesar un formulario, podemos crear otra función que por convención siguiendo nuestra ruta de tipo recurso sería la de index, la primera función que devuelve el primer recurso CRUD de nuestra app, la que permite mostrar todos nuestros registros.
Construyendo el listado desde el controlador
Así que aquí podríamos obtener de una todos los registros con la función de all:
$people->asObject()->get()Pero en CodeIgniter 4 tenemos una función que ya lleva mucho tiempo en otros frameworks como Laravel, Flask o Django y es la función llamada pagination que nos permite obtener los datos de manera paginada; internamente emplea un parámetro que viaja vía gestión llamada pago para saber qué bloque de registros paginados deseas trabajar:
public function index()
{
$people = new PersonModel();
$data = [
'people' => $people->asObject()->paginate(10),
'pager' => $people->pager
];
return view('person/index',$data);
}Y con esto, perfectamente podemos crear una vista; desde nuestra función de index:
Y con esto la vista:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
<a href="/people/new" class="btn btn-success mb-4"><i class="fa fa-plus"></i> Crear</a>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Edad</th>
<th>Opciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($people as $key => $p) : ?>
<tr>
<td><?= $p->id ?></td>
<td><?= $p->name ?></td>
<td><?= $p->surname ?></td>
<td><?= $p->age ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<?= $pager->links() ?>
</body>
</html>Que como puedes ver, simplemente tiene un foreach de php para iterar la data que le estamos pasando.
Enlaces de paginación
Para los enlaces de paginación podemos emplear la función llamada links:
<?= $pager->links() ?>Si quieres ver como adaptar CodeIgniter 4 con Bootstrap 5
Definir filtros o campos de búsqueda

Los filtros son una herramienta muy útil en el desarrollo web ya que permiten a los usuarios buscar, ordenar y filtrar información de manera eficiente. Los filtros pueden ser utilizados para ordenar y clasificar datos en una tabla o lista y acceder a la información deseada sin tener que buscar en grandes cantidades de datos. Veamos como implementar un sencillo filtro en CodeIgniter 4
En CodeIgniter 4, al igual que en varios frameworks, es muy sencillo crear funciones de tipo filtro; en este caso es mediante un campo de tipo texto; así que para esto, vamos a crear un campo como el siguiente:
<input type="text" name="search" placeholder="search" value="<?= $search ?>">Que forma parte de nuestro formulario completo:
<form method="get" id="formFilter">
<select name="type">
<option value="">Tipos</option>
<option <?= ($typeId == "exit") ? "selected" : "" ?> value="exit">Salida</option>
<option <?= ($typeId == "entry") ? "selected" : "" ?> value="entry">Entrada</option>
</select>
<select name="user_id">
<option value="">Usuarios</option>
<?php foreach ($users as $u) : ?>
<option <?= ($u->id == $userId) ? "selected" : "" ?> value="<?= $u->id ?>"><?= $u->username ?></option>
<?php endforeach ?>
</select>
<br>
<h4>Búsqueda</h4>
<input value="<?= $search ?>" type="text" name="search" placeholder="Buscar">
<h3>Cantidades</h3>
<label for="check_cant">
Activar
<input type="checkbox" name="check_cant" id="check_cant" checked>
</label>
<br>
<label for="min_cant">
Minimo <span><?= $minCant ? $minCant : 0 ?></span>:
<input type="range" name="min_cant" value="<?= $minCant ? $minCant : 0 ?>" min="0" max="90" step="1">
</label>
<br>
<label for="max_cant">
Maximo <span><?= $maxCant ? $maxCant : 100 ?></span>:
<input type="range" name="max_cant" value="<?= $maxCant ? $maxCant : 100 ?>" min="10" max="100" step="1">
</label>
<br>
<button type="submit">Enviar</button>
<a href="<?= route_to('product.trace',$product->id) ?>">Limpiar</a>
</form>En el cual puedes colocar otros campos de cualquier tipo para tus filtros; cosa que hacemos en el curso completo de CodeIgniter 4; estos campos a la final no son más que los empleados en los formularios para hacer cualquier otra operación de guardado o edición, tenemos los SELECT y campos de textos pero puedes usar otros como CHECKBOX o RADIUS si los consideras necesarios.
Pero esa es otra historia; fíjate que el action del formulario no está definido, lo que significa que al enviar la petición la misma va a ser procesada por la misma página que pinta el formulario de búsqueda; en el caso del curso es la de un listado de detalles de producto.
Cuyo filtro, como puedes ver en el formulario anterior, es procesado por el mismo controlador que procesa la tabla anterior; en nuestro controlador, para saber si vamos a procesar algún dato de búsqueda, preguntamos desde el filtro:
$searchs = explode(" ", trim($this->request->getGet('search')));
if ($this->request->getGet('search')) {
//->orGroupStart()
$query->groupStart();
foreach ($searchs as $s) {
$query->orLike('u.username', $s)
->orLike('u.email', $s)
->orLike('puc.description', $s);
}
$query->groupEnd();Aquí varios aspectos que tienes que tener en cuenta:
- Usamos la función de explode para buscar por tokens
- Iteramos los tokens y buscamos coincidencias parciales con el like sobre las columnas a las cuales queremos buscar; en nuestro caso, un username, email y descripción que fíate que pertenecen a diferentes tablas
Cómo estamos empleando operaciones OR, es posible que interfieren con otros filtros where o similares en el resto de la consulta; por lo tanto, los agrupamos (en SQL colocamos paréntesis) para que solo afecten la sección de la búsqueda con los groupStart y groupEnd
En esencia todos los pasos anteriores son opcionales y según tus necesidades puede que quieras buscar coincidencias exactas; si ese es el caso, te basta con definirlo como:
$query->where('u.username', $this->request->getGet('search'))Para buscar una coincidencia exacta sobre la columna de username.
Puedes ver el código completo por si tienes dudas
https://github.com/libredesarrollo/codeigniter4-inventario/blob/main/app/Controllers/Dashboard/Product.php
Aquí lo importante es notar que vamos construyendo el query a tokens o trozos, en los cuales preguntamos mediante un condicional si tenemos datos recibidos por el usuario y con esto armamos el query; como en nuestro ejemplo es un campo de búsqueda que queremos construir

El siguiente proceso, es completar el CRUD e implementar la opción de eliminar registros en CodeIgniter 4.