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.