Creando nuestro CRUD con grocery CRUD y CodeIgniter 3
- Andrés Cruz
Los CRUD, aquellas operaciones para Crear, Leer, Actualizar y Eliminar registros; fundamentales en todo desarrollo PHP o una cualquier aplicación en PHP tiene al menos un proceso CRUD (Create, Read, Update, Delete) o al menos alguna(s) de esta(s) operación(es).
Para hacer un CRUD sencillo en CodeIgniter, aunque se nos hace más fácil que el PHP tradicional, aun así tenemos que darle bastante a la caña, desarrollar varias vistas, métodos en el controlador y por supuesto tener la base de datos lista; antes de entrar al proceso de creación de un CRUD con Grocery CRUD quiero refrescarles un poco la memoria y por eso esta introducción algo dolorosa como verás a continuación; pero si quiere ir directamente a cómo usar Grocery CRUD para hacer el CRUD, dale un clic aquí:
Creando un CRUD para CodeIgniter de manera manual
Así que para hacer una sencilla tabla como la siguiente:
Tabla en nuestra base de datos para Crear, Leer, Actualizar y Eliminar registros
Lo primero que necesitamos es algo que listar; para ello la siguiente tabla en base de datos:
CREATE DATABASE crud;
USE crud;
CREATE TABLE IF NOT EXISTS `categories` (
`category_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`url_clean` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `categories` (`category_id`, `name`, `url_clean`) VALUES
(1, 'Categoria 1', 'categoria-1'),
(3, 'Categoria 3', 'categoria-3'),
(4, 'Categoria 4', 'categoria-4');
ALTER TABLE `categories`
ADD PRIMARY KEY (`category_id`);
ALTER TABLE `categories`
MODIFY `category_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
Como puedes ver es una tabla sencilla con la cual vamos a trabajar para hacer nuestro pequeño experimento; cuenta con un par de campos, un nombre y un campo para agregar el mismo nombre pero en un formato limpio; es decir, sin espacios y sin caracteres especiales, solo números y letras; todo esto es teórico pero para que te hagas mejor a la idea.
Modelos para la creación del CRUD
Para nuestro modelos tenemos que definir nuestro "mini" CRUD para hacer las operaciones más comunes como vemos a continuación:
class Category extends CI_Model {
public $table = "categories";
public $table_id = "category_id";
function findAll() {
$this->db->select();
$this->db->from($this->table);
$query = $this->db->get();
return $query->result();
}
function find($id) {
$this->db->select();
$this->db->from($this->table);
$this->db->where($this->table_id, $id);
$query = $this->db->get();
return $query->row();
}
function update($id, $data) {
$this->db->where($this->table_id, $id);
$this->db->update($this->table, $data);
}
function delete($id) {
$this->db->where($this->table_id, $id);
$this->db->delete($this->table);
}
function insert($data) {
$this->db->insert($this->table, $data);
return $this->db->insert_id();
}
}
Funciones en nuestro modelo para insertar, actualizar, encontrar un registro o todos ellos y por supuesto para borrar registros son necesarios y los puedes ver en el listado anterior.
Controlador y las acciones para las operaciones de Crear, Leer, Actualizar y Eliminar
En nuestro controlador no se queda atrás y necesitamos una gran cantidad de ... código, tanto para construir el listado con category_list
, también para guardar y actualizar nuevas categorías category_save
y por supuesto para eliminar registro, que lo hacemos con category_delete
:
/********
* CRUD PARA LOS CATEGORY
*/
public function category_list() {
$data["categories"] = $this->Category->findAll();
$this->load->view("admin/category/list", $data);
}
public function category_save($category_id = NULL) {
if ($category_id == NULL) {
// crear category
$data['name'] = "";
$data['url_clean'] = "";
} else {
// edicion category
$category = $this->Category->find($category_id);
$data['name'] = $category->name;
$data['url_clean'] = $category->url_clean;
}
if ($this->input->server('REQUEST_METHOD') == "POST") {
$this->form_validation->set_rules('name', 'Nombre', 'required|min_length[10]|max_length[100]');
$data['name'] = $this->input->post("name");
$data['url_clean'] = $this->input->post("url_clean");
if ($this->form_validation->run()) {
// nuestro form es valido
$save = array(
'name' => $this->input->post("name"),
'url_clean' => $url_clean
);
if ($category_id == NULL)
$category_id = $this->Category->insert($save);
else
$this->Category->update($category_id, $save);
}
}
$this->load->view("admin/category/save", $data);
}
public function category_delete($category_id = NULL) {
if ($category_id !== NULL) {
$this->Category->delete($category_id);
echo 1;
}
}
Aquí podríamos completarlo más empleando variables en sesión con los mensajes correspondientes pero para dejarlo simple lo dejaremos así.
Vistas para el listados de las categorías con las funciones de nuestro CRUD
Aquí vamos a suponer que empleamos modal de Bootstrap que como cualquier otro recurso o framework CSS funciona perfecto con CodeIgniter; también vamos a emplear un poco de jQuery y Ajax
Listado de los registros en nuestra tabla
<table class="table table-condensed">
<tbody>
<tr>
<th style="width: 10px">#</th>
<th>Nombre</th>
<th>Acciones</th>
</tr>
<?php foreach ($categories as $key => $c) : ?>
<tr>
<td><?php echo $c->category_id ?></td>
<td><?php echo $c->name ?></td>
<td>
<a class="btn btn-sm btn-primary"
href="<?php echo base_url() . 'admin/category_save/' . $c->category_id ?>">
<i class="fa fa-pencil"></i> Editar
</a>
<br>
<a class="btn btn-sm btn-danger"
data-toggle="modal"
data-target="#deleteModal"
href="#"
data-categoryid="<?php echo $c->category_id ?>">
<i class="fa fa-remove"></i> Eliminar
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New message</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-danger" id="borrar-category" data-dismiss="modal">Eliminar</button>
</div>
</div>
</div>
</div>
<script>
var category_id = 0;
var buttondelete;
// abrir el modal
$('#deleteModal').on('show.bs.modal', function (event) {
buttondelete = $(event.relatedTarget) // Button that triggered the modal
category_id = buttondelete.data('categoryid') // Extract info from data-* attributes
// If necessary, you could initiate an AJAX request here (and then do the updating in a callback).
// Update the modal's content. We'll use jQuery here, but you could use a data binding library or other methods instead.
var modal = $(this)
modal.find('.modal-title').text('Seguro que desea eliminar la Categoria seleccionada ' + category_id)
});
// llamar a eliminar
$("#borrar-category").click(function () {
$.ajax({
url: "<?php echo base_url() ?>admin/category_delete/" + category_id
}).done(function (res) {
if (res == 1) {
$(buttondelete).parent().parent().remove();
}
});
});
</script>
Como puedes ver, nuestro código consta de 3 bloques, dibujar la tabla, el modal de Bootstrap y el jQuery con el ajax; también empleamos el fontawesome; estos dos framework CSS son opcionales, pero ya que nos echamos todo este trabajo con esto, al menos que luzcan bien :); finalmente, queda de la siguiente forma:
Formulario de creación y actualización de las categorías
Siguiendo con nuestro código para nuestro CRUD, ahora vamos a ver el código para los formularios de edición y creación; que es el mismo:
<?php echo form_open('', 'class="my_form"'); ?>
<div class="form-group">
<?php
echo form_label('Nombre', 'name');
?>
<?php
$text_input = array(
'name' => 'name',
'id' => 'name',
'value' => $name,
'class' => 'form-control input-lg',
);
echo form_input($text_input);
?>
<?php echo form_error('name', '<div class="text-error">', '</div>') ?>
</div>
<div class="form-group">
<?php
echo form_label('Url limpia', 'url_clean');
?>
<?php
$text_input = array(
'name' => 'url_clean',
'id' => 'url_clean',
'value' => $url_clean,
'class' => 'form-control input-lg',
);
echo form_input($text_input);
?>
<?php echo form_error('url_clean', '<div class="text-error">', '</div>') ?>
</div>
<?php echo form_submit('mysubmit', 'Guardar', 'class="btn btn-primary"') ?>
<?php echo form_close() ?>
Como puedes ver, empleamos el helper para construir formularios de CodeIgniter; y no solamente eso, también empleamos la organización para emplear los formularios de bootstrap; y queda de la siguiente forma:
En esta oportunidad no empleamos una vista para la visualización de los datos; al ser poco registros no vale la pena, pero de igual manera la vista sería similar a la de los formularios para crear y editar registros.
Y bueno como ves, es bastante trabajoso, si agregamos una nueva columna o algo por el estilo tenemos que darle una vuelta a todas las vistas, y controlador; pero no te preocupes; te traemos una excelente librería para automatizar todo este proceso.
Crear un CRUD con Grocery CRUD en 5 minutos
Ahora veremos como instalar y descargar esta estupenda herramienta o mejor dicho librería (para respetar el modo de integración de esta herramienta a CodeIgniter) que permite crear una completa y compleja (hasta cierto punto) sistema de tablas administrativa (con esto nos referimos a tablas con búsqueda, por campos, paginación, ordenación e importación; en resumen crear el sistema CRUD por sus siglas "Crear, Leer, Actualizar y Borrar" -en inglés: Create, Read, Update and Delete-) empleando unas pocas líneas de código como las siguientes:
$crud = new grocery_CRUD();
$crud->set_theme('bootstrap');
$crud->set_table('contactos');
$output = $crud->render();
Este vendría siendo el resultado:
Además de la tabla administrativa muy útil de por si, también crea las funcionalidades de crear, actualizar y eliminar registros; lo que es un sistema CRUD.
jQuery datatable como elemento central en los listados de Grocery CRUD
Como veras si algunas vez has empleado las estupendas tablas de jQuery datatable son las usadas internamente para generar las tablas de la librería de grocery CRUD son las mismas; en general, lo "malo" de usar estas tablas o plugin jQuery, es que requieren bastante configuración, entre el mismo JavaScript, con la conexión del JSON -que es el mejor esquema que podríamos emplear (o similar)-, armar cada una de las columnas, dar formato a la tabla y desarrollar el servicio del JSON, el nombrado de las funciones y parámetros una y otra versión tienden a cambiar los métodos, etc.
Grocery CRUD nos ayuda en todo esto y para crear nuestro "núcleo" o elementos fundamentales para mostrar las tablas y CRUD (por sus siglas en inglés de: Create, Read, Update and Delete) que tendríamos que hacer a mano; apenar tendríamos que emplear algunas pocas líneas de código cuyos pasos resumidos serían:
- Crear nuestro controlador que nos devuelve un JSON para que se alimente la tabla (y todo lo que esto conlleva, como la creación de la consulta en el modelo).
- Cargar los plugins en nuestra vista.
- Definir las columnas que definimos en nuestro modelo y atarlas en la vista (siempre hay más de un problema en esta parte).
- Crear todo el JavaScript para realizar operaciones especiales: como la de modificación del registro listado, eliminar...
Cuando nos damos cuenta, tenemos nuestra estupenda tabla jQuery datatable con un montón de JavaScript y por ende se complica cuando queremos realizar modificaciones en la tabla y ni hablar si tenemos que replicar para varias tablas o CRUD en el sistema que estemos desarrollando.
La librería de grocery CRUD para CodeIgniter al rescate
La gente de grocery CRUD se les ocurrió una estupenda idea la cual es crear una librería para CodeIgniter (puedes consultar la hoja de "Futuras características") que ha simplificado todo lo mencionado anteriormente a esas simples líneas de código que vimos anteriormente:
Grocery CRUD no solo crea la jQuery datatable, si no también el completo CRUD de los registros.
Trabajando con Grocery CRUD
Ahora expliquemos cada una de las líneas vistas en un principio de esta entrada.
Instanciando la tabla Grocery CRUD
Primero creamos una instancia de la tabla con:
$crud = new grocery_CRUD();
Selección de la tabla
Para seleccionar el origen de nuestra data (que viene siendo una tabla o vista) se emplea el método $crud>set_table(NombreTabla)
; por ejemplo, para seleccionar la tabla 'contactos' haríamos algo como esto: $crud>set_table('contactos')
.
Carga de columnas
Es muy posible que no deseemos mostrar todo el contenido de la tabla de nuestra base de datos en nuestra tala administrativa y solo mostrar algunas columnas en particular; para esto empleamos: $crud>columns(Columnas)
; por ejemplo, para seleccionar un grupo de nuestras columnas:
$crud->columns('nombre', 'apellido', 'correo', 'telefono', 'cif', 'enombre', 'suspendida');
Campos de edición/creación
También es posible que los campos de las columnas de las tablas no sean los mismos que los campos de creación y/o edición de un nuevo registro; para ello empleamos el método field
:
$crud->fields('cliente', 'apellido', 'telefono', 'correo', 'user_id');
Cláusulas Where para filtrar datos
Si no deseas traer todos los registros de una tabla, si no aquellos que cumplan una condición en específico, podemos emplear la cláusula where
de la siguiente manera:
$crud->where('procesado_numero', NULL);
Desactivar operaciones del CRUD
Como podemos ver, la tabla por defecto ofrece un CRUD completo sobre todos los datos; si queremos limitar esto tenemos una serie de métodos que debemos emplear para cada situación; por ejemplo, si no deseas permitir la eliminación de registros:
$crud->unset_delete();
O ver los registro que en realidad no es muy útil:
$crud->unset_read();
O actualizar los registros
$crud->unset_edit();
Inputs ocultos para datos de control
Muchas veces es necesario presentar campos ocultos, para el control de nuestra data; por ejemplo, campos de fechas, niveles asignados en ciertos módulos que debemos especificar en el mismo bloque que creamos nuestra tabla mediante grocery_CRUD()
para que puedan ser referenciadas en cualquier otra parte; por ejemplo, en la sección de los callback (próxima sección); para ello podemos emplear los inputs de tipo hidden
como en la siguiente línea de código:
$crud>field_type('user_id', 'hidden', $this>session>userdata("id"));
Ejecutando eventos al insertar nuevos registros
También existe un método callback que se ejecuta antes de insertar registros en base de datos y después de insertar registros en base de datos; esto es particularmente útil si necesitamos rellenar algunos campos de manera autogenerada; por ejemplo:
$crud>callback_before_insert(array($this, 'before_estados_callback'));
Y el método tiene la siguiente estructura:
function before_estados_callback($post_array) { $post_array['user_id'] = $this->session->userdata('id'); return $post_array; }
Como puedes ver, al callback se le pasa como parámetro todos los datos que definimos en el formulário.
El callback para ejecutar operaciones luego de la inserción:
$crud->callback_after_insert(array($this, 'after_cliente_callback'));
...
function after_cliente_callback($post_array, $pk) {
$user = $this->Users->find($post_array['user_id']);
$this->emails->registro($post_array['cliente'].' '.$post_array['apellido'], $user->passwd_inicial, $post_array['email']);
return $post_array;
}
Aquí hay un parámetro extra que corresponde la identificador del registro insertado.
Lógicamente existen métodos callbacks para la actualización y borrado de registros:
$crud->callback_before_update(array($this, 'before_cliente_update_callback')); $crud->callback_after_update(array($this, 'after_cliente_update_callback')); $crud->callback_before_delete(array($this, 'before_cliente_delete_callback')); $crud->callback_after_delete(array($this, 'after_cliente_delete_callback'));
Todo esto es muy util, por ejemplo si tenemos que encriptar datos antes de insertarlo en la bd la función callback_before_update
viene de perlas en este escenario, aunque recuerda que toda los campos de los registros que quieras referencias deben estar especificados en la función principal; es decir, la función que define a la variable CRUD.
Creando nuestras propias acciones o funcionalidades en Grocery CRUD
También podemos asociar el registro a una acción en específico que no forma parte del CRUD como tal, por ejemplo, actualizar manejar un estatus; para esto se emplea el siguiente código:
$crud->add_action('Suspender', '', 'admin/supender_contacto', 'ui-icon-check');
- El primer parámetro corresponde al nombre de la acción.
- Una URL para una imagen.
Esta corresponde a la acción a invocar para este parámetro: function supender_contacto($id) {
$this->load->model('Mempresa_contacto');
$this->session->set_flashdata('tipo_m', 'success');
$this->session->set_flashdata('text', 'Cuenta suspendida.');
$this->Mempresa_contacto->suspender($id);
redirect("/admin/empresa");
}
Como vemos, es una simple función que recibe como parámetro el identificador de ese registro; es algo tosco este comportamiento, ya que al aplicar la acción para actualizar algo tan sencillo como un estatus debe culminar en la carga completa de la aplicación; sería interesante que la acción se le pueda atar a un campo de la tabla y la actualización se procese mediante AJAX; pero no ofrece tal opción.
Creando nuestras propias columnas en Grocery CRUD
También podemos especificar columnas que no se encuentren en el modelo de datos para mostrar alguna otra información precargada, por ejemplo, podemos extender el punto anterior y separar nuestras opciones para el manejo de la data desde otro bloque; por ejemplo, para crear una columna que llamaremos Opciones
primero debemos especificarla en la función columns()
y luego empleamos la función callback_column()
en donde especificamos la columna a insertar seguido del método que la define:
$crud->columns('username', 'email', 'Opciones'); $crud->callback_column('Opciones', array($this, 'usuario_opciones'));
Y la función luce de la siguiente manera:
function usuario_opciones($value, $row) { $tag = "<div class='actions'>"; $tag .= "<a href='" . base_url() . "index.php/admin/cambiar_password/$row->user_id' title='Cambiar contraseña'><i class='fa fa-2x fa-lock'></i></a>"; $tag . "</div>"; return $tag; }
Aquí podemos aplicar cualquier tipo de lógica, crear el contenido desde un helper, una vista aparte etc y podemos acceder a cada registro o fila por vez mediante la variable $row
como hacemos en el ejemplo anterior, que creamos una opción para los usuarios que es un link en el cual tomamos referenciado el identificador del mismo, esto es útil para agregar otras opciones sobre nuestro listado de datos que no existan en la tabla:
Evitar que Grocery CRUD cargue el jQuery en nuestro proyecto
Si en el sitio que estás empleando usas jQuery, para evitar cargarlo nuevamente podemos emplear la siguiente línea de código:
$crud->unset_jquery();
Crear relaciones con otras tablas
Si deseamos emplear alguna relación con otras tablas la cosa se nos puede complicar un poco; para ello hay que emplear el set_relation
:
$crud->set_relation('cliente_id', 'clientes', 'nombre_comercial');
La lógica es bastante sencilla, primero colocamos el campo en común que tiene la tabla establecida en nuestro Grocery CRUD con la que queremos vincular, en este caso cliente_id
, luego el nombre de la tabla y seguido el campo que deseamos mostrar; con esto logramos mostrar el campo 'nombre_comercial' en la tabla de jQuery Datatable y un campo de selección desplegable en los formularios de creación y edición:
$crud->set_table('proyectos');
$crud->fields('cliente_id', 'nombre', 'cantidad', 'fecha_alta', 'inicio_produccion', 'fecha_entrega', 'descripcion', 'precio', 'estado_id', 'user_id', 'estado_activo');
$crud->set_relation('cliente_id', 'clientes', '{nombre_comercial}- {cliente} {apellido}', array(
'clientes.email IS NOT NULL' => NULL,
'clientes.usuario' => 1,
'clientes.user_id != ' => $this->session->userdata('id')));
En el ejemplo anterior vemos que podemos emplear un esquema más complejo de set_relation
el cual nos permite mediante un array aplicar condiciones sobre la tabla clientes como si de un where se tratase; además, personalizamos el formato del campo a mostrar '{nombre_comercial}- {cliente} {apellido}' para mostrar múltiples campos de una sola vez.
Lamentablemente no podemos hacer múltiples vinculaciones con misma tabla, por ejemplo si quisiéramos mostrar los campos de nombre_comercial y nombre en columnas diferentes no hay manera.
Validando los formularios
Para aplicar validaciones en los formularios se emplea el método set_rules
:
$crud->set_rules('nombre', 'nombre', 'trim|max_length[100]|required');
$crud->set_rules('descripcion', 'descripcion', 'trim');
Cómo ves, las reglas aplicadas siguen la misma lógica que las validaciones de formularios en CodeIgniter y se separan con pipes.
Renderizar todo lo anterior (controlador)
Una vez configurada la tabla; empleamos el método:
$output = $crud->render();
Renderizar todo lo anterior (vista)
Finalmente, para mostrar la tabla en alguna vista seleccionada debemos emplear el siguiente código:
<?php foreach ($css_files as $file): ?>
<link type="text/css" rel="stylesheet" href="<?php echo $file; ?>" />
<?php endforeach; ?>
<?php foreach ($js_files as $file): ?>
<script src="<?php echo $file; ?>"></script>
<?php endforeach; ?>
<?php echo $output; ?>
Si lo analizamos, el código anterior no hace más que pintar los CSS y JavaScript necesarios para renderizar la tabla e imprimir la tabla.
Instalación de la librería
Los pasos para instalar la librería los puedes consultar desde el site oficial en: aunque todo se reduce a los siguientes pasos:
- Copiar el archivo de configuración
grocery_crud
en la carpeta conf. - Copiar el modelo
Grocery_crud_model
en la carpeta models. - Copiar el archivo
Grocery_CRUD
en la carpeta libraries. - Copiar la carpeta assets en la raíz.
Tema personalizado para la jQuery datatable
Verás que la tabla o el diseño por defecto aunque no es "horroroso" si está anticuado y rompe un poco con el esquema de sitios actuales los cuales todos tienen algo en común, que es ser minimalista; para esto la misma gente de grocery CRUD provee un tema (eso sí de pago) el cual puede consultar en el siguiente link junto con toda la información necesaria como forma de pago (PayPal), instalación (reemplazar algunos archivos y agregar otros) y poco más; como verás, es Bootstrap, lo cual puedes personalizarlo según la paleta de colores que tengas creada para tu aplicación.
Compre el tema promocionado (aunque no acostumbro a comprar absolutamente ningún recurso -es el primer recurso que compro-) y no he tenido ningún inconveniente mayor instalando, configurando y usando el mismo; lo que ves es lo que obtienes.
Para emplear un tema en particular (por ejemplo el anterior) se usa el método:
$crud->set_theme('bootstrap');
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter