Herramientas Informaticas

Etiqueta: CODEIGNITER

CodeIgniter 4 Aplicacion Boilerplate Plantilla

Que es boilerplate? normalmente en el ámbito de desarrollo de aplicaciones se le llama boilerplate a todo ese código repetitivo que usamos en un proyecto, o mas bien lo que siempre usamos en todos los proyectos, como login, menús, permisos.

A continuación les dejo un boilerplate para CodeIgniter el cual hice una bifurcación para adaptarlo a nuestras necesidades.

Este paquete para CodeIgniter 4 sirve como plataforma básica para crear rápidamente una aplicación administrativa. Incluye creación y gestión de perfiles, gestión de usuarios, roles, permisos y un menú generado dinámicamente.

Características

  • Tema backend configurable AdminLTE 3
  • CSS framework Bootstrap 4
  • Iconos de Font Awesome 5
  • Permisos basados en roles (RBAC) proporcionados porMyth/Auth
  • Generación de menús dinamicos
  • Traducciones en English / Indonesian / Spanish

Este proyecto aún está en sus primeras etapas de desarrollo… ¡no dude en contribuir!

El autor original es agungsugiarto/boilerplate https://github.com/agungsugiarto/boilerplate, solo hicimos un fork para adaptarlo a nuevas necesidades como traducirlo al español y hacerlo funcional en xampp/lampp

El fuente con los cambios mas recientes de fork estan en github https://github.com/julio101290/boilerplate

Instalación

  1. Obtener el modulo mediante composer
composer require julio101290/boilerplate

2. Establezca CI_ENVIRONMENT, baseURL, página de índice y configuración de la base de datos en su archivo .env según su base de datos existente (si no tiene un archivo .env, puede copiar primero desde el archivo env: cp env .env primero). Si la base de datos no existe, primero cree la base de datos.

# .env file
CI_ENVIRONMENT = development

app.baseURL = 'http://localhost:8080'
app.indexPage = ''

database.default.hostname = localhost
database.default.database = boilerplate
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi

3. Correr el comando para publicar de auth

php spark auth:publish

Publish Migration? [y, n]: y
  created: Database/Migrations/2017-11-20-223112_create_auth_tables.php
  Remember to run `spark migrate -all` to migrate the database.
Publish Models? [y, n]: n
Publish Entities? [y, n]: n
Publish Controller? [y, n]: n
Publish Views? [y, n]: n
Publish Filters? [y, n]: n
Publish Config file? [y, n]: y
  created: Config/Auth.php
Publish Language file? [y, n]: n

NOTA: Todo lo relacionado con cómo configurar la autenticación puede encontrar en Myth/Auth.

¿Ya está listo? ¡¡No tan rapido!! 😉 Después de publicar Config/Auth.php, debe cambiar las $views públicas con estas líneas a continuación:

public $views = [
    'login'           => 'julio101290\boilerplate\Views\Authentication\login',
    'register'        => 'julio101290\boilerplate\Views\Authentication\register',
    'forgot'          => 'julio101290\boilerplate\Views\Authentication\forgot',
    'reset'           => 'julio101290\boilerplate\Views\Authentication\reset',
    'emailForgot'     => 'julio101290\boilerplate\Views\Authentication\emails\forgot',
    'emailActivation' => 'julio101290\boilerplate\Views\Authentication\emails\activation',
];

Abra app\Config\Filters.php, busque $aliases y agregue estas líneas a continuación:

public $aliases = [
    'login'      => \Myth\Auth\Filters\LoginFilter::class,
    'role'       => \julio101290\boilerplate\Filters\RoleFilter::class,
    'permission' => \julio101290\boilerplate\Filters\PermissionFilter::class,
];

4. Ejecute publish, migrate y seed del boilerplate

php spark boilerplate:install

Abra app\Config\validation.php, busque $ruleSets y agregue estas líneas a continuación:

public $$ruleSets = [
    \Myth\Auth\Authentication\Passwords\ValidationRules::class,
];

Abra app\entities\Users.php, busque $casts y agregue estas líneas a continuación:


   protected $casts = [
        'username' => 'string',
        'email' => 'string',
        'firstname' => 'string',
        'lastname' => 'string',
        'active' => 'boolean',
        'force_pass_reset' => 'boolean',
    ];

Ejecute el servidor de desarrollo:

php spark serve

Abrir en el navegador http://localhost:8080/admin

Default user and password
+----+--------+-------------+
| No | User   | Password    |
+----+--------+-------------+
| 1  | admin  | super-admin |
| 2  | user   | super-user  |
+----+--------+-------------+

Configuraciones

Plantilla de configuración

Puede configurar el controlador de tablero predeterminado y el tema de back-end en app\Config\Boilerplate.php

class Boilerplate extends BaseConfig
{
    public $appName = 'Boilerplate';

    public $dashboard = [
        'namespace'  => 'julio101290\boilerplate\Controllers',
        'controller' => 'DashboardController::index',
        'filter'     => 'permission:back-office',
    ];
// App/Config/Boilerplate.php

Uso

Puede encontrar cómo funciona con las rutas de código de lectura, el controlador y las vistas, etc. Finalmente… ¡Feliz codificación!

Contribuciones

Las contribuciones son muy bienvenidas.

Licencia

Este paquete es software gratuito distribuido bajo los términos de la licencia MIT.

CI 4.0 MedicalSoft Modulo Para Cita y Creando nuestros Propios Helpers personalizados

En este apartado vamos a crear nuestro modulo de citas medicas que posteriormente podremos convertir en consulta, a su vez veremos como crear nuestros propios helpers y usarlos

Que es un helper en codeigniter, pues no es mas que funciones o utilerías que nos pueden servir en diferentes partes del sistema

Creamos la tabla para citas

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 08-01-2023 a las 23:44:15
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
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 */;

--
-- Base de datos: `medicalsoft`
--

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

--
-- Estructura de tabla para la tabla `citas`
--

CREATE TABLE `citas` (
  `id` int(11) NOT NULL,
  `idPaciente` int(11) DEFAULT NULL,
  `fechaHora` datetime NOT NULL,
  `hastaFechaHora` datetime DEFAULT NULL,
  `observaciones` varchar(1024) COLLATE utf8_spanish2_ci NOT NULL,
  `created_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `estado` varchar(15) COLLATE utf8_spanish2_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `citas`
--
ALTER TABLE `citas`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `citas`
--
ALTER TABLE `citas`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
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 */;

Y como de costumbre creamos primeramente el archiv CitasModel.php en la carpeta app/models y le ponemos el siguiente código

<?php

namespace App\Models;

use CodeIgniter\Model;

class CitasModel extends Model {

    protected $table = 'citas';
    protected $primaryKey = 'id';
    protected $useAutoIncrement = true;
    protected $returnType = 'array';
    protected $useSoftDeletes = true;
    protected $allowedFields = ['id', 'idPaciente', 'fechaHora', 'hastaFechaHora', 'observaciones', 'created_at', 'updated_at'];
    protected $useTimestamps = true;
    protected $createdField = 'created_at';
    protected $deletedField = 'deleted_at';
    protected $validationRules = [
        'observaciones' => 'required|alpha_numeric_space|min_length[3]'
    ];
    protected $validationMessages = [];
    protected $skipValidation = false;

    public function mdlObtenerDatos() {

        $resultado = $this->db->table('citas a, pacientes b')
                ->select('a.id,concat(b.nombres,\' \',b.apellidos) as nombrePaciente,a.observaciones,a.fechaHora,a.hastaFechaHora,estado,a.created_at,a.updated_at')
                ->where('a.idPaciente', 'b.id', false)
                ->where('a.deleted_at', null);

        return $resultado;
    }

    public function mdlObtenerCita($idCita) {

        $resultado = $this->db->table('citas a, pacientes b')
                ->select('a.id,concat(b.nombres,\' \',b.apellidos) as nombrePaciente,a.observaciones,a.fechaHora,a.hastaFechaHora,estado,a.created_at,a.updated_at,a.idPaciente')
                ->where('a.idPaciente', 'b.id', false)
                ->where('a.deleted_at', null)
                ->where('a.id', $idCita,null)->get()->getResultArray();

        return $resultado[0];
    }

}

Posteriormente creamos el archivo CitasController.php en la carpeta app/controller y le insertamos el siguiente código

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\CitasModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class CitasController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $citas;

    public function __construct() {
        $this->citas = new CitasModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
      
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->citas->mdlObtenerDatos();  
                    //where('deleted_at', null);
            // $resultado = $this->bitacora->findAll();
            // $this->bitacora->getResource()->countAllResults(),
            // $this->bitacora->getResource($search)->countAllResults()
            //      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarCita\" data-toggle=\"modal\" idCita=\"$row->id\" data-target=\"#modalAgregarCitas\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarCita\" idCita=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        
        $fechaActual =  fechaMySQLADateTimeHTML5(fechaHoraActual());
        
        $titulos["fecha"] =  $fechaActual;
        $titulos["title"] = lang('citas.title');
        $titulos["subtitle"] = lang('citas.subtitle');

        return view('citas', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traeCita() {


        $idCita = $this->request->getPost("idCita");
        $datosCita = $this->citas->mdlObtenerCita($idCita);

        echo json_encode($datosCita);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

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

        if ($datos["idCita"] == 0) {


            try {


                if ($this->citas->save($datos) === false) {

                    $errores = $this->citas->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo la medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->citas->update($datos["idCita"], $datos) == false) {
                
                $errores = $this->citas->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }
                
                return;
               
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;


    }
    
    
    
     public function delete($id)
    {
        if (!$found = $this->citas->delete($id)) {
            return $this->failNotFound(lang('citas.msg.msg_get_fail'));
        }
        
        $infoCita = $this->citas->find($id);
        
        $datosBitacora["descripcion"] = "Se elimino la medicamento que contenia los siguientes datos ". json_encode($infoCita);
        
        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('citas.msg.msg_delete'));
    }

}

Luego Creamos la vista citas.php en app/views

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

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

<?= $this->include('citasModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarCitas" data-toggle="modal" data-target="#modalAgregarCitas"><i class="fa fa-plus"></i>

                    <?= lang('citas.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaCitas" class="table table-striped table-hover va-middle tablaCitas">
                        <thead>
                            <tr>



                                <th>#</th>

                                <th><?= lang('citas.nombrePaciente') ?></th>
                                <th><?= lang('citas.observaciones') ?></th>
                                <th><?= lang('citas.fechaHora') ?></th>
                                <th><?= lang('citas.hastaFechaHora') ?></th>
                                <th><?= lang('citas.estado') ?></th>


                                <th><?= lang('citas.createdAt') ?></th>
                                <th><?= lang('citas.updateAt') ?></th>
                                <th><?= lang('citas.actions') ?> </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

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




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



        $('.tablaCitas').DataTable({
            "ajax": "<?= route_to('admin/citas') ?>",
            "deferRender": true,
            "serverSide": true,
            "retrieve": true,
            "processing": true

        });

    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaCitas").on("click", ".btnEditarCita", function () {

        var idCita = $(this).attr("idCita");


        console.log("idCita ", idCita);

        var datos = new FormData();
        datos.append("idCita", idCita);

        $.ajax({

            url: "<?= route_to('admin/citas/traeCita') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idCita").val(respuesta["id"]);
                $("#observaciones").val(respuesta["observaciones"]);
                $("#fechaHora").val(respuesta["fechaHora"]);
                $("#hastaFechaHora").val(respuesta["hastaFechaHora"]);


                //$("#pacientes").val(respuesta["idPaciente"]);

                $("#pacientes").empty() //empty select
                        .append($("<option/>") //add option tag in select
                                .val(respuesta["idPaciente"]) //set value for option to post it
                                .text(respuesta["nombrePaciente"])) //set a text for show in select
                        .val(respuesta["idPaciente"]) //select option of select2
                        .trigger("change");


            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaCitas").on("click", ".btnEliminarCita", function () {

        var idCita = $(this).attr("idCita");




        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/citas') ?>/` + idCita,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                            $(".tablaCitas").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

Luego en la carpeta app/views creamos la carpeta citasModulos, creamos el archivo modalCaptura.php y metemos el siguiente código

<!-- Modal Citas -->

<div class="modal fade" id="modalAgregarCitas" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('citas.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-enfermedad" class="form-horizontal">
                    <!-- CSRF token --> 
                    <input type="hidden" class="txt_csrfname" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />

                    <input type="hidden" id="idCita" name="idCita" value="0">




                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.nombrePaciente') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>

                                <select id='pacientes'  name='pacientes' style='width: 80%;'>
                                    <option value='0'><?= lang('citas.seleccionePaciente') ?></option>
                                </select>


                            </div>
                        </div>
                    </div>


                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.observaciones') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>

                                <textarea class="form-control <?= session('error.observaciones ') ? 'is-invalid' : '' ?>" rows="3" placeholder="<?= lang('citas.observaciones') ?>" id="observaciones" name="observaciones" autocomplete="off"></textarea>

                            </div>
                        </div>
                    </div>



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.fechaHora') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>


                                <input type="datetime-local" id="fechaHora" name="fechaHora" value="<?= $fecha ?>">


                            </div>
                        </div>
                    </div>


                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('citas.hastaFechaHora') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>


                                <input type="datetime-local" id="hastaFechaHora" name="hastaFechaHora" value="<?= $fecha ?>">


                            </div>
                        </div>
                    </div>



                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarCita"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarCitas', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idCita").val("0");

        $("#btnGuardarCita").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnGuardarCita', function (e) {


        var idCita = $(this).attr("idCita");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idCita").val(idCita);
        $("#btnGuardarCita").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarCita', function (e) {

        var idCita = $("#idCita").val();
        var observaciones = $("#observaciones").val();
        var idPaciente = $("#pacientes").val();
        var fechaHora = $("#fechaHora").val();
        var hastaFechaHora = $("#hastaFechaHora").val();
       

        console.log("OBSERVACIONES:",observaciones);

        $("#btnGuardarCita").attr("disabled", true);


        var datos = new FormData();
        datos.append("idCita", idCita);
        datos.append("observaciones", observaciones);
        datos.append("idPaciente", idPaciente);
        datos.append("fechaHora", fechaHora);
        datos.append("hastaFechaHora", hastaFechaHora);


        $.ajax({

            url: "<?= route_to('admin/citas/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaCitas').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarCita").removeAttr("disabled");


                    $('#modalAgregarCitas').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarCita").removeAttr("disabled");
                    //  $('#modalAgregarCita').modal('hide');

                }

            }

        }

        )




    });



    // Initialize select2
    $("#pacientes").select2({
        ajax: {
            url: "<?= site_url('admin/pacientes/traerPacientesAjax') ?>",
            type: "post",
            dataType: 'json',
            delay: 250,
            data: function (params) {
                // CSRF Hash
                var csrfName = $('.txt_csrfname').attr('name'); // CSRF Token name
                var csrfHash = $('.txt_csrfname').val(); // CSRF hash

                return {
                    searchTerm: params.term, // search term
                    [csrfName]: csrfHash // CSRF Token
                };
            },
            processResults: function (response) {

                // Update CSRF Token
                $('.txt_csrfname').val(response.token);

                return {
                    results: response.data
                };
            },
            cache: true
        }
    });



</script>


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

En este modulo como se vieron el archivo CitasController.php se usaron unas funciones para el formato de las fechas dichas funciones estan en el helper, estas funciones nos sirvieron anteriormente en JCPOS2021, entonces en la carpeta app/Helpers creamos el archivo utilerias_helper.php y metemos el siguiente código

<?php

function strMenuActivo($strMenu1, $strMenu2) {
    if ($strMenu1 == $strMenu2) {
        $respuesta = 'class="active"';
    } else {
        $respuesta = "";
    }
    return $respuesta;
}

//SI LA VARIABLE ESTA VACIA O NO SETA DECLARADA MANDARA CERO SIEMPRE, ES COMO EL VAL DE VISUAL BASIC 6.0

function esCero($value) {

    if (empty($value)) {
        return "0";
    } else {
        return $value;
    }
}

//FECHA SQL PARA GUARDAR EN BASE DE DATOS

function fechaSQL($fecha) {

    return date("Ymd", strtotime($fecha));
}

// CONVIERTE FECHA MYSQLDATETIME A HTML5
function fechaMySQLADateTimeHTML5($fecha) {

    return date("Y-m-d", strtotime($fecha)) . "T" . date("H:i:s", strtotime($fecha));
}

function agregarMinutos($fecha, $minutos) {


    return date("Y/m/d h:i:s", strtotime($fecha . "+ $minutos minutes"));

    /*
      $fecha1= new DateTime($fecha);
      $fecha1->add(new DateInterval('PT10H30S'));
      return $date->format('Y-m-d H:i:s') . "\n";
     * 
     */
}

//CONVIERTE LA FECHA EN PERIODO
function fechaPeriodo($fecha) {

    return date("Ym", strtotime($fecha));
}

//OBTIENE FECHA ACTUAL
function fechaActual() {

    return date("Y/m/d");
}

//OBTIENE FECHA HORA ACTUAL

function fechaHoraActual() {

    return date("Y-m-d H:i:s ", time());
}

//DIFERENCIA ENTRE MINUTOS
function diferenciaMinutos($fecha_i, $fecha_f) {
    $minutos = (strtotime($fecha_i) - strtotime($fecha_f)) / 60;

    $minutos = abs($minutos);
    $minutos = floor($minutos);

    return $minutos;
}

function strSellar($llave, $password, $cadenaOriginal) {


    $archivoPem = "/tmp/llave.key.pem";
    $comando = "openssl pkcs8 -inform DER -in $llave -passin pass:$password -out $archivoPem";

    exec($comando);
    $sello = "ok";

    //Sellar
    $archivo = openssl_pkey_get_private(file_get_contents($archivoPem));
    $sig = "";
    openssl_sign($cadenaOriginal, $sig, $archivo, OPENSSL_ALGO_SHA256);

    $sello = base64_encode($sig);

    return $sello;
}

//SOLO DIA
function dia($fecha) {

    return date("d", strtotime($fecha));
}

//SOLO MES
function mes($fecha) {

    return date("m", strtotime($fecha));
}

//SOLO AÑO
function año($fecha) {

    return date("Y", strtotime($fecha));
}

Ahora en app/lenguajes/en/ creamos el archivo citas.php y metemos el siguiente archivo

<?php

$citas["observaciones"] = "Observations";
$citas["nombrePaciente"] = "Patient´s Name";
$citas["fechaHora"] = "Start Date";
$citas["hastaFechaHora"] = "End Date";

$citas["createdAt"] = "Date Creation";
$citas["updateAt"] = "Date Update";
$citas["add"] = "add appointment";
$citas["actions"] = "Acciones";
$citas["estado"] = "status";
$citas["createEdit"] = "add  / update appointment";
$citas["seleccionePaciente"] = "Select an Patient";
$citas["title"] = "appointment";
$citas["subtitle"] = "List of appointments";

$citas["msg_delete"] = "The quote has been removed .";
$citas["msg_get_fail"] = "The appointment does not exist or was deleted.";





return $citas;

Ahora en app/lenguajes/es/ creamos el archivo citas.php y metemos el siguiente archivo

<?php

$citas["observaciones"] = "Observaciones";
$citas["nombrePaciente"] = "Nombre Paciente";
$citas["fechaHora"] = "Fecha Inicio Cita";
$citas["obsevaciones"] = "Observaciones";
$citas["hastaFechaHora"] = "Fin Cita";

$citas["createdAt"] = "Fecha Creación";
$citas["updateAt"] = "Fecha de Modificación";
$citas["add"] = "Agregar Cita";
$citas["actions"] = "Acciones";
$citas["estado"] = "Estado";
$citas["createEdit"] = "Crear  / Editar Citas";
$citas["seleccionePaciente"] = "Seleccione un paciente";
$citas["title"] = "Citas";
$citas["subtitle"] = "Lista de Citas";

$citas["msg_delete"] = "La cita ha sido eliminada .";
$citas["msg_get_fail"] = "La cita no existe o fue eliminada.";

return $citas;

Luego ya por ultimo creamos las rutas en app/config/routes.php metemos el siguiente codigo

    $routes->resource('citas', [
        'filter' => 'permission:citas-permiso',
        'controller' => 'CitasController',
        'except' => 'show'
    ]);

    $routes->post('citas/guardar', 'CitasController::guardar');

    $routes->post('citas/traeCita', 'CitasController::traeCita');
Y listo ya tenemos nuestro modulo de citas y aprendimos como usar los Helpers que creamos

CI 4.0 MedicalSoft CRUD Para Enfermedades

Ya por ultimo creamos un CRUD super básico para las enfermedades

Primero creamos la tabla

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 07-01-2023 a las 17:11:20
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
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 */;

--
-- Base de datos: `medicalsoft`
--

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

--
-- Estructura de tabla para la tabla `enfermedades`
--

CREATE TABLE `enfermedades` (
  `id` int(11) NOT NULL,
  `descripcion` varchar(256) COLLATE utf8_spanish2_ci NOT NULL,
  `created_at` datetime NOT NULL,
  `deleted_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `enfermedades`
--
ALTER TABLE `enfermedades`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `enfermedades`
--
ALTER TABLE `enfermedades`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
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 */;

Creamos el archivo EnfermedadesModel.php en app/models

<?php

namespace App\Models;

use CodeIgniter\Model;

class EnfermedadesModel extends Model
{
    protected $table      = 'enfermedades';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','descripcion', 'created_at','updated_at'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    =  [
        'descripcion'     => 'required|alpha_numeric_space|min_length[3]'
        
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
   
}

Creamos el archivo EnfermedadesController.php en app/controller

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\EnfermedadesModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class EnfermedadesController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $enfermedades;

    public function __construct() {
        $this->enfermedades = new EnfermedadesModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->enfermedades->select('id,descripcion,created_at,updated_at')->where('deleted_at', null);
            // $resultado = $this->bitacora->findAll();
            // $this->bitacora->getResource()->countAllResults(),
            // $this->bitacora->getResource($search)->countAllResults()
            //      var_dump($datos);

            $dataTable =  \Hermawan\DataTables\DataTable::of($datos);
            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarEnfermedad\" data-toggle=\"modal\" idEnfermedad=\"$row->id\" data-target=\"#modalAgregarEnfermedades\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarEnfermedad\" idEnfermedad=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        $titulos["title"] = lang('enfermedades.title');
        $titulos["subtitle"] = lang('enfermedades.subtitle');

        return view('enfermedades', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traeEnfermedad() {


        $idEnfermedad = $this->request->getPost("idEnfermedad");
        $datosEnfermedad = $this->enfermedades->find($idEnfermedad);

        echo json_encode($datosEnfermedad);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

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

        if ($datos["idEnfermedad"] == 0) {


            try {


                if ($this->enfermedades->save($datos) === false) {

                    $errores = $this->enfermedades->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo la enfermedad con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->enfermedades->update($datos["idEnfermedad"], $datos) == false) {
                
                $errores = $this->enfermedades->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }
                
                return;
               
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el paciente con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;


    }
    
    
    
     public function delete($id)
    {
        if (!$found = $this->enfermedades->delete($id)) {
            return $this->failNotFound(lang('enfermedades.msg.msg_get_fail'));
        }
        
        $infoEnfermedad = $this->enfermedades->find($id);
        
        $datosBitacora["descripcion"] = "Se elimino la enfermedad que contenia los siguientes datos ". json_encode($infoEnfermedad);
        
        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('enfermedades.msg.msg_delete'));
    }

}

Creamos el archivo enfermedades en la carpeta app/views

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

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

<?= $this->include('enfermedadesModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarPaciente" data-toggle="modal" data-target="#modalAgregarEnfermedades"><i class="fa fa-plus"></i>

                    <?= lang('enfermedades.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaEnfermedades" class="table table-striped table-hover va-middle tablaEnfermedades">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('enfermedades.description') ?></th>
                        
                                <th><?= lang('enfermedades.createdAt') ?></th>
                                <th><?= lang('enfermedades.updateAt') ?></th>
                                <th>Acciones </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

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




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



         $('.tablaEnfermedades').DataTable({
         "ajax": "<?= route_to('admin/enfermedades') ?>",
         "deferRender": true,
         "serverSide": true,
         "retrieve": true,
         "processing": true
         
         });
        
    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaEnfermedades").on("click", ".btnEditarEnfermedad", function () {

        var idEnfermedad = $(this).attr("idEnfermedad");


        console.log("idEnfermedad ", idEnfermedad);

        var datos = new FormData();
        datos.append("idEnfermedad", idEnfermedad);

        $.ajax({

            url: "<?= route_to('admin/enfermedades/traerEnfermedad') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idEnfermedad").val(respuesta["id"]);
                $("#descripcion").val(respuesta["descripcion"]);
            
            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaEnfermedades").on("click", ".btnEliminarEnfermedad", function () {

        var idEnfermedad = $(this).attr("idEnfermedad");


 

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/enfermedades') ?>/` + idEnfermedad,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                           $(".tablaEnfermedades").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

En views Creamos la carpeta enfermedadesModulos y dentro de ella creamos el archivo modalCaptura.php y metemos el siguiente código

<!-- Modal Enfermedads -->
<div class="modal fade" id="modalAgregarEnfermedades" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('enfermedades.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-enfermedad" class="form-horizontal">
                    <input type="hidden" id="idEnfermedad" name="idEnfermedad" value="0">



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('enfermedades.description') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="descripcion" id="descripcion" class="form-control <?= session('error.descripcion ') ? 'is-invalid' : '' ?>" value="<?= old('descripcion') ?>" placeholder="<?= lang('enfermedades.description') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                  

         

   
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarEnfermedad"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarEnfermedad', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idEnfermedad").val("0");
        
           $("#btnGuardarEnfermedad").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnGuardarEnfermedad', function (e) {


        var idEnfermedad = $(this).attr("idEnfermedad");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idEnfermedad").val(idEnfermedad);
        $("#btnGuardarEnfermedad").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarEnfermedad', function (e) {

        var idEnfermedad = $("#idEnfermedad").val();
        var descripcion = $("#descripcion").val();
      


        $("#btnGuardarEnfermedad").attr("disabled", true);


        var datos = new FormData();
        datos.append("idEnfermedad", idEnfermedad);
        datos.append("descripcion", descripcion);


        $.ajax({

            url: "<?= route_to('admin/enfermedades/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaEnfermedades').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarEnfermedad").removeAttr("disabled");

                   
                     $('#modalAgregarEnfermedades').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarEnfermedad").removeAttr("disabled");
                  //  $('#modalAgregarEnfermedad').modal('hide');

                }

            }

        }

        )




    });
</script>


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

En app/languaje/en metemos el archivo de traducción en ingles enfermedades.php con el siguiente código

<?php

$enfermedades["descrption"] = "Description";

$enfermedades["createdAt"] = "Date Creation";
$enfermedades["updateAt"] = "Date Update";
$enfermedades["add"] = "Add disease";
$enfermedades["actions"] = "Actions";
$enfermedades["createEdit"] = "Create  / Edit disease";
$enfermedades["title"] = "Disease";
$enfermedades["subtitle"] = "List of diseases";

$enfermedades["msg_delete"] = "disease has deleted .";
$enfermedades["msg_get_fail"] = "The disease not exist or has deleted.";





return $enfermedades;

En app/languaje/esmetemos el archivo de traducción en español enfermedades.php con el siguiente código

<?php

$enfermedades["description"] = "Descripcion";

$enfermedades["createdAt"] = "Fecha Creación";
$enfermedades["updateAt"] = "Fecha de Modificación";
$enfermedades["add"] = "Agregar Enfermedades";
$enfermedades["actions"] = "Acciones";
$enfermedades["createEdit"] = "Crear  / Editar Enfermedades";
$enfermedades["title"] = "Enfermedades";
$enfermedades["subtitle"] = "Lista de Enfermedades";

$enfermedades["msg_delete"] = "La enfermedad ha sido eliminada .";
$enfermedades["msg_get_fail"] = "La enfermedad no existe o fue eliminada.";





return $enfermedades;

Por ultimo creamos las rutas en app/config/routes.php

    $routes->resource('enfermedades', [
        'filter' => 'permission:enfermedades-permiso',
        'controller' => 'EnfermedadesController',
        'except' => 'show'
    ]);

    $routes->post('enfermedades/guardar', 'EnfermedadesController::guardar');

    $routes->post('enfermedades/traerEnfermedad', 'EnfermedadesController::traeEnfermedad');
Y listo al final quedaría así

CI 4.0 MedicalSoft CRUD Para Medicamentos

Ya creamos el CRUD para pacientes, ahora será necesario crear un CRUD para los medicamentos.

Primero creamos la tabla medicamentos, les dejo la exportación desde phpmyadmin

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 06-01-2023 a las 20:37:36
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
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 */;

--
-- Base de datos: `medicalsoft`
--

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

--
-- Estructura de tabla para la tabla `medicamentos`
--

CREATE TABLE `medicamentos` (
  `id` int(11) NOT NULL,
  `descripcion` varchar(256) COLLATE utf8_spanish2_ci NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `medicamentos`
--
ALTER TABLE `medicamentos`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `medicamentos`
--
ALTER TABLE `medicamentos`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
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 */;

Creamos el archivo MedicamentosModel.php en app/models/

<?php

namespace App\Models;

use CodeIgniter\Model;

class MedicamentosModel extends Model
{
    protected $table      = 'medicamentos';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','descripcion', 'created_at','updated_at'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    =  [
        'descripcion'     => 'required|alpha_numeric_space|min_length[3]'
        
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
   
}

Creamos el archivo MedicamentosController.php en app/controller

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\MedicamentosModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class MedicamentosController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $medicamentos;

    public function __construct() {
        $this->medicamentos = new MedicamentosModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->medicamentos->select('id,descripcion,created_at,updated_at')->where('deleted_at', null);
            // $resultado = $this->bitacora->findAll();
            // $this->bitacora->getResource()->countAllResults(),
            // $this->bitacora->getResource($search)->countAllResults()
            //      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarMedicamento\" data-toggle=\"modal\" idMedicamento=\"$row->id\" data-target=\"#modalAgregarMedicamentos\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarMedicamento\" idMedicamento=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        $titulos["title"] = lang('medicamentos.title');
        $titulos["subtitle"] = lang('medicamentos.subtitle');

        return view('medicamentos', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traeMedicamento() {


        $idMedicamento = $this->request->getPost("idMedicamento");
        $datosMedicamento = $this->medicamentos->find($idMedicamento);

        echo json_encode($datosMedicamento);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

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

        if ($datos["idMedicamento"] == 0) {


            try {


                if ($this->medicamentos->save($datos) === false) {

                    $errores = $this->medicamentos->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo la medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->medicamentos->update($datos["idMedicamento"], $datos) == false) {
                
                $errores = $this->medicamentos->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }
                
                return;
               
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;


    }
    
    
    
     public function delete($id)
    {
        if (!$found = $this->medicamentos->delete($id)) {
            return $this->failNotFound(lang('medicamentos.msg.msg_get_fail'));
        }
        
        $infoMedicamento = $this->medicamentos->find($id);
        
        $datosBitacora["descripcion"] = "Se elimino la medicamento que contenia los siguientes datos ". json_encode($infoMedicamento);
        
        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('medicamentos.msg.msg_delete'));
    }

}

Creamos el archivo de la vista medicamentos.php en app/views

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

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

<?= $this->include('medicamentosModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarMedicamento" data-toggle="modal" data-target="#modalAgregarMedicamentos"><i class="fa fa-plus"></i>

                    <?= lang('medicamentos.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaMedicamentos" class="table table-striped table-hover va-middle tablaMedicamentos">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('medicamentos.description') ?></th>
                        
                                <th><?= lang('medicamentos.createdAt') ?></th>
                                <th><?= lang('medicamentos.updateAt') ?></th>
                                <th>Acciones </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

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




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



         $('.tablaMedicamentos').DataTable({
         "ajax": "<?= route_to('admin/medicamentos') ?>",
         "deferRender": true,
         "serverSide": true,
         "retrieve": true,
         "processing": true
         
         });
        
    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaMedicamentos").on("click", ".btnEditarMedicamento", function () {

        var idMedicamento = $(this).attr("idMedicamento");


        console.log("idMedicamento ", idMedicamento);

        var datos = new FormData();
        datos.append("idMedicamento", idMedicamento);

        $.ajax({

            url: "<?= route_to('admin/medicamentos/traerMedicamento') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idMedicamento").val(respuesta["id"]);
                $("#descripcion").val(respuesta["descripcion"]);
            
            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaMedicamentos").on("click", ".btnEliminarMedicamento", function () {

        var idMedicamento = $(this).attr("idMedicamento");


 

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/medicamentos') ?>/` + idMedicamento,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                           $(".tablaMedicamentos").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

Creamos la carpeta en views medicamentosModulos y dentro de ella creamos el archivo modelCaptura.php con el siguiente código

<!-- Modal Medicamentos -->
<div class="modal fade" id="modalAgregarMedicamentos" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('medicamentos.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-enfermedad" class="form-horizontal">
                    <input type="hidden" id="idMedicamento" name="idMedicamento" value="0">



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('medicamentos.description') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="descripcion" id="descripcion" class="form-control <?= session('error.descripcion ') ? 'is-invalid' : '' ?>" value="<?= old('descripcion') ?>" placeholder="<?= lang('medicamentos.description') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                  

         

   
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarMedicamento"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarMedicamento', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idMedicamento").val("0");
        
           $("#btnGuardarMedicamento").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnGuardarMedicamento', function (e) {


        var idMedicamento = $(this).attr("idMedicamento");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idMedicamento").val(idMedicamento);
        $("#btnGuardarMedicamento").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarMedicamento', function (e) {

        var idMedicamento = $("#idMedicamento").val();
        var descripcion = $("#descripcion").val();
      


        $("#btnGuardarMedicamento").attr("disabled", true);


        var datos = new FormData();
        datos.append("idMedicamento", idMedicamento);
        datos.append("descripcion", descripcion);


        $.ajax({

            url: "<?= route_to('admin/medicamentos/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaMedicamentos').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarMedicamento").removeAttr("disabled");

                   
                     $('#modalAgregarMedicamentos').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarMedicamento").removeAttr("disabled");
                  //  $('#modalAgregarMedicamento').modal('hide');

                }

            }

        }

        )




    });
</script>


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

en app/Languaje/en creamos el archivo medicamentos.php con el siguiente código

<?php

$medicamentos["descrption"] = "Description";

$medicamentos["createdAt"] = "Date Creation";
$medicamentos["updateAt"] = "Date Update";
$medicamentos["add"] = "Add medicine";
$medicamentos["actions"] = "Actions";
$medicamentos["createEdit"] = "Create  / Edit medicine";
$medicamentos["title"] = "Medicine";
$medicamentos["subtitle"] = "List of medicines";

$medicamentos["msg_delete"] = "medicine has deleted .";
$medicamentos["msg_get_fail"] = "The medicine not exist or has deleted.";





return $medicamentos;

en app/Languaje/escreamos el archivo medicamentos.php con el siguiente código

<?php

$medicamentos["description"] = "Descripcion";

$medicamentos["createdAt"] = "Fecha Creación";
$medicamentos["updateAt"] = "Fecha de Modificación";
$medicamentos["add"] = "Agregar Medicamentos";
$medicamentos["actions"] = "Acciones";
$medicamentos["createEdit"] = "Crear  / Editar Medicamentos";
$medicamentos["title"] = "Medicamentos";
$medicamentos["subtitle"] = "Lista de Medicamentos";

$medicamentos["msg_delete"] = "El medicamento ha sido eliminada .";
$medicamentos["msg_get_fail"] = "La medicamento no existe o fue eliminada.";





return $medicamentos;

en el archivo app/config/routes.php agregamos dentro del grupo admin la siguiente rutas

    $routes->resource('medicamentos', [
        'filter' => 'permission:medicamentos-permiso',
        'controller' => 'MedicamentosController',
        'except' => 'show'
    ]);

    $routes->post('medicamentos/guardar', 'MedicamentosController::guardar');

    $routes->post('medicamentos/traerMedicamento', 'MedicamentosController::traeMedicamento');
Y listo ya tenemos nuestro CRUD de medicamentos

CI 4.0 MedicalSoft CRUD Para Pacientes

Ahora veremos como crear el CRUD de pacientes en el framework CodeIgniter 4.0 con los siguientes datos, Nombres, Apellidos, DNI, Correo Electrónico, para ello creamos la siguiente tabla.

Leer Mas: CI 4.0 MedicalSoft CRUD Para Pacientes
-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 04-01-2023 a las 18:20:08
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
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 */;

--
-- Base de datos: `medicalsoft`
--

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

--
-- Estructura de tabla para la tabla `pacientes`
--

CREATE TABLE `pacientes` (
  `id` int(11) NOT NULL,
  `nombres` varchar(256) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `apellidos` varchar(256) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `dni` varchar(32) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `telefono` varchar(16) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `correoElectronico` varchar(64) COLLATE utf8_spanish2_ci DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `pacientes`
--
ALTER TABLE `pacientes`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `pacientes`
--
ALTER TABLE `pacientes`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
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 */;

Creamos el archivo PacientesModel.php en la carpeta model para el acceso a la tabla de la base de datos

<?php

namespace App\Models;

use CodeIgniter\Model;

class PacientesModel extends Model
{
    protected $table      = 'pacientes';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','nombres', 'apellidos','dni','telefono','correoElectronico','created_at','updated_at'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    =  [
        'nombres'     => 'required|alpha_numeric_space|min_length[3]',
        'apellidos'     => 'required|alpha_numeric_space|min_length[5]',
        'correoElectronico'        => 'required|valid_email|is_unique[users.email]'
        
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
   
}

Creamos el archivo PacientesController.php alli tendremos la funciones para guardar, leer y modificar, creo que no falta decir que va en la carpeta controller

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use \App\Models\PacientesModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class PacientesController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $pacientes;

    public function __construct() {
        $this->pacientes = new PacientesModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->pacientes->select('id,nombres,apellidos,dni,telefono,correoElectronico,created_at,updated_at')->where('deleted_at', null);
// $resultado = $this->bitacora->findAll();
// $this->bitacora->getResource()->countAllResults(),
// $this->bitacora->getResource($search)->countAllResults()
//      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarPaciente\" data-toggle=\"modal\" idPaciente=\"$row->id\" data-target=\"#modalAgregarPaciente\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarPaciente\" idPaciente=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        $titulos["title"] = lang('patients.title');
        $titulos["subtitle"] = lang('patients.subtitle');
        
       
//$data["data"] = $datos;
        return view('pacientes', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traePaciente() {


        $idPaciente = $this->request->getPost("idPaciente");
        $datosPaciente = $this->pacientes->find($idPaciente);

        echo json_encode($datosPaciente);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

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

        if ($datos["idPaciente"] == 0) {


            try {


                if ($this->pacientes->save($datos) === false) {

                    $errores = $this->pacientes->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo el paciente con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->pacientes->update($datos["idPaciente"], $datos) == false) {

                $errores = $this->pacientes->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }

                return;
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el paciente con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;
    }

    public function delete($id) {
        if (!$found = $this->pacientes->delete($id)) {
            return $this->failNotFound(lang('patients.msg.msg_get_fail'));
        }

        $infoPaciente = $this->pacientes->find($id);

        $datosBitacora["descripcion"] = "Se elimino el paciente que contenia los siguientes datos " . json_encode($infoPaciente);

        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('patients.msg.msg_delete'));
    }

    /**
     * Trae en formato JSON los pacientes para el select2
     * @return type
     */
    public function traerPacientesAjax() {
        
        $request = service('request');
        $postData = $request->getPost();

        $response = array();

        // Read new token and assign in $response['token']
        $response['token'] = csrf_hash();

        if (!isset($postData['searchTerm'])) {
            // Fetch record
            $pacientes = new PacientesModel();
            $listaPacientes = $pacientes->select('id,nombres,apellidos')
                    ->orderBy('nombres')
                    ->findAll(10);
        } else {
            $searchTerm = $postData['searchTerm'];

            // Fetch record
            $pacientes = new PacientesModel();
            $listaPacientes = $pacientes->select('id,nombres,apellidos')
                    ->where("deleted_at",null)
                    ->like('nombres', $searchTerm)
                    ->orLike('apellidos', $searchTerm)
                    ->orderBy('nombres')
                    ->findAll(10);
        }

        $data = array();
        foreach ($listaPacientes as $paciente) {
            $data[] = array(
                "id" => $paciente['id'],
                "text" => $paciente['nombres'].' '.$paciente['apellidos'],
            );
        }

        $response['data'] = $data;

        return $this->response->setJSON($response);
    }

}

En Views creamos el archivo de la vista

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

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

<?= $this->include('pacientesModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarPaciente" data-toggle="modal" data-target="#modalAgregarPaciente"><i class="fa fa-plus"></i>

                    <?= lang('patients.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaPacientes" class="table table-striped table-hover va-middle tablaPacientes">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('patients.names') ?></th>
                                <th><?= lang('patients.lastName') ?></th>
                                <th><?= lang('patients.dni') ?></th>
                                <th><?= lang('patients.phone') ?></th>
                                <th><?= lang('patients.email') ?></th>
                                <th><?= lang('patients.createdAt') ?></th>
                                <th><?= lang('patients.updateAt') ?></th>
                                <th>Acciones </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

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




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



         $('.tablaPacientes').DataTable({
         "ajax": "<?= route_to('admin/pacientes') ?>",
         "deferRender": true,
         "serverSide": true,
         "retrieve": true,
         "processing": true
         
         });
        
    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaPacientes").on("click", ".btnEditarPaciente", function () {

        var idPaciente = $(this).attr("idPaciente");


        console.log("paciente ", idPaciente);

        var datos = new FormData();
        datos.append("idPaciente", idPaciente);

        $.ajax({

            url: "<?= route_to('admin/pacientes/traerPaciente') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idPaciente").val(respuesta["id"]);
                $("#nombres").val(respuesta["nombres"]);
                $("#apellidos").val(respuesta["apellidos"]);
                $("#correoElectronico").val(respuesta["correoElectronico"]);
                $("#telefono").val(respuesta["telefono"]);
                $("#dni").val(respuesta["dni"]);

            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaPacientes").on("click", ".btnEliminarPaciente", function () {

        var idPaciente = $(this).attr("idPaciente");


        console.log("eliminar");

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/pacientes') ?>/` + idPaciente,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                           $(".tablaPacientes").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

En views creamos la carpeta pacientesModulos y dentro de esa capeta creamos el archivo modalCaptura.php con la siguiente código

<!-- Modal Pacientes -->
<div class="modal fade" id="modalAgregarPaciente" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('patients.createEdit') ?></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form id="form-paciente" class="form-horizontal">
                    <input type="hidden" id="idPaciente" name="idPaciente" value="0">



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('patients.names') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="nombres" id="nombres" class="form-control <?= session('error.nombres') ? 'is-invalid' : '' ?>" value="<?= old('nombres') ?>" placeholder="<?= lang('patients.names') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('patients.lastName') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="apellidos"  id="apellidos"class="form-control <?= session('error.apellidos') ? 'is-invalid' : '' ?>" value="<?= old('apellidos') ?>" placeholder="<?= lang('patients.lastName') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="dni" class="col-sm-2 col-form-label"><?= lang('patients.dni') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-credit-card"></i></span>
                                </div>
                                <input type="text" name="dni"  id="dni" class="form-control <?= session('error.dni') ? 'is-invalid' : '' ?>" value="<?= old('dni') ?>" placeholder="<?= lang('patients.dni') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="phone" class="col-sm-2 col-form-label"><?= lang('patients.phone') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-mobile"></i></span>
                                </div>
                                <input type="text" name="telefono" id="telefono" class="form-control <?= session('error.phone') ? 'is-invalid' : '' ?>" value="<?= old('telefono') ?>" placeholder="<?= lang('patients.phone') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="correoElectronico" class="col-sm-2 col-form-label"><?= lang('patients.email') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-envelope"></i></span>
                                </div>
                                <input type="text" name="correoElectronico" id="correoElectronico" class="form-control <?= session('error.correoElectronico') ? 'is-invalid' : '' ?>" value="<?= old('correoElectronico') ?>" placeholder="<?= lang('patients.email') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarPaciente"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarPaciente', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idPaciente").val("0");
        
           $("#btnGuardarPaciente").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnEditarPaciente', function (e) {


        var idPaciente = $(this).attr("idPaciente");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idPaciente").val(idPaciente);
        $("#btnGuardarPaciente").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarPaciente', function (e) {

        var idPaciente = $("#idPaciente").val();
        var nombres = $("#nombres").val();
        var apellidos = $("#apellidos").val();
        var telefono = $("#telefono").val();
        var dni = $("#dni").val();
        var correoElectronico = $("#correoElectronico").val();

        var ajaxPaciente = "ajaxPaciente";


        $("#btnGuardarPaciente").attr("disabled", true);


        var datos = new FormData();
        datos.append("idPaciente", idPaciente);
        datos.append("nombres", nombres);
        datos.append("apellidos", apellidos);
        datos.append("telefono", telefono);
        datos.append("dni", dni);
        datos.append("correoElectronico", correoElectronico);

        $.ajax({

            url: "<?= route_to('admin/pacientes/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaPacientes').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarPaciente").removeAttr("disabled");

                   
                     $('#modalAgregarPaciente').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarPaciente").removeAttr("disabled");
                  //  $('#modalAgregarPaciente').modal('hide');

                }

            }

        }

        )




    });
</script>


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

Ahora configuramos las rutas en app/config/routes.php

    $routes->resource('pacientes', [
        'filter' => 'permission:pacientes-permiso',
        'controller' => 'PacientesController',
        'except' => 'show'
    ]);

    $routes->post('pacientes/guardar', 'PacientesController::guardar');

    $routes->post('pacientes/traerPaciente', 'PacientesController::traePaciente');

    $routes->post('pacientes/traerPacientesAjax', 'PacientesController::traerPacientesAjax');

En languaje en la carpeta es creamos el archivo patients con el siguiente codigo

<?php

$patients["names"] = "Firs Name";
$patients["lastName"] = "Last Name";
$patients["dni"] = "CARD ID";
$patients["phone"] = "Phone";
$patients["email"] = "E-Mail";
$patients["createdAt"] = "Date Creation";
$patients["updateAt"] = "Date Update";
$patients["add"] = "Add Patient";
$patients["actions"] = "Actions";
$patients["createEdit"] = "Create  / Edit Patient";
$patients["title"] = "Patient";
$patients["subtitle"] = "List of Patients";

$patients["msg_delete"] = "Patient has deleted .";
$patients["msg_get_fail"] = "The patient not exist or has deleted.";





return $patients;

Y en la carpeta es de español creamos el archivo patients con el siguiente código

<?php

$patients["names"] = "Nombres";
$patients["lastName"] = "Apellidos";
$patients["dni"] = "INE";
$patients["phone"] = "Telefono";
$patients["email"] = "Correo Electronico";
$patients["add"] = "Agregar Paciente";
$patients["updateAt"] = "Fecha Modificacion";
$patients["actions"] = "Acciones";
$patients["createEdit"] = "Crear / Editar Paciente";
$patients["title"] = "Pacientes";
$patients["subtitle"] = "Lista de Pacientes";

$patients["msg_delete"] = "El paciente ha sido eliminado correctamente.";
$patients["msg_get_fail"] = "El paciente no existe o fue eliminado.";




return $patients;
Y listo ya tenemos nuestro CRUD de pacientes

CI 4.0 MedicalSoft Datatable para tabla de Bitacora

Ya vimos como guardar los datos de la bitácora, ahora veremos como usar el datatable con server side para mostrar los datos de guardados en la bitácora

Primero creamos el archivo BitacoraModel.php de la siguiente forma en la carpeta app/model/ aunque a estas altura ya debemos tenerlo

Leer Mas: CI 4.0 MedicalSoft Datatable para tabla de Bitacora
<?php

namespace App\Models;

use CodeIgniter\Model;

class BitacoraModel extends Model
{
    protected $table      = 'bitacora';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','descripcion', 'usuario'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    = [];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
    
    
    const ORDERABLE = [
        1 => 'id',
        2 => 'descripcion',
        3 => 'usuario',
        4 => 'created_at',
        
    ];

    /**
     * Get resource data.
     *
     * @param string $search
     *
     * @return \CodeIgniter\Database\BaseBuilder
     */
   public function getResource(string $search = '')
    {
        $builder = $this->builder()
            ->select('id, descripcion, usuario, created_at');

        $condition = empty($search)
            ? $builder
            : $builder->groupStart()
                ->like('descripcion', $search)
                ->orLike('Usuario', $search)
                ->orLike('created_at', $search)
            ->groupEnd();

        return $condition->where('deleted_at', null);
    }
}

Para convertir fácilmente el resultado de la consulta SQL del QUERYBUILDER a formato json que usa datatable instalamos en la terminal el siguiente componente de hermawan usando composer

composer require hermawan/codeigniter4-datatables

Creamos el archivo BitacoraController.php con los siguientes, si observamos en la función index vemos que solo con una linea se crea el json

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class BitacoraController extends BaseController {

    use ResponseTrait;

    protected $bitacora;

    public function __construct() {
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
            $start = $this->request->getGet('start');
            $length = $this->request->getGet('length');
            $search = $this->request->getGet('search[value]');
            $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
            $dir = $this->request->getGet('order[0][dir]');
*/

           $datos = $this->bitacora->select('id,descripcion,usuario,created_at');
           // $resultado = $this->bitacora->findAll();
                                   // $this->bitacora->getResource()->countAllResults(),
                                   // $this->bitacora->getResource($search)->countAllResults()
           return \Hermawan\DataTables\DataTable::of($datos)->toJson();
        }
        
        $titulos["title"] = lang('log.title');
        $titulos["subtitle"] = lang('log.subtitle');
        //$data["data"] = $datos;
        return view('bitacora',$titulos);
    }

    public function tabla() {


        if ($this->request->isAJAX()) {
            $start = $this->request->getGet('start');
            $length = $this->request->getGet('length');
            $search = $this->request->getGet('search[value]');
            $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
            $dir = $this->request->getGet('order[0][dir]');

            return $this->respond(Collection::datatable(
                                    $this->bitacora->getResource($search)->orderBy($order, $dir)->limit($length, $start)->get()->getResultObject(),
                                    $this->bitacora->getResource()->countAllResults(),
                                    $this->bitacora->getResource($search)->countAllResults()
            ));
        }
    }

}

Creamos la vista en app/views/bitacora.php

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="card-tools">
            <div class="btn-group">


            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaBitacora" class="table table-striped table-hover va-middle tablaBitacora">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th><?= lang('log.description') ?></th>
                                <th><?= lang('log.user') ?></th>
                                <th><?= lang('log.date') ?></th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>

<?= $this->section('js') ?>
<script>

/**
 * Cargamos la tabla
 */
$('.tablaBitacora').DataTable( {
    "ajax": "<?= route_to('admin/bitacora/manage') ?>",
    "deferRender": true,
     "serverSide" : true,
  "retrieve": true,
  "processing": true

} )

    
</script>
<?= $this->endSection() ?>

En las rutas app/config/routes.php agregamos la siguiente linea

    /*
     * RUTA BITACORA
     */

    $routes->resource('bitacora', [
        'filter' => 'permission:bitacora-permiso',
        'controller' => 'BitacoraController',
        'except' => 'show'
    ]);
Al final este es el resultado

Crear un Conversor de Facturas XML Mexicanas a PDF en CodeIgniter4

En este pequeño tutorial vamos a mostrar como crear rápidamente un conversor XML a PDF de las facturas electrónicas Mexicanas
Requisitos

  1. Tener instalado la versión mas reciente de Composer
  2. Conocimiento básico de PHP y Programación
  3. Ganas de aprender

Paso uno creamos el proyecto en CodeIgniter 4

composer create-project codeigniter4/appstarter convertidorXMLPDF

Actualizamos

composer update

Instalamos de composer el paquete que convierte el xml PDF

composer require phpcfdi/cfditopdf

creamos el archivo FileUpload.php en controladores

<?php 
namespace App\Controllers;
// use App\Models\FormModel;
use CodeIgniter\Controller;
class FileUpload extends Controller
{
    public function index() 
    {
        return view('home');
    }
    function upload() { 
        helper(['form', 'url','filesystem']);



 
            $xml = $this->request->getFile('file');
           /* $img->move(WRITEPATH . 'uploads');
    
            $data = [
               'name' =>  $img->getName(),
               'type'  => $img->getClientMimeType()
            ];
    
            $save = $db->insert($data);
            print_r('File has successfully uploaded');     */


         //  echo $_FILES['file']['tmp_name'];
            $string = file_get_contents($_FILES['file']['tmp_name']);

         
            // clean cfdi
            $xml = \PhpCfdi\CfdiCleaner\Cleaner::staticClean($string);

            
         
            // create the main node structure
            $comprobante = \CfdiUtils\Nodes\XmlNodeUtils::nodeFromXmlString($xml);

            // create the CfdiData object, it contains all the required information
            $cfdiData = (new \PhpCfdi\CfdiToPdf\CfdiDataBuilder())
                ->build($comprobante);

            // create the converter
            $converter = new \PhpCfdi\CfdiToPdf\Converter(
                new \PhpCfdi\CfdiToPdf\Builders\Html2PdfBuilder()
            );

            // create the invoice as output.pdf
            $converter->createPdfAs($cfdiData,  'output.pdf');
            
                $filename = WRITEPATH . 'uploads\output.pdf';
                
                echo '
                <script type="text/javascript">
                    window.open("/output.pdf" , "_blank");
                  </script>';
                
             
               return view('welcome_message');
                  
                
        }
     
    
}

*Agregamos el formulario en las vistas en este caso al archivo welcome_message.php

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Conversor CFDI!</title>
        <meta name="description" content="Pequeño y poderoso conversor de CFDI XML a PDF hecho en CodeIgnter">
        <meta name="keywords" content="CFDI, CFDI 4.0, CFDI 3.3, Representación Impresa, CodeIgniter, Factura Electrónica"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="shortcut icon" type="image/png" href="/favicon.ico"/>

        <!-- STYLES -->

        <style {csp-style-nonce}>
            * {
                transition: background-color 300ms ease, color 300ms ease;
            }
            *:focus {
                background-color: rgba(221, 72, 20, .2);
                outline: none;
            }
            html, body {
                color: rgba(33, 37, 41, 1);
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
                font-size: 16px;
                margin: 0;
                padding: 0;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
                text-rendering: optimizeLegibility;
            }
            header {
                background-color: rgba(247, 248, 249, 1);
                padding: .4rem 0 0;
            }
            .menu {
                padding: .4rem 2rem;
            }
            header ul {
                border-bottom: 1px solid rgba(242, 242, 242, 1);
                list-style-type: none;
                margin: 0;
                overflow: hidden;
                padding: 0;
                text-align: right;
            }
            header li {
                display: inline-block;
            }
            header li a {
                border-radius: 5px;
                color: rgba(0, 0, 0, .5);
                display: block;
                height: 44px;
                text-decoration: none;
            }
            header li.menu-item a {
                border-radius: 5px;
                margin: 5px 0;
                height: 38px;
                line-height: 36px;
                padding: .4rem .65rem;
                text-align: center;
            }
            header li.menu-item a:hover,
            header li.menu-item a:focus {
                background-color: rgba(221, 72, 20, .2);
                color: rgba(221, 72, 20, 1);
            }
            header .logo {
                float: left;
                height: 44px;
                padding: .4rem .5rem;
            }
            header .menu-toggle {
                display: none;
                float: right;
                font-size: 2rem;
                font-weight: bold;
            }
            header .menu-toggle button {
                background-color: rgba(221, 72, 20, .6);
                border: none;
                border-radius: 3px;
                color: rgba(255, 255, 255, 1);
                cursor: pointer;
                font: inherit;
                font-size: 1.3rem;
                height: 36px;
                padding: 0;
                margin: 11px 0;
                overflow: visible;
                width: 40px;
            }
            header .menu-toggle button:hover,
            header .menu-toggle button:focus {
                background-color: rgba(221, 72, 20, .8);
                color: rgba(255, 255, 255, .8);
            }
            header .heroe {
                margin: 0 auto;
                max-width: 1100px;
                padding: 1rem 1.75rem 1.75rem 1.75rem;
            }
            header .heroe h1 {
                font-size: 2.5rem;
                font-weight: 500;
            }
            header .heroe h2 {
                font-size: 1.5rem;
                font-weight: 300;
            }
            section {
                margin: 0 auto;
                max-width: 1100px;
                padding: 2.5rem 1.75rem 3.5rem 1.75rem;
            }
            section h1 {
                margin-bottom: 2.5rem;
            }
            section h2 {
                font-size: 120%;
                line-height: 2.5rem;
                padding-top: 1.5rem;
            }
            section pre {
                background-color: rgba(247, 248, 249, 1);
                border: 1px solid rgba(242, 242, 242, 1);
                display: block;
                font-size: .9rem;
                margin: 2rem 0;
                padding: 1rem 1.5rem;
                white-space: pre-wrap;
                word-break: break-all;
            }
            section code {
                display: block;
            }
            section a {
                color: rgba(221, 72, 20, 1);
            }
            section svg {
                margin-bottom: -5px;
                margin-right: 5px;
                width: 25px;
            }
            .further {
                background-color: rgba(247, 248, 249, 1);
                border-bottom: 1px solid rgba(242, 242, 242, 1);
                border-top: 1px solid rgba(242, 242, 242, 1);
            }
            .further h2:first-of-type {
                padding-top: 0;
            }
            footer {
                background-color: rgba(221, 72, 20, .8);
                text-align: center;
            }
            footer .environment {
                color: rgba(255, 255, 255, 1);
                padding: 2rem 1.75rem;
            }
            footer .copyrights {
                background-color: rgba(62, 62, 62, 1);
                color: rgba(200, 200, 200, 1);
                padding: .25rem 1.75rem;
            }
            @media (max-width: 629px) {
                header ul {
                    padding: 0;
                }
                header .menu-toggle {
                    padding: 0 1rem;
                }
                header .menu-item {
                    background-color: rgba(244, 245, 246, 1);
                    border-top: 1px solid rgba(242, 242, 242, 1);
                    margin: 0 15px;
                    width: calc(100% - 30px);
                }
                header .menu-toggle {
                    display: block;
                }
                header .hidden {
                    display: none;
                }
                header li.menu-item a {
                    background-color: rgba(221, 72, 20, .1);
                }
                header li.menu-item a:hover,
                header li.menu-item a:focus {
                    background-color: rgba(221, 72, 20, .7);
                    color: rgba(255, 255, 255, .8);
                }
            }
        </style>
    </head>
    <body>

        <!-- HEADER: MENU + HEROE SECTION -->
        <header>

            <div class="menu">
                <ul>
                    <li class="logo">
                        <a href="https://cesarsystems.com.mx/" target="_blank">
                            <img src="<?php echo base_url();?>/logo.png"
                                 aria-label="Visita mi pagina oficial para mas programas y utilidades!"/></img>
                        </a>
                    </li>
                    <li class="menu-toggle">
                        <button onclick="toggleMenu();">&#9776;</button>
                    </li>
                    <li class="menu-item hidden"><a href="#">Inicio</a></li>
                    <li class="menu-item hidden"><a href="https://cesarsystems.com.mx/" target="_blank">Descargas</a>
                    </li>
                    <li class="menu-item hidden"><a href="https://www.youtube.com/c/rasec555" target="_blank">Comunidad</a></li>
                    <li class="menu-item hidden"><a
                            href="https://patreon.com/user?u=74078772&utm_medium=clipboard_copy&utm_source=copyLink&utm_campaign=creatorshare_creator" target="_blank">Patrocinar</a>
                    </li>
                </ul>
            </div>

            <div class="heroe">

                <h1>Bienvenido a Conversor XML A PDF V2</h1>

                <h2>Un simple software para convertir los CFDI XML a PDF</h2>

            </div>

        </header>

        <!-- CONTENT -->

        <section>

            <h1>Acerca de este programa</h1>

            <p>Este programa toma los archivos XML de la factura 3.3 o 4.0 para convertirlos a una representación generica impresa de un Comprobante Fiscal Digital</p>

            <p>Para convertir el XML a PDF puede arrastrar el archivo XML en el Boton llamado "Seleccionar archivo" </p>

            <p>En caso de que no salga el PDF asegurate de no tener bloqueado el popup o desbloquealo para esta pagina ya que el pdf saldra en el popup" </p>



            <form method="post" action="<?php echo base_url('FileUpload/upload'); ?>" enctype="multipart/form-data">
                <div class="form-group">
                    <label>Seleccione el archivo XML </label>
                    <input type="file" name="file" id="file"  accept=".xml" class="form-control">
                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-danger">Generar PDF</button>
                </div>
            </form>

        </section>

        <div class="further">

            <section>

                <h1>¡Ir por más!</h1>

                <h2>
                    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><rect x='32' y='96' width='64' height='368' rx='16' ry='16' style='fill:none;stroke:#000;stroke-linejoin:round;stroke-width:32px'/><line x1='112' y1='224' x2='240' y2='224' style='fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px'/><line x1='112' y1='400' x2='240' y2='400' style='fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px'/><rect x='112' y='160' width='128' height='304' rx='16' ry='16' style='fill:none;stroke:#000;stroke-linejoin:round;stroke-width:32px'/><rect x='256' y='48' width='96' height='416' rx='16' ry='16' style='fill:none;stroke:#000;stroke-linejoin:round;stroke-width:32px'/><path d='M422.46,96.11l-40.4,4.25c-11.12,1.17-19.18,11.57-17.93,23.1l34.92,321.59c1.26,11.53,11.37,20,22.49,18.84l40.4-4.25c11.12-1.17,19.18-11.57,17.93-23.1L445,115C443.69,103.42,433.58,94.94,422.46,96.11Z' style='fill:none;stroke:#000;stroke-linejoin:round;stroke-width:32px'/></svg>
                    Descargas y tutoriales
                </h2>

                <p>Puedes encontrar mas aplicativos, manuales y tutoriales en la siguiente pagina <a href="https://cesarsystems.com.mx/"
                                                target="_blank">Pagina Principal</a> !</p>

                <h2>
                    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path d='M431,320.6c-1-3.6,1.2-8.6,3.3-12.2a33.68,33.68,0,0,1,2.1-3.1A162,162,0,0,0,464,215c.3-92.2-77.5-167-173.7-167C206.4,48,136.4,105.1,120,180.9a160.7,160.7,0,0,0-3.7,34.2c0,92.3,74.8,169.1,171,169.1,15.3,0,35.9-4.6,47.2-7.7s22.5-7.2,25.4-8.3a26.44,26.44,0,0,1,9.3-1.7,26,26,0,0,1,10.1,2L436,388.6a13.52,13.52,0,0,0,3.9,1,8,8,0,0,0,8-8,12.85,12.85,0,0,0-.5-2.7Z' style='fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px'/><path d='M66.46,232a146.23,146.23,0,0,0,6.39,152.67c2.31,3.49,3.61,6.19,3.21,8s-11.93,61.87-11.93,61.87a8,8,0,0,0,2.71,7.68A8.17,8.17,0,0,0,72,464a7.26,7.26,0,0,0,2.91-.6l56.21-22a15.7,15.7,0,0,1,12,.2c18.94,7.38,39.88,12,60.83,12A159.21,159.21,0,0,0,284,432.11' style='fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px'/></svg>
                    Grupos
                </h2>

                <p>Puede seguirnos en los siguientes grupos y redes sociales como <a href="https://www.youtube.com/c/rasec555"
                                      target="_blank">Canal de youtube </a>, 
                                      
                                      <a href="https://t.me/CesarSystems"
                                      target="_blank">Canal de Telegram</a> ,
                                      
                                      <a href="https://twitter.com/JulioLeyvaR"
                                      target="_blank">Twitter</a> ,
                                      
                                       <a href="https://odysee.com/@JulioCesarLeyvaRodriguez:9"
                                      target="_blank">Odysee</a> y
                                      
                                      <a href="https://www.facebook.com/rasec555/"
                                      target="_blank">Facebook</a> 
                
                
                </p>

                <h2>
                    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><line x1='176' y1='48' x2='336' y2='48' style='fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px'/><line x1='118' y1='304' x2='394' y2='304' style='fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px'/><path d='M208,48v93.48a64.09,64.09,0,0,1-9.88,34.18L73.21,373.49C48.4,412.78,76.63,464,123.08,464H388.92c46.45,0,74.68-51.22,49.87-90.51L313.87,175.66A64.09,64.09,0,0,1,304,141.48V48' style='fill:none;stroke:#000;stroke-linecap:round;stroke-miterlimit:10;stroke-width:32px'/></svg>
                    Patrocinar
                </h2>

                <p>Puedes apoyar nuestros proyectos en Patreon y obtendras acceso exclusivo a los fuentes mas recientes
                    <a href="https://patreon.com/user?u=74078772&utm_medium=clipboard_copy&utm_source=copyLink&utm_campaign=creatorshare_creator" target="_blank">
                        Unete</a> </p>

            </section>

        </div>

        <!-- FOOTER: DEBUG INFO + COPYRIGHTS -->

        <footer>
            <div class="environment">

                <p>Tiempo de Renderizado {elapsed_time} seconds</p>

            

            </div>

            <div class="copyrights">

                <p>&copy; <?= date('Y') ?> Conversor XML a PDF es un proyecto de código abierto lanzado con la licencia MIT
                    open source licence.</p>

            </div>

        </footer>

        <!-- SCRIPTS -->

        <script>
            function toggleMenu() {
                var menuItems = document.getElementsByClassName('menu-item');
                for (var i = 0; i < menuItems.length; i++) {
                    var menuItem = menuItems[i];
                    menuItem.classList.toggle("hidden");
                }
            }
        </script>

        <!-- -->

    </body>
</html>

Y por último agregamos esto en las rutas

$routes->get('/', 'FileUpload::index');
$routes->match(['get', 'post'], 'FileUpload/upload', 'FileUpload::upload');

y listo pueden ver un ejemplo de como quedo en el siguiente enlace https://xml2pdf.cesarsystems.com.mx/

Conversor CFDI 3.3 4.0 XML A PDF

Esta utilidad hecha en CodeIgniter 4 y usando ElectroJS para la versión de escritorio, nos servirá para poder convertir nuestros XML a la representación impresa de la factura digital.

Para probar el programa en linea pueden entrar en el siguiente enlace https://xml2pdf.cesarsystems.com.mx/

Es posible que la primera vez no funcione ya que el PDF sale en un popup por lo cual tienen que habilitar en el navegador para que permita visualizarlo, en el caso de la versión de escritorio funcionara correctamente.

Es una app muy sencilla pero se se agregaran funciones extras entre mas se descargue y use

Normalmente sale cono una X roja, le dan click y le dan permitir
El resultado final quedaría de esta forma

Dejo ambos enlaces tanto para la App de escritorio como la que esta en linea

CREANDO SISTEMA DE CONSULTORIO MEDICO – INSTALANDO CODEIGNITER 4.1.1 EN PHP8 CAPITULO #1

En esta serie de vídeos veremos como hacer un sistema basico de consultorio medico En este capitulo veremos las caracteristicas de codeigniter hasta su instalación básica cualquier distribución de GNU/Linux basade de Ubuntu 18.04 en este caso es Zorin Linux Core 15.3
0:00 Intro
1:00 Descargar Code Igniter
1:32 Caracteristicas
4:15 Requisitos del Servidor
6:13 Configurando CodeIgniter con Netbeans 12.0
10:30 Instalando Extensiones PHP Necesarias

VK
https://vk.com/id543521033

FACEBOOK
https://www.facebook.com/rasec555

TWITTER
https://twitter.com/Cesar101290

CHANNEL TELEGRAM
https://telegram.me/CesarSystems


PAGINA WEB https://cesarsystems.com.mx/

MOVIL / WHATSAPP

6688612348

Página 4 de 4

Creado con WordPress & Tema de Anders Norén