jueves, 2 de agosto de 2012

[Tutorial] Sencillo chat con Codeigniter (PHP + MySQL + JSON)

Os traigo un pequeño tutorial de como montar un sencillo chat con la ayuda del framework de PHP Codeigniter. Este chat se basará en la tecnología que nos brinda PHP junto a MySQL y recogiendo los datos en objectos JSON para trabajar con ellos de una manera rápida.

Tomaros está guía como una ayuda al resultado que realmente estáis buscando, y no como una guía del chat definitivo, pues tal y como esta contemplado es para un proyecto en el que va incorporado esto, con lo que retocaré la información que os daré para que se ajuste a hacer un chat simple y espero que no se me escape nada. Aun así, si teneis dudas, no temáis en preguntar! En esta guía tampoco pasaré a detallar configuraciones de Codeigniter, ya que la documentación que trae es suficiente para que al menos sepáis configurar la base de datos del proyecto.

Lo primero de todo será crear en nuestro esquema de la base de datos una tabla que pasaremos a llamar "chat_mensaje". Os dejo el script SQL para que solo tengáis que copiar, pegar y ejecutar.

CREATE TABLE  `chat_mensaje` (
  `id_chat_mensaje` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `m_chat_usuario` varchar(15) NOT NULL,
  `chat_mensaje` varchar(140) NOT NULL,
  `creado_en` datetime NOT NULL,
  PRIMARY KEY (`id_chat_mensaje`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 



Los campos identifican lo siguiente:

  • id_chat_mensaje: Un identificador autonumérico para determinar el mensaje (no nos influye para nada ya que es un campo autogestionado)
  • m_chat_usuario: Este campo identifica al usuario, tipo varchar. Será el nick de nuestro usuario por cada mensaje.
  • chat_mensaje: Este campo es el mensaje de chat. Máximo 140 caracteres.
  • creado_en: Fecha en formato DATETIME (YYYY-MM-DD hh:ii:ss)
Vamos a añadir un modelo a nuestro proyecto en Codeigniter que vamos a denominar ChatModel.php. Para ello creamos el fichero dentro de la carpeta application/models dentro de nuestro proyecto de Codeigniter. Agregamos al chat.php lo siguiente:
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class ChatModel extends CI_Model {
    
    
    public function getMensajesCanalOnTime($notnick){
        $this->db->where('TIMESTAMPDIFF(SECOND,creado_en,NOW()) <= 2',null,false);
        $this->db->where('m_chat_usuario !=',$notnick);
        $query = $this->db->get('chat_mensaje');
        return $query->result_array();
    }
    
    
    public function addMensaje($usuario,$mensaje){
        $data = array(
  'm_chat_usuario' => $usuario,
                'chat_mensaje' => $mensaje,
                        );
        $this->db->set('creado_en', 'NOW()', FALSE);
        return $this->db->insert('chat_mensaje', $data);
    }
    
}

La primera function getMensajesCanalOnTime devolverá todos aquellos mensajes que fueron escritos entre 2 segundos y el tiempo actual pasandole el nick del usuario que hace la solicitud para que no le devuelva sus propios mensajes. Dado que la velocidad de refresco del chat será de dos segundos, esto debería bastar para también dar un poco de respiro al servidor entre consulta y consulta.

La segunda función addMensaje será para ir añadiendo los mensajes que los distintos usuarios pongan en el chat, se les pasa el mensaje, y el nick de quien ha escrito el mensaje.

Ahora pasamos al controlador. Creamos un nuevo controlador en nuestro proyecto que vamos a denominar también Chat.php. Este fichero lo ubicaremos en application/controllers y añadimos lo siguiente:
if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Chat extends CI_Controller {
    
    
    public function __construct() {
        parent::__construct();
        $this->load->helper('url');
        $this->load->model('chatmodel');
    }
    

    public function index($nickusuario){
        $data['usuario'] = $nickusuario;        
        $this->load->view('chat_vista',$data);
    }

    
    public function mensajes_canal(){
        $usuario = $this->input->post('usuario');
        $mensajesCanal = $this->chatmodel->getMensajesCanalOnTime($usuario);
        echo json_encode($mensajesCanal);
    }
    
    public function add_mensaje_canal(){
        $mensaje = $this->input->post('mensaje');
        $usuario = $this->input->post('usuario');
        $this->chatmodel->addMensaje($usuario,$mensaje);
        $data['fecha'] = date('Y-m-d H:i:s');
        $data['usuario'] = $usuario;
        $data['mensaje'] = $mensaje;
        echo json_encode($data);
        
    }
}

El constructor carga nuestro modelo del chat, para luego usar sus métodos a lo largo del controlador, y también el helper de la url, para poner las urls dinamicas luego dentro de la vista. El index cargaría nuestra vista, y los otros dos metodos son para llamarlos mediante ajax y hacer las operaciones en nuestro chat (recargar el chat con los nuevos mensajes, y añadir un mensaje al chat para el resto de usuarios). Estos devolverían el resultado de la base de datos en formato JSON.

Queda añadir la vista para completar nuestro chat. Para ello creamos el archivo chat_vista.php que pondremos en la carpeta application/views. El codigo para nuestra vista será:
<html>
<head>
Simple chat by danidomen


</head>
<body>
 
</body> </html>

El script js que hay que añadir en el comentario entre ## en el código anterior:
$(document).ready(function (){
 var styloFila2 = "background-color:#F0F0F0";
    var styloFila1 = "background-color:#FFF";
    var nMensaje = 1;
    var idMensajeReportado = 0;
    var enviado = false;
    
    $('#chat_mensaje').bind('keyup', function(e){
        if(e.keyCode==13){
            if(!enviado){
                enviado = true;
                addVentanaChatContenido();
            }
            
        }
    });
    
    function addVentanaChatContenido(){  
        $.ajax({
            type: "POST",
            data: "usuario=<?php echo $usuario;?>&mensaje="+$('#chat_mensaje').val(),
            url: '<?php echo base_url();?>chat/add_mensaje_canal',
            dataType: "json",
            cache: false,
            success: function(data){
                $("#chat_mensaje").val(""); 
                stiloFila = styloFila2;
                if(nMensaje%2!=0){
                    stiloFila = styloFila1;
                }
                $("#TableChatContent").append(''+
                                        '<tr style="'+stiloFila+'">'+
                                            '<td width="85%"><strong>@'+data.usuario+': </strong><i>'+data.mensaje+'</i></td>'+
                                            '<td style="font-size:10px"><i title="'+data.fecha.substr(0,10)+'">'+data.fecha.substr(11,8)+'</i></td>'+
                                        '</tr>');
                 $("#chat_content").scrollTop(5000000);
                 nMensaje++;
                 enviado=false;
            }
            
        });
    }
    
    function recargarVentanaChat(){
        setInterval(function(){
            $.ajax({
    type: "POST",
                dataType: "json",
                url: '<?php echo base_url();?>chat/mensajes_canal',
                cache: false,
    data: "usuario=<?php echo $usuario;?>",
                success: function(data){
                    $.each(data,function(index,value) {
                        stiloFila = styloFila2;
                        $('#trMensaje_'+data[index].id_chat_mensaje).remove();        
      if(nMensaje%2!=0){
       stiloFila = styloFila1;
      }
      $("#TableChatContent").append(''+
          '<tr id="trMensaje_'+data[index].id_chat_mensaje+'" style="'+stiloFila+'">'+
           '<td width="85%"><strong>@'+data[index].m_chat_usuario+': </strong><i>'+data[index].chat_mensaje+'</i></td>'+
           '<td style="font-size:10px"><i title="'+data[index].creado_en.substr(0,10)+'">'+data[index].creado_en.substr(11,8)+'</i></td>'+
          '</tr>');
      nMensaje++;
                            
                    });
                    


                }
                
            });
            $("#chat_content").scrollTop(5000000);
        },2000);
    }

    recargarVentanaChat();

});
En este script estamos creando la funcion que al pulsar "Intro" sobre el input de introducir texto, llame por ajax a la funcion de addVentanaChatContenido() que mandará al controlador y al método add_mensaje_canal el usuario y mensaje actual. Luego por JSON añade el mensaje a la tabla. Igualmente hace la funcion de recargarVentanaChat, que en este caso tiene un interval de 2 segundos. Además si por algún casual se repitiese algún mensaje, ya se encarga de borrarlos para que eso no suceda.

Ya solo tenéis que arrancar vuestro servidor, acceder a localhost/vuestroproyecto/index.php/chat/index/nombredeusuario localhost/vuestroproyecto/chat/index/nombredeusuario en el caso de que hayais modificado correctamente el ruteo del Codeigniter. Y voilá! Ya tenemos chat. Puedes abrir varias instancias con distintos nombres de usuarios y comprobar que el chat funciona. Si te diera cualquier problema, o no consiguieras hacerlo funcionar, no dudes en comentar :)

Notas sobre configuración:
- Recordar que en el archivo "application/config/database.php" teneís puesta correctamente vuestra configuración de base de datos
- En "application/config/autoload.php", teneís al menos puesta la libreria de base de datos ($autoload['libraries'] = array('database');)
- En "application/config/config.php" la base_url está correctamente puesta. En mi caso, el proyecto al llamarse "chat" dentro de mi htdocs del xampp, mi base_url sería tal que: $config['base_url'] = 'http://localhost/chat/';

Actualización 05/04/2016: Aplicados los cambios que el compañero Ferni comentaba, ya que esto era totalmente incompatible con la versión 3 de Codeigniter. También se han cambiado las rutas a las que tiene que apuntar (chat/index/nombreusuario) ya que yo debía tener unas routes especiales de las que ni me acuerdo :S. Se ha modificado la función recargarVentanaChat ya que daba error porque le faltaba que fuera de tipo POST. Se han añadido unas últimas indicaciones para la configuración al final del post.

13 comentarios:

  1. Que crack! Lo implementamos en Kylook? :D

    ResponderEliminar
  2. Muy útil! me ha ahorrado un montón de tiempo, pero he tenido que hacer un par de correcciones para que funcionara:
    Se queja de repetición de nombre de clase, así que al modelo le he llamado chat_model. Hay que cambiar el nombre del archivo, de la clase,
    y en el controlador cambiar las llamadas this->chat-> por this->chat_model->

    Falta una coma en el script, después de
    data: "usuario=",

    Y en el modelo de momento he quitado las dos clausulas where para que funcione.

    ResponderEliminar
    Respuestas
    1. Hola Ferni!

      Muchísimas gracias por el comentario y me alegro que te haya servido de ayuda. Ya he pegado la corrección al script que apuntabas ;)

      Eliminar
    2. Hola, soy nueva en esto. Y tengo una duda. Me arrojan estos errores.

      A PHP Error was encountered

      Severity: Warning

      Message: Missing argument 1 for Chat::index()

      Filename: controllers/chat.php

      Line Number: 14

      Backtrace:

      File: C:\xampp\htdocs\TEChat\application\controllers\chat.php
      Line: 14
      Function: _error_handler

      File: C:\xampp\htdocs\TEChat\index.php
      Line: 315
      Function: require_once

      A PHP Error was encountered

      Severity: Notice

      Message: Undefined variable: nickusuario

      Filename: controllers/chat.php

      Line Number: 15

      Backtrace:

      File: C:\xampp\htdocs\TEChat\application\controllers\chat.php
      Line: 15
      Function: _error_handler

      File: C:\xampp\htdocs\TEChat\index.php
      Line: 315
      Function: require_once

      Eliminar
    3. Hola Elisa,

      Te está dando un error referente al paso de "Ya solo tenéis que arrancar vuestro servidor, acceder a localhost/vuestroproyecto/index.php/chat/nombredeusuario o localhost/vuestroproyecto/chat/nombredeusuario en el caso de que hayais modificado correctamente el ruteo del Codeigniter"

      Es decir, lo que te está diciendo, es que le falta el nick del usuario (Message: Undefined variable: nickusuario) porque en la URL le tienes que poner el nick del usuario. Te debería quedar algo tal que: http://localhost/TEChat/index.php/chat/elisa

      Cualquier cosa, me comentas :)

      Eliminar
    4. Este comentario ha sido eliminado por el autor.

      Eliminar
    5. Hola Daniel gracias por responder! Mira de hecho le hice de esa manera.
      http://localhost/TEChat/index.php/chat/elisa
      Me arroja el error "404 page not found"

      Intenté con esto otro:
      http://localhost/TEChat/index.php/chat/index/elisa
      En cuyo caso, me manda solo el input. No me muestra nada más.
      Perdón por el spam. Sé que se debe a alguna nimiedad. Podrías proporcionarme alguna dirección de correo? Creo que es parecido a lo que te comentaba Jeisson H.

      Saludos.

      Eliminar
    6. Hola Elisa,

      La verdad es que estaba bastante desactualizado esto con el tema de la versión 3 de Codeigniter.

      He realizado en un momento el proyecto de 0, y he corregido el artículo así como añadido algunos comentarios.

      Sobre la URL, llevas razón, es con el xx/chat/index/usuario, quizá antiguamente tenía algo en las routes para que no fuera necesario.

      De todas formas, te dejo aquí un .rar con los 3 ficheros para directamente ponerlos en tu proyecto, aunque te aconsejo revisar de nuevo el post por las actualizaciones que he metido (sobretodo al final).
      DESCARGAR chat_codeigniter_3_0_6.rar
      https://drive.google.com/open?id=0B4Ja1gsDxX-wbzdtVmhmWERjT0U

      Un saludo.

      Eliminar
    7. Hola Daniel, Gracias por atender mis dudas, muy pocos bloggers se dedican a responder a los usuarios. Excelente post.

      Eliminar
  3. Hola, lo que pasa es que estoy probando éste chat y no funciona, hice las correciones de Ferni y tampoco. En la vista sólo me carga un input, escribo algo y al momento de pulsar 'Enter' no pasa nada, qué puedo hacer por favor???, gracias de antemano por la ayuda

    ResponderEliminar
    Respuestas
    1. Hola Jeisson,

      Acabo de darme cuenta, que aquí tenía un comentario :S

      Ha pasado mucho tiempo, y quizá lo resolviste, pero si puedo serte de ayuda en algo, aquí estoy.

      Un saludo.

      Eliminar
    2. tengo el mismo error de jeison solo me carga el input y como dice el tampoco me hace nada

      Eliminar
    3. Hola Samuel,

      Abre la consola de chrome e intenta hacer una captura y postearla para poder ver el error que te está dando.

      Un saludo.

      Eliminar