0

Relaciones de tipo foraneas en CodeIgniter 4

Una clave foránea o FK de sus siglas en inglés de Foreign Key, es un campo (o colección de campos) en una tabla, que se refiere a la CLAVE PRIMARIA en otra tabla.

La restricción FOREIGN KEY se usa para evitar acciones que destruirían enlaces entre tablas y poder enlazar tablas sin necesidad de duplicar registros, si no simplemente referenciando a los mismos.

Migración de la tabla a relacionar

En CodeIgniter 4, al igual que ocurre con Laravel, para poder estructurar este tipo de relaciones, tenemos que definirlas desde las migraciones; en este ejemplo, voy a tomar un par de tablas de mi curso completo, las de movies y categorías en la cual una movie o película pertenece a una categoría; primero la migración de la tabla a relacionar, es decir, la de movies:

<?php namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;
class Categories extends Migration
{
public function up(){
$this->forge->addField([
'id'          => [
'type'           => 'INT',
'constraint'     => 5,
'unsigned'       => TRUE,
'auto_increment' => TRUE
],
'title'       => [
'type'           => 'VARCHAR',
'constraint'     => '255',
],
]);
$this->forge->addKey('id', TRUE);
$this->forge->createTable('categories');
}
//--------------------------------------------------------------------
public function down()
{
$this->forge->dropTable('categories');
}
}

Lo típico, un campo id y nombre para las categorías y en la función down hacemos las operaciones inversas a la de uno, que si en la de up creamos una tabla, por el down la destruimos; recuerda que el tema de las migraciones en CodeIgniter ya fue tratado.

Migración de la FK o clave foranea

Finalmente la tabla que vamos a relacionar mediante la foreign key:

<?php namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Movies extends Migration
{
public function up()
{
$this->forge->addField([
'id'          => [
'type'           => 'INT',
'constraint'     => 5,
'unsigned'       => TRUE,
'auto_increment' => TRUE
],
            title=> [
                'type' => 'VARCHAR',
                'constraint' => 255
            ]
'category_id'          => [
'type'           => 'INT',
'constraint'     => 5,
'unsigned'       => TRUE
],
 ****
]);
$this->forge->addKey('id', TRUE);
            $this->forge->addForeignKey('category_id' , categories,'id','CASCADE','CASCADE');
$this->forge->createTable('movies');
}
//--------------------------------------------------------------------
public function down()
{
$this->forge->dropTable('movies');
}
}

Una migración con un campo ID para mantenerla sencilla y el resto de los campos  que de manera ejemplificada solamente tenemos el del título; lo importante de aqui es la columna llamada category_id que con la misma más adelante la empleamos para definir la clave foránea mediante la función de addForeignKey, la cual recibe:

  1. El nombre de la columna en movies, es decir, la tabla que mantendrá la relación FK
  2. El nombre de la tabla a relacionar, categories en nuestro caso
  3. La columna de la tabla a relacionar, es decir, la PK de categorías que se llama id

Modelos

Finalmente, los modelos de las migraciones anteriores:

<?php namespace App\Models;
use CodeIgniter\Model;
class MovieModel extends Model
{
    protected $table = 'movies';
    protected $primaryKey = 'id';
    protected $allowedFields = ['title','category_id'];
}
<?php namespace App\Models;
use CodeIgniter\Model;
class CategoryModel extends Model
{
    protected $table = 'categories';
    protected $primaryKey = 'id';
    protected $allowedFields = ['title'];
    public function get($id = null)
    {
        if ($id === null) {
            return $this->findAll();
        }
        return $this->asArray()
            ->where(['id' => $id])
            ->first();
    }
}

Extra: Crear un registro con relación foránea

Para crear un registro no tienes que definir alguna regla y estructura adicional; la relación FK a la final es una columna de tipo entero con una restricción extra que es el de la FK; así que, pasamos directamente el valor de la clave a relacionar; en este ejemplo, estamos suponiendo que estos datos son recibidos desde un formulario:

$movie = new MovieModel();
        if ($this->validate('movies')) {
            $id = $movie->insert([
                'title' => $this->request->getPost('title'),
                'description' => $this->request->getPost('description'),
                'category_id' => $this->request->getPost('category_id'),
            ]);
0

Crea cuadrículas de datos Ajax con CodeIgniter y jQuery

En esta lección, crearemos una biblioteca CodeIgniter que nos permite generar cuadrículas de datos automáticamente para administrar cualquier tabla de base de datos. Explicaré cada paso necesario para crear esta clase; por lo que probablemente aprenderás algunas técnicas/conceptos nuevos de programación orientada a objetos en el proceso.

Como beneficio adicional, procederemos a escribir un código jQuery que permitirá al usuario actualizar el contenido de la cuadrícula de datos sin tener que esperar a que se actualice la página.

¿Qué es una cuadrícula de datos?

Una cuadrícula de datos es una tabla que muestra el contenido de una base de datos o una tabla junto con los controles de clasificación.

Una cuadrícula de datos es una tabla que muestra el contenido de una base de datos o una tabla junto con los controles de clasificación. En este tutorial, se nos asignará la tarea de proporcionar esta funcionalidad, pero también evitar que el usuario espere a que la página se actualice cada vez que se realiza una operación. ¡Gracias a jQuery, esta será una tarea bastante simple!

¿Qué pasa con los usuarios que no tienen Javascript habilitado? ¡No te preocupes, también los compensaremos!

Paso 1: crear una clase de generador de cuadrícula de datos

Queremos construir una herramienta que nos permita crear cuadrículas de datos dinámicamente para cualquier tabla de base de datos que tengamos. Esto significa que el código no está vinculado a ninguna estructura de tabla específica y, por lo tanto, es independiente de los datos en sí. Todo lo que el codificador (el desarrollador que usa nuestra clase) debe saber es el nombre de la tabla que se va a transformar en una cuadrícula y la clave principal de esa tabla. Aquí está el previo de la clase que desarrollaremos durante la mayor parte de este tutorial:

<?php
class Datagrid{
    private $hide_pk_col = true;
    private $hide_cols = array();
    private $tbl_name = '';
    private $pk_col = '';
    private $headings = array();
    private $tbl_fields = array();
}
?>

La clase Datagrid bien podría agregarse a la carpeta de la aplicación/biblioteca, pero la vamos a agregar como ayuda al marco CodeIgniter. ¿Por qué? Debido a que cargar bibliotecas no nos permite pasar argumentos al constructor de la clase, cargarlo como ayuda resolverá el problema. Este punto tendrá más sentido para ti cuando hayamos terminado de escribir el constructor.

El método constructor de clase

public function __construct($tbl_name, $pk_col = 'id'){
    $this->CI =& get_instance();
    $this->CI->load->database();
    $this->tbl_fields = $this->CI->db->list_fields($tbl_name);
    if(!in_array($pk_col,$this->tbl_fields)){
        throw new Exception("Primary key column '$pk_col' not found in table '$tbl_name'");
    }
    $this->tbl_name = $tbl_name;
    $this->pk_col = $pk_col;
    $this->CI->load->library('table');
   

Ya tenemos mucho que hacer; pero no te preocupes, ya que te lo explicaré todo en el siguiente párrafo.

El constructor toma dos argumentos: el primero es el nombre de la tabla en tu base de datos que desea mostrar como una cuadrícula de datos al usuario; el segundo parámetro es el nombre de la columna que sirve como clave principal para esa tabla (más sobre esto más adelante). Dentro del cuerpo del constructor, instanciamos el objeto CodeIgniter, el objeto de base de datos y la clase/biblioteca de tabla HTML. Todos estos serán necesarios durante la vida útil de un objeto de cuadrícula de datos y ya están integrados en el marco de CI. Ten en cuenta que también verificamos si la clave principal realmente existe en la tabla dada y, en caso de que no, lanzamos una excepción que informa el error. Ahora la variable miembro $this->tbl_fields estará disponible para su uso posterior, por lo que no tenemos que recuperar la base de datos nuevamente.

«Podemos usar el comando $CI->db->list_fields($tbl_name) para obtener los nombres de todos los campos que tiene una tabla. Sin embargo, para un mejor rendimiento, recomiendo almacenar los resultados en caché «.

Método para personalizar los títulos de las tablas

public function setHeadings(array $headings){
    $this->headings = array_merge($this->headings, $headings);
}

Esto te permite personalizar los encabezados de la tabla de la cuadrícula de datos, es decir, puedes sobrescribir los nombres de las columnas originales para ciertos campos de la tabla. Se necesita un array asociativo, como este: regdate =>»Registration Date». En lugar de solo el «Regdate» técnico como el encabezado de la columna para ese tipo de datos, tenemos un título más legible por humanos en su lugar. En breve se dará a conocer el código responsable de la aplicación de los títulos.

Método para ignorar/ocultar campos de tabla

public function ignoreFields(array $fields){
    foreach($fields as $f){
        if($f!=$this->pk_col)
            $this->hide_cols[] = $f;
    }
}

ignoreFields recibe un array que contiene los campos que se ignorarán al obtener datos de la base de datos. Esto es útil cuando tenemos tablas con muchos campos, pero solo queremos ocultar un par de ellos. Este método es lo suficientemente inteligente como para rastrear un intento de ignorar el campo de clave principal y luego omitirlo. Esto es así porque la clave principal no se puede ignorar por razones técnicas (verás por qué en breve). Aún así, si deseas ocultar la columna de clave principal para que no aparezca en el Reino Unido, puedes usar el método hidePkCol:

public function hidePkCol($bool){
    $this->hide_pk_col = (bool)$bool;
}

Este método recibe un valor booleano para indicar si queremos ocultar la columna de clave primaria para que no aparezca en la cuadrícula de datos. A veces, es una idea desagradable mostrar los datos pkey, que generalmente es un código numérico sin ningún significado para el usuario.

Método de la siguiente instancia:

private function _selectFields(){
    foreach($this->tbl_fields as $field){
        if(!in_array($field,$this->hide_cols)){
            $this->CI->db->select($field);
            // hide pk column heading?
            if($field==$this->pk_col && $this->hide_pk_col) continue;
                $headings[]= isset($this->headings[$field]) ? $this->headings[$field] : ucfirst($field);
        }
    }
    if(!empty($headings)){
        // prepend a checkbox for toggling 
        array_unshift($headings,"<input type='checkbox' class='check_toggler'>");
        $this->CI->table->set_heading($headings);
    }
     
}

Aquí tenemos un método auxiliar; por eso tiene el modificador «private» y tiene como prefijo un carácter subrayado (convención de código). Será utilizado por el método generate() – explicado brevemente – para tener los campos de tabla apropiados seleccionados y también los encabezados apropiados establecidos para la tabla (generador) object. Observa la siguiente línea:

$headings[]= isset($this->headings[$field]) ? $this->headings[$field] : ucfirst($field);

Aquí es donde aplicamos los encabezados personalizados o recurrimos a los predeterminados si no se da ninguno. Si se supone que la columna pk está oculta de la pantalla, se omitirá su encabezado. También observa la siguiente línea:

array_unshift($headings,"<input type='checkbox' class='dg_check_toggler'>");

El comando anterior indica al programa que anteponga una casilla de verificación «Master» como el primer encabezado de la tabla. Esa casilla de verificación es diferente de otras casillas de verificación en la cuadrícula en que le permite al usuario marcar o desmarcar todas las casillas de verificación de una sola vez. Esta funcionalidad de alternancia se implementará en unos momentos con un simple fragmento de código jQuery.

Método para generar/renderizar la cuadrícula de datos

Ahora viene lo que realmente hace el trabajo por nosotros:

public function generate(){
    $this->_selectFields();
    $rows = $this->CI->db
            ->from($this->tbl_name)
            ->get()
            ->result_array();
    foreach($rows as &$row){
        $id = $row[$this->pk_col];
         
        // prepend a checkbox to enable selection of items/rows
        array_unshift($row, "<input class='dg_check_item' type='checkbox' name='dg_item[]' value='$id' />");
         
        // hide pk column cell?
        if($this->hide_pk_col){
            unset($row[$this->pk_col]);
        }
    }
     
    return $this->CI->table->generate($rows);
}

El método generate, como su nombre indica, es responsable de generar la propia cuadrícula de datos. Debes llamar a este método solo después de haber configurado el objeto de acuerdo a tus necesidades. Lo primero que hace es llamar al método $this->_selectFields() para realizar las acciones que explicamos anteriormente. Ahora tiene que buscar todas las filas de la base de datos y luego recorrerlas, agregando casillas de verificación al comienzo de cada fila:

// prepend a checkbox to enable selection of items/rows
array_unshift($row, "<input class='dg_check_item' type='checkbox' name='dg_item[]' value='$id' />");

Dentro del bucle foreach en el método generate, si el indicador $this->hide_pk_col se establece en true, entonces debemos desarmar la entrada de clave primaria en la matriz $row array para que no aparezca como una columna cuando $this->CI->table object procesa todas las filas y genera la salida html final. En este punto, está bien eliminar la clave principal, si es necesario, porque ya no necesitamos esa información.

Pero, ¿qué hace el usuario con las filas seleccionadas/marcadas? Para responder a esto, he preparado algunos métodos más. El primero nos permite crear «action buttons» sin tener que conocer ningún detalle técnico sobre cómo funciona internamente el sistema de cuadrícula:

Método para agregar botones a un formulario de cuadrícula de datos

public static function createButton($action_name, $label){
    return "<input type='submit' class='$action_name' name='dg_action[$action_name]' value='$label' />";
}

Simplemente pasa el nombre de la acción como primer argumento y un segundo argumento para indicar la etiqueta del botón generado. Se genera automáticamente un atributo class para ese botón para que podamos jugar con él más fácilmente cuando estamos trabajando con él en nuestro JavaScript. Pero, ¿cómo sabemos si el usuario ha pulsado un determinado botón de acción? La respuesta se puede encontrar en el siguiente método:

public static function getPostAction(){
// get name of submitted action (if any)
    if(isset($_POST['dg_action'])){
        return key($_POST['dg_action']);
    }
}

¡Sí! Otro método estático que nos ayuda cuando se trata de formularios. Si se ha enviado alguna cuadrícula de datos, este método devolverá el nombre de la acción (u «operation») asociada con ese evento de envío. Además, otra herramienta útil para procesar nuestros formularios de cuadrícula de datos es …

public static function getPostItems(){
    if(!empty($_POST['dg_item'])){
        return $_POST['dg_item'];
    }
    return array();
}

… que devuelve un array que contiene los ids seleccionados para que puedas rastrear qué filas se han seleccionado en la cuadrícula y luego realizar alguna acción con ellas. Como ejemplo de lo que se puede hacer con una selección de ids de fila, he preparado otro método, este es un método de instancia, y no uno estático, porque hace uso de los recursos de instancia del objeto para hacer su negocio:

public function deletePostSelection(){
// remove selected items from the db
    if(!empty($_POST['dg_item']))
        return $this->CI->db
            ->from($this->tbl_name)
            ->where_in($this->pk_col,$_POST['dg_item'])
            ->delete();
}

Si se marcó al menos una casilla de verificación, el método deletePostSelection() generará y ejecutará una declaración SQL como la siguiente (supón $tbl_name='my_table' y $pk_col='id'):

DELETE FROM my_table WHERE id IN (1,5,7,3,etc...)

… que eliminará efectivamente las filas seleccionadas de la capa persistente. Podría haber más operaciones que podrías agregar a una cuadrícula de datos, pero eso dependerá de las características específicas de tu proyecto. Como consejo, podrías extender esta clase a, digamos, InboxDatagrid, por lo que, más allá del método deletePostSelection, podría incluir operaciones adicionales, como moveSelectedMessagesTo($place), etc…

Poniendo todo junto

Ahora, si has seguido este tutorial paso a paso, deberías haber terminado con algo similar a lo siguiente:

class Datagrid{
     
    private $hide_pk_col = true;
    private $hide_cols = array();
    private $tbl_name = '';
    private $pk_col = '';
    private $headings = array();
    private $tbl_fields = array();
     
    function __construct($tbl_name, $pk_col = 'id'){
        $this->CI =& get_instance();
        $this->CI->load->database();
        $this->tbl_fields = $this->CI->db->list_fields($tbl_name);
        if(!in_array($pk_col,$this->tbl_fields)){
            throw new Exception("Primary key column '$pk_col' not found in table '$tbl_name'");
        }
        $this->tbl_name = $tbl_name;
        $this->pk_col = $pk_col;
        $this->CI->load->library('table');
         
    }
     
    public function setHeadings(array $headings){
        $this->headings = array_merge($this->headings, $headings);
    }
     
    public function hidePkCol($bool){
        $this->hide_pk_col = (bool)$bool;
    }
     
    public function ignoreFields(array $fields){
        foreach($fields as $f){
            if($f!=$this->pk_col)
                $this->hide_cols[] = $f;
        }
    }
     
    private function _selectFields(){
        foreach($this->tbl_fields as $field){
            if(!in_array($field,$this->hide_cols)){
                $this->CI->db->select($field);
                // hide pk column heading?
                if($field==$this->pk_col && $this->hide_pk_col) continue;
                $headings[]= isset($this->headings[$field]) ? $this->headings[$field] : ucfirst($field);
            }
        }
        if(!empty($headings)){
            // prepend a checkbox for toggling 
            array_unshift($headings,"<input type='checkbox' class='dg_check_toggler'>");
            $this->CI->table->set_heading($headings);
        }
         
    }
     
    public function generate(){
        $this->_selectFields();
        $rows = $this->CI->db
                ->from($this->tbl_name)
                ->get()
                ->result_array();
        foreach($rows as &$row){
            $id = $row[$this->pk_col];
             
            // prepend a checkbox to enable selection of items
            array_unshift($row, "<input class='dg_check_item' type='checkbox' name='dg_item[]' value='$id' />");
             
            // hide pk column?
            if($this->hide_pk_col){
                unset($row[$this->pk_col]);
            }
        }
         
        return $this->CI->table->generate($rows);
    }
     
    public static function createButton($action_name, $label){
        return "<input type='submit' class='$action_name' name='dg_action[$action_name]' value='$label' />";
    }
     
    public static function getPostAction(){
    // get name of submitted action (if any)
        if(isset($_POST['dg_action'])){
            return key($_POST['dg_action']);
        }
    }
     
    public static function getPostItems(){
        if(!empty($_POST['dg_item'])){
            return $_POST['dg_item'];
        }
        return array();
    }
     
    public function deletePostSelection(){
    // remove selected items from the db
        if(!empty($_POST['dg_item']))
            return $this->CI->db
                ->from($this->tbl_name)
                ->where_in($this->pk_col,$_POST['dg_item'])
                ->delete();
    }
 
}

Aviso: no olvides guardar este archivo como datagrid_helper.php y colocarlo en «application/helper/»

Paso 2: Probar la clase auxiliar de cuadrícula de datos con un controlador CodeIgniter

Ahora crearemos un controlador de prueba simple y cargaremos la clase Datagrid como ayuda en su constructor. Pero antes de eso, debemos definir una tabla de base de datos ficticia y completarla con algunos datos de muestra.

Ejecuta el siguiente SQL para crear la base de datos y la tabla de usuario:

CREATE DATABASE `dg_test`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(80) NOT NULL,
  `password` varchar(32) NOT NULL,
  `email` varchar(255) NOT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;

A continuación, agreguemos algunos usuarios:

INSERT INTO `users` (`id`, `username`, `password`, `email`) VALUES
(1, 'david', '12345', 'david@domain.com'),
(2, 'maria', '464y3y', 'maria@domain.com'),
(3, 'alejandro', 'a42352fawet', 'alejandro@domain.com'),
(4, 'emma', 'f22a3455b2', 'emma@domain.com');

Ahora, guarda el siguiente código como «test.php«, y agréguelo a la carpeta «aplicación/controladores»:

<?php
class Test extends CI_Controller{
 
    function __construct(){
        parent::__construct();
        $this->load->helper(array('datagrid','url'));
        $this->Datagrid = new Datagrid('users','id');
    }
     
    function index(){
        $this->load->helper('form');
        $this->load->library('session');
 
        $this->Datagrid->hidePkCol(true);
        $this->Datagrid->setHeadings(array('email'=>'E-mail'));
        $this->Datagrid->ignoreFields(array('password'));
         
        if($error = $this->session->flashdata('form_error')){
            echo "<font color=red>$error</font>";
        }
        echo form_open('test/proc');
        echo $this->Datagrid->generate();
        echo Datagrid::createButton('delete','Delete');
        echo form_close();
    }
     
    function proc($request_type = ''){
        $this->load->helper('url');
        if($action = Datagrid::getPostAction()){
            $error = "";
            switch($action){
                case 'delete' :
                    if(!$this->Datagrid->deletePostSelection()){
                        $error = 'Items could not be deleted';
                    }
                break;
            }
            if($request_type!='ajax'){
                $this->load->library('session');
                $this->session->set_flashdata('form_error',$error);
                redirect('test/index');
            } else {
                echo json_encode(array('error' => $error));
            }
        } else {
            die("Bad Request");
        }
    }
 
}
?>

Se crea una instancia de esta clase y se pasa como referencia al miembro $this->Datagrid .Ten en cuenta que obtendremos datos de una tabla llamada «usuarios» cuya clave principal es la columna «id»; luego, en el método de índice, tomamos los siguientes pasos: configurar el objeto Datagrid, renderizarlo dentro de un formulario con un botón de eliminar agregado y ver si todo funciona como se esperaba:

Pregunta: ¿Qué sucede cuando se envía el formulario?

Respuesta: El método «Test::proc()» se encarga de procesar el formulario y elegir la operación correcta para aplicar contra los id que fueron seleccionados por el remitente del formulario. También se encarga de las solicitudes AJAX, por lo que enviará un objeto JSON al cliente. Esta función compatible con AJAX será útil cuando jQuery entre en acción, ¡que es ahora mismo!

«Siempre es una buena idea crear aplicaciones web que compensen cuando JavaScript/AJAX no está disponible. De esta manera, algunos usuarios tendrán una experiencia más rica y más rápida, mientras que aquellos que no tengan JavaScript habilitado aún podrán usar la aplicación con normalidad «.

Paso 3: Implementando de Ajax (¡jQuery al rescate!)

Cuando el usuario hace clic en el botón (o cualquier otro botón de acción), nos gustaría, quizás, evitar que la página se recargue y tenga que generar todo de nuevo; ¡Esto podría hacer que el usuario de nuestra aplicación se duerma! Eludir este problema no será una tarea difícil si nos ceñimos a la biblioteca jQuery. Dado que este no es un tutorial para «principiantes», no voy a repasar todos los detalles relacionados con cómo obtener la biblioteca, cómo incluirla en la página, etc. Se espera que conozcas estos pasos por tu cuenta.

Crea una carpeta, llamada «js«, agrega la biblioteca jQuery dentro y crea un archivo de vista, llamado users.php. Abrd este nuevo archivo y agrega:

<html>
<head>
    <title>Users Management</title>
    <script src="<?php echo base_url(); ?>js/jquery-1.6.3.min.js"></script>
    <script src="<?php echo base_url(); ?>js/datagrid.js"></script>
</head>
<body>
<?php
        $this->Datagrid->hidePkCol(true);
        if($error = $this->session->flashdata('form_error')){
            echo "<font color=red>$error</font>";
        }
        echo form_open('test/proc',array('class'=>'dg_form'));
        echo $this->Datagrid->generate();
        echo Datagrid::createButton('delete','Delete');
        echo form_close();
?>
</body>
</html>

¿Te diste cuenta de que hemos movido el código de Test::index al nuevo script de vista? Esto significa que debemos cambiar el método Test::index() en consecuencia:

function index(){
    $this->load->helper('form');
    $this->load->library('session');
    $this->load->view('users');
}

Eso está mejor. Si deseas agregar algo de estilo a la cuadrícula, puedes usar el siguiente CSS (o hacer un mejor diseño por tu cuenta):

.dg_form table{
    border:1px solid silver;
}
 
.dg_form th{
    background-color:gray;
    font-family:"Courier New", Courier, mono;
    font-size:12px;
}
 
.dg_form td{
    background-color:gainsboro;
    font-size:12px;
}
 
.dg_form input[type=submit]{
    margin-top:2px;
}

Ahora, por favor, crea un archivo «datagrid.js», colócalo en el directorio «js» y comienza con este código:

$(function(){
	// cool stuff here...
})

Dentro de este cierre, escribiremos código que se encargará de controlar ciertos eventos de envío una vez que la página se haya cargado por completo. Lo primero que debemos hacer es rastrear cuando un usuario hace clic en un botón de envío en el formulario de cuadrícula de datos y luego enviar esos datos para que se procesen en el servidor.

$('.dg_form :submit').click(function(e){
		e.preventDefault();
		var $form = $(this).parents('form');
		var action_name = $(this).attr('class').replace("dg_action_","");
		var action_control = $('<input type="hidden" name="dg_action['+action_name+']" value=1 />');
		
		$form.append(action_control);
		
		var post_data = $form.serialize();
		action_control.remove();
		
		var script = $form.attr('action')+'/ajax';
		$.post(script, post_data, function(resp){
			if(resp.error){
				alert(resp.error);
			} else {
				switch(action_name){
					case 'delete' :
						// remove deleted rows from the grid
						$form.find('.dg_check_item:checked').parents('tr').remove();
					break;
					case 'anotherAction' :
						// do something else...
					break;
				}
			}
		}, 'json')
	})

Alternativamente, podríamos haber comenzado con algo como: $('.Dg_form').submit(function(e){...}). Sin embargo, dado que quiero rastrear qué botón se ha presionado y extraer el nombre de la acción elegida en función de él, prefiero vincular un controlador de eventos al botón de enviar y luego subir en la jerarquía de nodos para encontrar el formulario que el botón presionado pertenece a:

// finds the form
var $form = $(this).parents('form');
// extracts the name of the action
var action_name = $(this).attr('class').replace("dg_action_","");

A continuación, agregamos un elemento de entrada oculto dentro del elemento del formulario para indicar qué acción se está enviando:

// create the hidden input
var action_control = $('<input type="hidden" name="dg_action['+action_name+']" value=1 />');
// add to the form
$form.append(action_control);

Esto es necesario porque la función no considera que el botón de envío sea una entrada de formulario válida. Entonces debemos tener ese truco en su lugar al serializar los datos del formulario.

action_control.remove();

«¡No lo olvides: la función ignora el botón de enviar y lo descarta como una simple pieza de chatarra de marcado!»

Envíando datos del formulario al servidor

A continuación, procedemos a obtener el atributo action del elemento de formulario y anexamos la cadena «/ajax» a esa url, para que el método sepa que, de hecho, se trata de una solicitud AJAX. Después de eso, usamos la función jQuery.post para enviar los datos para ser procesados por el controlador apropiado, del lado del servidor, y luego interceptar el evento de respuesta con un callback/closure registrado:

var script = $form.attr('action')+'/ajax';
	$.post(script, post_data, function(resp){
		if(resp.error){
			alert(resp.error);
		} else {
			switch(action_name){
				case 'delete' :
					// remove deleted rows from the grid
					$form.find('.dg_check_item:checked').parents('tr').remove();
				break;
				case 'anotherAction' :
					// do something else...
				break;
			}
		}
	},'json')

Observa que estamos pidiendo que la respuesta se codifique como «json» ya que estamos pasando esa cadena como el cuarto argumento de la función $.post. El contenido de la devolución de llamada que trata de la respuesta del servidor debería ser bastante sencillo de comprender; determina si hay un error y, de ser así, lo alerta. De lo contrario, indicará que la acción fue procesada con éxito (en este caso, si se trata de una «» acción, eliminamos las filas relacionadas con los ids que fueron seleccionados por el usuario).

Paso 4: ¡Marca todo o nada!

Lo único que falta ahora es la funcionalidad de alternancia que prometí antes. Debemos registrar una función de devolución de llamada para cuando se haga clic en la casilla de verificación «Master», que tiene un atributo de clase establecido en «dg_check_toggler«. Agrega el siguiente fragmento de código después del anterior:

$('.dg_check_toggler').click(function(){
		var checkboxes = $(this).parents('table').find('.dg_check_item');
		if($(this).is(':checked')){
			checkboxes.attr('checked','true');
		} else {
			checkboxes.removeAttr('checked');
		}
	})

Cuando se hace clic en la casilla de verificación «toggler», si pasa a un estado «checked», todas las filas de la cuadrícula de datos correspondiente se marcarán simultáneamente; de lo contrario, todo estará desmarcado.

Pensamientos finales

No hemos llegado a la punta del iceberg cuando se trata de cuadrículas de datos para sistemas de gestión de contenido más complejos. Otras características que pueden resultar útiles son:

  • Ordenar la cuadrícula de datos por nombres de columna
  • Enlaces de paginación para navegar por la cuadrícula de datos
  • Editar/modificar enlaces para actualizar los datos de una sola fila
  • Mecanismo de búsqueda para filtrar resultados

Gracias por leer. Si desea un tutorial de seguimiento, házmelo saber en los comentarios.

0

Definir filtros o campos de búsqueda en CodeIgniter 4

En CodeIgniter 4, al igual que en varios frameworks, es muy sencillo crear funciones de tipo filtro; en este caso es mediante un campo de tipo texto; así que para esto, vamos a crear un campo como el siguiente:

<input type="text" name="search" placeholder="search" value="<?= $search ?>">

Que forma parte de nuestro formulario completo:

<form method="get" id="formFilter">
    <select name="type">
        <option value="">Tipos</option>
        <option <?= ($typeId == "exit") ? "selected" : "" ?> value="exit">Salida</option>
        <option <?= ($typeId == "entry") ? "selected" : "" ?> value="entry">Entrada</option>
    </select>
    <select name="user_id">
        <option value="">Usuarios</option>
        <?php foreach ($users as $u) : ?>
            <option <?= ($u->id == $userId) ? "selected" : "" ?> value="<?= $u->id ?>"><?= $u->username ?></option>
        <?php endforeach ?>
    </select>
    <br>
    <h4>Búsqueda</h4>
    <input value="<?= $search ?>" type="text" name="search" placeholder="Buscar">
    <h3>Cantidades</h3>
    <label for="check_cant">
        Activar
        <input type="checkbox" name="check_cant" id="check_cant" checked>
    </label>
    <br>
    <label for="min_cant">
        Minimo <span><?= $minCant ? $minCant : 0 ?></span>:
        <input type="range" name="min_cant" value="<?= $minCant ? $minCant : 0 ?>" min="0" max="90" step="1">
    </label>
    <br>
    <label for="max_cant">
        Maximo <span><?= $maxCant ? $maxCant : 100 ?></span>:
        <input type="range" name="max_cant" value="<?= $maxCant ? $maxCant : 100 ?>" min="10" max="100" step="1">
    </label>
    <br>
    <button type="submit">Enviar</button>
    <a href="<?= route_to('product.trace',$product->id) ?>">Limpiar</a>
</form>

En el cual puedes colocar otros campos de cualquier tipo para tus filtros.

 fijate que el action del formulario no está definido, lo que significa que al enviar la petición la misma va a ser procesada por la misma página que pinta el formulario de búsqueda; en el caso del curso es la de un listado de detalles de producto.

Cuyo filtro, como puedes ver en el formulario anterior, es procesado por el mismo controlador que procesa la tabla anterior; en nuestro controlador, para saber si vamos a procesar algún dato de búsqueda, preguntamos desde el filtro:

$searchs = explode(" ", trim($this->request->getGet('search')));
        if ($this->request->getGet('search')) {
            //->orGroupStart()
            $query->groupStart();
            foreach ($searchs as $s) {
                $query->orLike('u.username', $s)
                    ->orLike('u.email', $s)
                    ->orLike('puc.description', $s);
            }
            $query->groupEnd();

Aquí varios aspectos que tienes que tener en cuenta:

  • Usamos la función de explode para buscar por tokens
  • Iteramos los tokens y buscamos coincidencias parciales con el like sobre las columnas a las cuales queremos buscar; en nuestro caso, un username, email y descripción que fijate que pertenecen a diferentes tablas

Cómo estamos empleando operaciones OR, es posible que interfieren con otros filtros where o similares en el resto de la consulta; por lo tanto, los agrupamos (en SQL colocamos parentensiste) para que solo afecten la sección de la búsqueda con los groupStart groupEnd

En esencia todos los pasos anteriores son opcionales y según tus necesidades puede que quieras buscar coincidencias exactas; si ese es el caso, te basta con definirlo como:

$query->where('u.username', $this->request->getGet('search'))

Para buscar una coincidencia exacta sobre la columna de username.

Aquí lo importante es notar que vamos construyendo el query a tokens o trozos, en los cuales preguntamos mediante un condicional si tenemos datos recibidos por el usuario y con esto armamos el query; como en nuestro ejemplo es un campo de búsqueda que queremos construir

0

Login y Control de Acceso en CodeIgniter con Community Auth

El Login también llamada autenticación es una función casi imprescindible en cualquier aplicación web, es una de los elementos fundamentales que tienen que estar cuando queremos llevar una simple página web a una aplicación web; y esto es decido que las aplicaciones web tienen siempre partes administrativas de la aplicación que se encuentran protegidas, con un… usuario y contraseña para autenticarse; en otras palabras un login que hoy veremos en CodeIgniter.

Otro elemento un poco menos necesario pero muy importante son el control de acceso o los permisos que le damos al usuario, generalmente se crear una implementación pobre en este aspecto, puros condicionales y cosas mal hechas, pero este paquete ofrece un completo control sobre nuestros usuarios, entre ellos están las famosas Listas de Control de Acceso o ACL.

En esta entrada se explica que es, como dar los primeros pasos con Community Auth y cuales son sus principales funcionalidades que en resumidas cuentas son: login, recuperación de cuenta o contraseña, Control de Acceso, Permisos a los usuarios, permisos bloqueo de usuarios y mucho más.

¿Qué es el Community Auth y en qué nos ofrece para el login y control de usuarios?

Community Auth es un paquete creado por un tercero, lo que quiere decir que no pertenece o no viene incluido al descargar el framework desde la web de CodeIgniter:Downloads si no lo tenemos que descargar desde otra web y posteriormente instalarlo; dicha instalación consta de varios pasos pero el resultado es tener una completa herramienta para el control de nuestros usuarios incluyendo la autenticación.

Community Auth funciona para CodeIgniter 3; y dentro de lo que componen su core permite la autenticación (login), manejo de roles, lista de control de acceso (ACL) para el manejo de los permisos, manejo de recuperación de contraseña, límite de logins fallidos entre otras características que podrás visualizar en el site.

Empezando con Community Auth: Qué podemos hacer

Su uso es muy sencillo, y el código que debemos incluir para emplearlo en nuestros controladores es realmente poco; tan solo el necesario para definir las funciones de acceso y sesión.

Pero este excelente paquete para CodeIgniter que podemos obtener de manera gratuita nos permite hacer mucho más, pero mucho más:

  • Podemos tener un completo sistema de permisos a nuestros usuarios, es decir, podemos limitar el acceso a cierto módulos, controladores y/o acciones en nuestra aplicación.
  • Podemos bloquear o bannear usuarios.
  • Podemos hacer el login, claro está.
  • Nos genera un token para recuperación de credenciales que luego podemos enviarsela al usuario mediante un envío de correos con CodeIgniter para que el usuario recupere sus credenciales
  • Protege nuestra contraseña con un hash para mayor seguridad.
  • Nos devuelve un objeto con los datos del usuario autenticado.
  • Factor para bloquear a usuarios por un tiempo determinado tras múltiples intentos fallidos
  • Login mediante Ajax o el tradicional.
  • Y mucho más; todo para tener un completo sistema de autenticación en CodeIgniter que lo puedes personalizar con CSS por ejemplo bootstrap.

Instalando Community Auth en nuestro proyecto en Community Auth

Su instalación no tiene mayor complejidad, simplemente nos descargamos la última versión de Community Auth disponible en su web: Download Community Auth y seguimos los pasos para su instalación los cuales son unos 16 que puedes encontrar en el siguiente enlace: Instrucciones de instalación de Community Auth.

En resumidas cuentas su instalación consiste en…

Descargar, descomprimir y copiar la carpeta community_auth dentro de la carpeta third_party, debemos copiar los php ubicados en la carpeta core dentro del la carpeta core de nuestro proyecto, crear la ruta al login de nuestra aplicación en el archivo de routes, habilitar el uso de los hooks y por supuesto, ejecutar el SQL en la base de datos de nuestro proyecto para copiar el esquema del paquete.

Funcionalidades principales de Community Auth

Community Auth cuenta con una buena cantidad de funcionalidades a las cuales les podemos sacar provecho para el tipo de funcionalidades que ofrece según sea necesario; estas funcionalidades las podemos configurar mediante el archivo de configuración de este paquete ubicado en:

Localización: /application/third_party/community_auth/config/authentication.php

Configuraciones principales

Community Auth cuenta con una gran cantidad de funcionalidades que resultan útiles en la mayoría de los casos; para evitar que hackeen la página mediante fuerza bruta (que intenten probar múltiples combinaciones de contraseñas de manera sucesiva) se puede emplear la configuración de max_allowed_attempts que por defecto se encuentra de la siguiente manera:

$config['max_allowed_attempts'] = 5;

Lo que significa que si un usuario realiza 5 intentos de inicio de sesión de manera incorrecta, la aplicación no le permitirá probar otra combinación de usuario contraseña por un tiempo determinado que puedes configurar mediante la siguiente configuración expresada en segundos:

$config['seconds_on_hold'] = 600;

Los bloqueos se realizan a nivel de IP asi que si estamos en Localhost posiblemente la URL bloqueada sea la de localhost: 127.0.0.1, pero si estamos en la web, sería la que nos da nuestro proveedor de interés; en ambos casos puedes consultar la tabla ips_on_hold para ver el resultado en caso de que se te bloquee el acceso en alguna ocasión:

Al eliminar manualmente el registro el bloqueo acaba.

También puedes denegar el acceso a un usuario (un código 403 de HTTP) cuando este no tiene éxito al momento de ingresar:

$config['deny_access_at'] = 10;

Que por defecto se encuentra en 10 cuyo valor representa el número de intentos fallidos por parte del usuario para denegar el acceso.

Puede evitar múltiples logins o autenticaciones concurrentes mediante la configuración:

$config['disallow_multiple_logins'] = FALSE;

Manejo de niveles y roles

Como se indicó en un inicio, Community Auth permite el manejo de permisos y roles que fácilmente son asignados a un usuario creado en nuestra base de datos en el campo auth_level de la tabla users:

De tal forma que cada uno de nuestros usuarios en base de datos debe pertenecer a un nivel previamente configurado mediante levels_and_roles de nuestro archivo de configuración:

$config['levels_and_roles'] = array(
    '1' => 'customer',
    '6' => 'manager',
    '9' => 'admin'
);

Y claro que podemos personalizar esta configuración que no es más que un array en PHP de la manera que queramos, en donde mientras más alto sea el valor del nivel, más alto será el nivel de acceso.

Un usuario con nivel de acceso 9 puede acceder al contenido de un usuario con nivel de acceso 6 pero este usuario con nivel de acceso 6 no puede acceder a uno con nivel de acceso 7 o superior.

Desde nuestro controlador, y dentro de nuestro mismo __construct() podemos ingresar la siguiente línea de código para empezar a trabajar con los niveles y accesos a nuestro sistema.

$this->require_min_level(MY_LEVEL);

En dónde «MY_LEVEL» es el nivel de bloqueo, que varía según las los niveles de acceso creados anteriormente.

Podemos colocar la línea anterior en el __construct() para que cada una de nuestras acciones herede dicho nivel de acceso (el método __construct() se invoca antes que nuestras acciones) o podemos colocarlo en cada una de las acciones que nosotros queramos.

Puedes obtener más información en la documentación oficial:Community Auth authentication.php

Dando los primeros pasos con Community Auth

Ya conocidas las configuraciones y funcionalidades principales del paquete Community Auth, ahora veamos qué otras características podemos usar.

Para tener un fácil acceso, manejaremos todo lo que tenga que ver con nivel de acceso en un controlador llamado User desde aquí manejaremos el login, logout, y recuperación de la cuenta; el login luce de la siguiente manera:

public function login() {

	if ($this->uri->uri_string() == 'user/login')
		show_404();

	if ($this->session->userdata("auth_level") != NULL) {
		redirect("/user/logout");
	}

	if (strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
		$this->require_min_level(1);
	}

	$this->setup_login_form();

	$html = $this->load->view('user/page_header', '', TRUE);
	$html .= $this->load->view('user/login_form', '', TRUE);
	$html .= $this->load->view('user/page_footer', '', TRUE);

	echo $html;
}

El logout llamamos al método correspondiente y destruimos la sesión:

$this->authentication->logout();

$redirect_protocol = USE_SSL ? 'https' : NULL;
$this->session->sess_destroy();
redirect(site_url(LOGIN_PAGE . '?logout=1', $redirect_protocol));

Y por último la funcionalidad que nos permite recuperar la contraseña, la cual consiste en crear un token para el usuario que solicite dicha recuperación que debería ser enviada mediante un correo electrónico.

public function recover() {
        // Load resources
        $this->load->model('examples_model');
        $this->load->library('emails');

        /// If IP or posted email is on hold, display message
        if ($on_hold = $this->authentication->current_hold_status(TRUE)) {
            $view_data['disabled'] = 1;
        } else {
            // If the form post looks good
            if ($this->input->post('email')) {
                if ($user_data = $this->examples_model->get_recovery_data($this->input->post('email'))) {
                    // Check if user is banned
                    if ($user_data->banned == '1') {
                        // Log an error if banned
                        $this->authentication->log_error($user_data->email);

                        // Show special message for banned user
                        $view_data['banned'] = 1;
                    } else {
                        /**
                         * Use the authentication libraries salt generator for a random string
                         * that will be hashed and stored as the password recovery key.
                         * Method is called 4 times for a 88 character string, and then
                         * trimmed to 72 characters
                         */
                        $recovery_code = substr($this->authentication->random_salt()
                                . $this->authentication->random_salt()
                                . $this->authentication->random_salt()
                                . $this->authentication->random_salt(), 0, 72);

                        // Update user record with recovery code and time
                        $this->examples_model->update_user_raw_data(
                                $user_data->user_id, [
                            'passwd_recovery_code' => $this->authentication->hash_passwd($recovery_code),
                            'passwd_recovery_date' => date('Y-m-d H:i:s')
                                ]
                        );

                        // Set the link protocol
                        $link_protocol = USE_SSL ? 'https' : NULL;

                        // Set URI of link
                        $link_uri = 'user/recovery_verification/' . $user_data->user_id . '/' . $recovery_code;
                        $this->emails->recuperar_cuenta($link_uri, $user_data->email);

                        $view_data['special_link'] = anchor(
                                site_url($link_uri, $link_protocol), site_url($link_uri, $link_protocol), 'target ="_blank"'
                        );

                        $view_data['confirmation'] = 1;
                    }
                }

                // There was no match, log an error, and display a message
                else {
                    // Log the error
                    $this->authentication->log_error($this->input->post('email', TRUE));

                    $view_data['no_match'] = 1;
                }
            }
        }

        echo $this->load->view('user/page_header', '', TRUE);
        echo $this->load->view('user/recover_form', ( isset($view_data) ) ? $view_data : '', TRUE);
        echo $this->load->view('user/page_footer', '', TRUE);
    }

A modo de ejemplo se coloca una función para enviar dicho enlace o link para recuperar la cuenta mediante un helper $this->emails->recuperar_cuenta($link_uri, $user_data->email);.

Seguramente querrás cambiar el estilo de cada una de las páginas que maneja Community Auth para el login, recuperación de cuenta, etc; las puedes encontrar en la siguiente carpeta:

/application/third_party/community_auth/views/user/*

En general el código es de fácil compresión y ya viene listo al momento de descargar el paquete, tan solo debemos usarlo y terminar de configurar cualquier parámetro dentro de los mismos según sea necesario.

Para pintar los datos de sesión, una vez autenticado desde nuestro controlador que hereda de la clase MY_Controller que nos la ofrece este paquete de autenticación para hacer nuestro login y que se ubica en la carpeta core de nuestra aplicación, podemos hacer lo siguiente para cargar los datos en sesión y de esta manera emplearlos en cualquier parte de nuestra aplicación:

$this->session->set_userdata(array(
	'email' => $this->auth_data->email,
	'name' => $this->auth_data->username,
	'id' => $this->auth_data->user_id,
	'auth_level' => $this->auth_data->auth_level
));
0

Envío de correos o emails con CodeIgniter, guía rápida

En esta entrada veremos cómo enviar correos con el framework CodeIgniter, CodeIgniter ofrece una gran librería de manera nativa para trabajar con el envío de correo, la misma permite enviar emails simples sin realizar configuraciones sobre el servidor SMTP, realizando configuraciones para poder emplear el servidor SMTP al 100%, e incluso un modo debugging para la impresión de posibles errores en configuración, y envío de correos en general; además permite el envío de correos en formato HTML; hoy veremos en detalle cada uno de los escenarios anteriores.

Por supuesto, para que funcione el envío de correos debe de tener configurado un servidor de correos en su máquina o emplear el de algun hosting como iPage, Godaddy, Hostgator, etc

Envío de correo simple con CodeIgniter

El envío de correo más sencillo que podemos realizar con CodeIgniter está configurado de la siguiente manera:

public function email() {
	$CI = & get_instance();
	$CI->load->helper('url');
	$CI->load->library('session');
	$CI->config->item('base_url');

	$CI->load->library('email');

	$subject = 'Bienvenido a mi app';

	$msg = 'Mensaje de prueba';

	$CI->email
		->from('barackobama@gmail.com')
		->to($email)
		->subject($subject)
		->message($msg)
		->send();
}

El inconveniente con este método, como podemos apreciar es que podemos colocar lo que queramos para el parámetro de from y es muy posible que los motores de correo etiqueten estos mensajes como SPAM al ser recibidos ya que no están siendo enviados desde la cuanta reflejada en dicho campo from además de que no todos los hosting permiten el envío de correos de tal forma.

Envío de correo con configuración en CodeIgniter

Esta sería el formato más adecuado para enviar correos con CodeIgniter, ya que configuramos todo lo necesario para tal fin; para eso definimos una variable config con los siguientes parámetros:

public function email() {

    $config = Array(
        'protocol' => 'smtp',
        'smtp_host' => 'smtp.1and1.com',
        'smtp_port' => 25,
        'smtp_user' => 'barackobama@misitio.com',
        'smtp_pass' => '12345',
        'charset' => 'utf-8',
        'priority' => 1
    );

    $CI = & get_instance();
    $CI->load->helper('url');
    $CI->load->library('session');
    $CI->config->item('base_url');

    $CI->load->library('email');

    $CI->email->initialize($config);

    $subject = 'Bienvenido a mi app';

    $msg = 'Mensaje de prueba';

    $CI->email
            ->from('barackobama@gmail.com')
            ->to($email)
            ->subject($subject)
            ->message($msg)
            ->send();
}

En la variable config especificamos elementos como el protocolo, el nombre del servidor SMTP, el puerto del servidor SMTP, el nombre de la cuenta, la contraseña de la cuenta, la codificación, la prioridad, etc; puedes consultar la lista completa en el siguiente enlace: CodeIgniter Email Class

Es importante notar que si estamos trabajando con un servidor web, hay parámetros que nos deben dar ellos que seguramente estan en algun apartado del CPanel; por ejemplo el de smtp_host en donde indicamos el servidor SMTP que vamos a emplear, también otros datos como smtp_port que debes de indicar el puerto.

smtp_user y smtp_pass user debes de especificar la dirección de email y password que vas a utilizar, la prioridad priority es simplemente una bandera, que por ejemplo Google toma en cuenta al momento de posicionar el correo en alguno de sus paneles:

Por último las funciones message y subject en donde especificamos el asunto y cuerpo del contenido; si quieres que el contenido a enviar sea HTML debes de realizar los siguientes cambios:

$msg =  $CI->load->view('email/recuperar_cuenta', $data, TRUE);

***

$CI->email->from('barackobama@gmail.com')
		->to($email)
		->subject($subject)
		->message($msg)
		->set_mailtype('html')
		->send();

De esta forma, con el tercer parámetro establecido en TRUE le estamos diciendo a CodeIgniter que devuelva el contenido de la vista y la guardamos en una variable, luego el método set_mailtype lo establecemos como HTML.

Como puedes ver, estos parámetros indicados en la variable config depende en gran medida de la configuración que emplee su hosting y deben preguntar por los mismos a su hosting en caso de no estar en su panel.

Envío de correos con formato HTML en CodeIgniter

Hasta ahora se ha mostrado cómo enviar correos sin formato, es decir puro texto, veamos cómo enviar un correo con formato HTML; para ello:

public function email() {
    $CI = & get_instance();
    $CI->load->helper('url');
    $CI->load->library('session');
    $CI->config->item('base_url');

    $CI->load->library('email');

    $subject = 'Bienvenido a mi app';

    $msg = $CI->load->view('email/vista', $data, true);

    $CI->email
            ->from('barackobama@gmail.com')
            ->to($email)
            ->subject($subject)
            ->message($msg)
            ->set_mailtype('html')
            ->send();
}

$CI->load->view con el parámetro true permite obtener o retornar el código fuente de la vista, la cual establecemos en el parámetro message.

Debugging al momento del envío de los emails

En caso de ocurrir algún error en los parámetros de configuración es muy útil el llamado e impresión de esta función, la misma nos provee de más datos como error en las credenciales del correo, si el servidor existe o no, etc; muy útil al momento de configurar nuestra función de envío de correos:

public function email() {

$config = Array(
'protocol' => 'smtp',
'smtp_host' => 'smtp.1and1.com',
'smtp_port' => 25,
'smtp_user' => 'barackobama@gmail.com',
'smtp_pass' => '12345',
'charset' => 'utf-8',
'wordwrap' => true,
'priority' => 1
);

$CI = & get_instance();
$CI->load->helper('url');
$CI->load->library('session');
$CI->config->item('base_url');

$CI->load->library('email');

$subject = 'Bienvenido a mi app';

$msg = 'Mensaje de prueba';

$CI->email
->from('barackobama@gmail.com')
->to($email)
->subject($subject)
->message($msg)
->send();

echo $CI->email->print_debugger();

}

Atachar archivos en los correos

Por último mostramos cómo podemos atachar múltiples archivos o un archivo (remover el foreach en un email):

$path = realpath('proyectos/archivos/');

foreach ($archivos_proyecto as $archivo_proyecto) {
    var_dump($path . $archivo_proyecto->archivo);
    $CI->email->attach($path . '/' . $archivo_proyecto->archivo);
}

Necesitamos el verdadero path o path completo de los archivos que deseamos adjuntar, y por eso empleamos la función provista por la API de PHP realpath, si el archivo no existe, simplemente no se adjuntará al momento de enviar el email.

Por supuesto puedes combinar entre sí las distintas secciones que vimos anteriormente según sea lo que amerite.

Extra: enviar emails en CodeIgniter con Gmail

El siguiente código muestra como enviar correos con Gmail lo cual puede no ser siempre la mejor opción ya que los hosting tienden a bloquear el acceso a estos servicios pero de igual forma les dejo el código:

//configuracion para gmail

 $config = array(
 'protocol' => 'smtp',
 'smtp_host' => 'ssl://smtp.gmail.com',
 'smtp_port' => 465,
 'smtp_user' => 'mi_correo_gmail@gmail.com',
 'smtp_pass' => 'password',
 'mailtype' => 'html',
 'charset' => 'utf-8',
 'newline' => "\r\n"
 ); 

1 2