Sistema de comentarios AJAX simple

Esta vez, estamos creando un sistema de comentarios AJAX simple. Contará con una integración gravatar y demostrará cómo lograr una comunicación efectiva entre jQuery y PHP/MySQL con la ayuda de JSON.

Paso 1 - XHTML

Primero, echemos un vistazo al marcado de los comentarios. Este código es generado por PHP en la clase Comentario, que veremos en un momento.

demo.php

<div class="comment">
    <div class="avatar">
        <a href="https://tutorialzine.com/">
        <img src="http://www.gravatar.com/avatar/112fdf7a8fe3609e7af2cd3873b5c6bd?size=50&default=http%3A%2F%2Fdemo.tutorialzine.com%2F2010%2F06%2Fsimple-ajax-commenting-system%2Fimg%2Fdefault_avatar.gif">
        </a>
    </div>

    <div class="name"><a href="https://tutorialzine.com/">Person's Name</a></div>
    <div title="Added at 06:40 on 30 Jun 2010" class="date">30 Jun 2010</div>
    <p>Comment Body</p>
</div>

El div de avatar contiene un hipervínculo (si el usuario ingresó una URL válida al enviar el comentario) y una imagen de avatar, que se obtiene de gravatar.com. Volveremos a esto en el paso de PHP del tut. Por último, tenemos las divisiones de nombre y tiempo, y el cuerpo del comentario.

El otro elemento importante en la parte XHTML es el formulario de comentarios. Se envía por POST . Todos los campos excepto el campo URL son obligatorios.

demo.php

<div id="addCommentContainer">
    <p>Add a Comment</p>
    <form id="addCommentForm" method="post" action="">
        <div>
            <label for="name">Your Name</label>
            <input type="text" name="name" id="name" />

            <label for="email">Your Email</label>
            <input type="text" name="email" id="email" />

            <label for="url">Website (not required)</label>
            <input type="text" name="url" id="url" />

            <label for="body">Comment Body</label>
            <textarea name="body" id="body" cols="20" rows="5"></textarea>

            <input type="submit" id="submit" value="Submit" />
        </div>
    </form>
</div>

El formulario se envía a través de AJAX. La validación se realiza completamente en el backend por submit.php , como verá en el paso jQuery del tutorial. Cada campo tiene un elemento de etiqueta correspondiente, con un para apropiado atributo.

Paso 2 - PHP

PHP maneja la comunicación con la base de datos MySQL y genera el marcado de los comentarios. También está en el extremo receptor de las solicitudes de AJAX e inserta los datos de los comentarios en los comentarios. mesa. Puede ver el código que imprime los comentarios en la página siguiente.

demo.php

/*
/   Select all the comments and populate the $comments array with objects
*/

$comments = array();
$result = mysql_query("SELECT * FROM comments ORDER BY id ASC");

while($row = mysql_fetch_assoc($result))
{
    $comments[] = new Comment($row);
}

La consulta MySQL selecciona todas las entradas de la base de datos y llena los $comentarios array con objetos de la clase comment, que verás a continuación. Esta matriz se genera más adelante en la ejecución del script.

demo.php

/*
/   Output the comments one by one:
*/

foreach($comments as $c){
    echo $c->markup();
}

Cada comentario tiene un marcado() método, que genera código HTML válido listo para ser impreso en la página. Puede ver la definición de este método y la clase a continuación.

La clase toma una fila de la base de datos (obtenida con mysql_fetch_assoc() ) y lo almacena en la variable privada $data . Está disponible solo para los métodos de la clase y no se puede acceder desde el exterior.

comentario.clase.php - Parte 1

class Comment
{
    private $data = array();

    public function __construct($row)
    {
        /*
        /   The constructor
        */

        $this->data = $row;
    }

    public function markup()
    {
        /*
        /   This method outputs the XHTML markup of the comment
        */

        // Setting up an alias, so we don't have to write $this->data every time:
        $d = &$this->data;

        $link_open = '';
        $link_close = '';

        if($d['url']){

            // If the person has entered a URL when adding a comment,
            // define opening and closing hyperlink tags

            $link_open = '<a href="'.$d['url'].'">';
            $link_close =  '</a>';
        }

        // Converting the time to a UNIX timestamp:
        $d['dt'] = strtotime($d['dt']);

        // Needed for the default gravatar image:
        $url = 'http://'.dirname($_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]).
                '/img/default_avatar.gif';

        return '

            <div class="comment">
                <div class="avatar">
                    '.$link_open.'
                    <img src="http://www.gravatar.com/avatar/'.
                md5($d['email']).'?size=50&default='.
                urlencode($url).'" />
                    '.$link_close.'
                </div>

                <div class="name">'.$link_open.$d['name'].$link_close.'</div>
                <div class="date" title="Added at '.
                date('H:i \o\n d M Y',$d['dt']).'">'.
                date('d M Y',$d['dt']).'</div>
                <p>'.$d['body'].'</p>
            </div>
        ';
    }

Este script usa gravatar para presentar avatares en los comentarios. Para aquellos de ustedes que no han usado gravatar, este es un servicio realmente útil, que les permite asociar un avatar con su dirección de correo electrónico. La imagen del avatar se puede recuperar fácilmente pasando un md5() hash codificado de su dirección de correo electrónico a gravatar.com. Esto es exactamente lo que hacemos en línea 48 .

Observe línea 39 arriba:el script intenta averiguar la URL en la que se encuentra y determina la dirección exacta del default_avatar.gif imagen. Este gif se pasa a gravatar a lo largo del hash md5, por lo que si no se encuentra ningún avatar para este correo electrónico en particular, se muestra la imagen alternativa en su lugar.

comentario.clase.php - Parte 2

 public static function validate(&$arr)
    {
        /*
        /   This method is used to validate the data sent via AJAX.
        /
        /   It return true/false depending on whether the data is valid, and populates
        /   the $arr array passed as a paremter (notice the ampersand above) with
        /   either the valid input data, or the error messages.
        */

        $errors = array();
        $data   = array();

        // Using the filter_input function introduced in PHP 5.2.0

        if(!($data['email'] = filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)))
        {
            $errors['email'] = 'Please enter a valid Email.';
        }

        if(!($data['url'] = filter_input(INPUT_POST,'url',FILTER_VALIDATE_URL)))
        {
            // If the URL field was not populated with a valid URL,
            // act as if no URL was entered at all:

            $url = '';
        }

        // Using the filter with a custom callback function:

        if(!($data['body'] = filter_input(INPUT_POST,'body',FILTER_CALLBACK,
                        array('options'=>'Comment::validate_text'))))
        {
            $errors['body'] = 'Please enter a comment body.';
        }

        if(!($data['name'] = filter_input(INPUT_POST,'name',FILTER_CALLBACK,
                        array('options'=>'Comment::validate_text'))))
        {
            $errors['name'] = 'Please enter a name.';
        }

        if(!empty($errors)){

            // If there are errors, copy the $errors array to $arr:

            $arr = $errors;
            return false;
        }

        // If the data is valid, sanitize all the data and copy it to $arr:

        foreach($data as $k=>$v){
            $arr[$k] = mysql_real_escape_string($v);
        }

        // Ensure that the email is in lower case (for a correct gravatar hash):
        $arr['email'] = strtolower(trim($arr['email']));

        return true;

    }

El validar() El método anterior (también parte de la clase) se define como estático . Esto significa que se puede evocar directamente como Comment::validate() , sin necesidad de crear un objeto de la clase. Lo que hace este método es validar los datos de entrada que se envían a través de AJAX.

Este método utiliza las nuevas funciones de filtro, que están disponibles a partir de PHP 5.2.0 . Estos nos permiten validar y filtrar fácilmente cualquier dato de entrada que se pase al script. Por ejemplo filter_input(INPUT_POST,'url',FILTER_VALIDATE_URL) significa que estamos comprobando si $_POST['url'] es una dirección URL válida. Si es así, la función devuelve el valor de la variable; de ​​lo contrario, devuelve falso .

Esto es realmente útil, ya que hasta ahora teníamos que usar expresiones regulares personalizadas para validar datos (y tener una serie de declaraciones if). Además, otra ventaja es que estos datos se obtienen antes de que se apliquen transformaciones específicas de configuración (como comillas mágicas).

También tenemos la opción de especificar una función personalizada que aplicará algunas modificaciones más avanzadas de los datos, como puede ver en las líneas 31 y 37.

comentario.clase.php - Parte 3

 private static function validate_text($str)
    {
        /*
        /   This method is used internally as a FILTER_CALLBACK
        */

        if(mb_strlen($str,'utf8')<1)
            return false;

        // Encode all html special characters (<, >, ", & .. etc) and convert
        // the new line characters to <br> tags:

        $str = nl2br(htmlspecialchars($str));

        // Remove the new line characters that are left
        $str = str_replace(array(chr(10),chr(13)),'',$str);

        return $str;
    }

}

El último método es validate_text, que estamos pasando como una función de devolución de llamada en las dos llamadas filter_input anteriores. Codifica todos los caracteres HTML especiales, bloqueando efectivamente los ataques XSS. También reemplaza los caracteres de nueva línea con
saltos de línea.

enviar.php

/*
/   This array is going to be populated with either
/   the data that was sent to the script, or the
/   error messages:
/*/

$arr = array();

$validates = Comment::validate($arr);

if($validates)
{
    /* Everything is OK, insert to database: */

    mysql_query("   INSERT INTO comments(name,url,email,body)
                    VALUES (
                        '".$arr['name']."',
                        '".$arr['url']."',
                        '".$arr['email']."',
                        '".$arr['body']."'
                    )");

    $arr['dt'] = date('r',time());
    $arr['id'] = mysql_insert_id();

    /*
    /   The data in $arr is escaped for the mysql insert query,
    /   but we need the unescaped text, so we apply,
    /   stripslashes to all the elements in the array:
    /*/

    $arr = array_map('stripslashes',$arr);

    $insertedComment = new Comment($arr);

    /* Outputting the markup of the just-inserted comment: */

    echo json_encode(array('status'=>1,'html'=>$insertedComment->markup()));

}
else
{
    /* Outputting the error messages */
    echo '{"status":0,"errors":'.json_encode($arr).'}';
}

enviar.php recibe los datos del formulario de comentarios a través de una solicitud AJAX. Lo valida y genera un objeto JSON con el marcado XHTML del comentario insertado correctamente o una lista de mensajes de error. jQuery usa la propiedad de estado para determinar si mostrar los mensajes de error o agregar el marcado de comentario a la página.

Puede ver dos respuestas de ejemplo a continuación.

Respuesta exitosa

{
    "status": 1,
    "html": "Html Code Of The Comment Comes Here..."
}

El html La propiedad contiene el código del comentario, similar al marcado en el paso uno.

Respuesta de falla

{
    "status": 0,
    "errors": {
        "email": "Please enter a valid Email.",
        "body": "Please enter a comment body.",
        "name": "Please enter a name."
    }
}

En caso de falla, jQuery recorre el objeto de errores y muestra los errores junto a los campos que los causaron.

Paso 3 - CSS

Ahora que tenemos todas las marcas correctamente generadas y mostradas en la página, podemos pasar a darle estilo.

estilos.css - Parte 1

.comment,
#addCommentContainer{

    /* Syling the comments and the comment form container */

    padding:12px;
    width:400px;
    position:relative;
    background-color:#fcfcfc;
    border:1px solid white;
    color:#888;
    margin-bottom:25px;

    /* CSS3 rounded corners and drop shadows */

    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;

    -moz-box-shadow:2px 2px 0 #c2c2c2;
    -webkit-box-shadow:2px 2px 0 #c2c2c2;
    box-shadow:2px 2px 0 #c2c2c2;
}

.comment .avatar{

    /*
    /   The avatar is positioned absolutely,
    /   and offset outside the comment div
    /*/

    height:50px;
    left:-70px;
    position:absolute;
    width:50px;
    background:url('img/default_avatar.gif') no-repeat #fcfcfc;

    /* Centering it vertically: */

    margin-top:-25px;
    top:50%;

    -moz-box-shadow:1px 1px 0 #c2c2c2;
    -webkit-box-shadow:1px 1px 0 #c2c2c2;
    box-shadow:1px 1px 0 #c2c2c2;
}

Los divs .comment y #addCommentContainer tienen estilo a la vez porque comparten la mayor parte del estilo. Se aplican una serie de reglas CSS3, incluidas las esquinas redondeadas y una sombra de cuadro. No hace falta decir que estos no funcionan en navegadores más antiguos, pero como son puramente de presentación, el script seguirá funcionando sin ellos.

estilos.css - Parte 2

.comment .avatar img{
    display:block;
}

.comment .name{
    font-size:20px;
    padding-bottom:10px;
    color:#ccc;
}

.comment .date{
    font-size:10px;
    padding:6px 0;
    position:absolute;
    right:15px;
    top:10px;
    color:#bbb;
}

.comment p,
#addCommentContainer p{
    font-size:18px;
    line-height:1.5;
}

#addCommentContainer input[type=text],
#addCommentContainer textarea{

    /* Styling the inputs */

    display:block;
    border:1px solid #ccc;
    margin:5px 0 5px;
    padding:3px;
    font-size:12px;
    color:#555;
    font-family:Arial, Helvetica, sans-serif;
}

#addCommentContainer textarea{
    width:300px;
}

label{
    font-size:10px;
}

label span.error{
    color:red;
    position:relative;
    right:-10px;
}

#submit{

    /* The submit button */

    background-color:#58B9EB;
    border:1px solid #40A2D4;
    color:#FFFFFF;
    cursor:pointer;
    font-family:'Myriad Pro',Arial,Helvetica,sans-serif;
    font-size:14px;
    font-weight:bold;
    padding:4px;
    margin-top:5px;

    -moz-border-radius:4px;
    -webkit-border-radius:4px;
    border-radius:4px;
}

#submit:hover{
    background-color:#80cdf5;
    border-color:#52b1e2;
}

En la segunda parte de la hoja de estilo, aplicamos estilo a los elementos de comentarios y formularios. Observe la entrada[tipo=texto] Selector, que selecciona elementos según el atributo de tipo.

Paso 4 - jQuery

Ahora continuemos con jQuery, que es el último paso de este tutorial. Después de incluir la biblioteca en la parte inferior de la página (lo mejor para el rendimiento percibido de la página), podemos comenzar a codificar el archivo de secuencia de comandos.

secuencia de comandos.js

$(document).ready(function(){
    /* The following code is executed once the DOM is loaded */

    /* This flag will prevent multiple comment submits: */
    var working = false;

    /* Listening for the submit event of the form: */
    $('#addCommentForm').submit(function(e){

        e.preventDefault();
        if(working) return false;

        working = true;
        $('#submit').val('Working..');
        $('span.error').remove();

        /* Sending the form fileds to submit.php: */
        $.post('submit.php',$(this).serialize(),function(msg){

            working = false;
            $('#submit').val('Submit');

            if(msg.status){

                /*
                /   If the insert was successful, add the comment
                /   below the last one on the page with a slideDown effect
                /*/

                $(msg.html).hide().insertBefore('#addCommentContainer').slideDown();
                $('#body').val('');
            }
            else {

                /*
                /   If there were errors, loop through the
                /   msg.errors object and display them on the page
                /*/

                $.each(msg.errors,function(k,v){
                    $('label[for='+k+']').append('<span class="error">'+
                        v+'</span>');
                });
            }
        },'json');

    });

});

Comenzando desde arriba, tenemos el $(document).ready() llamada, que vincula una función al contenido DOM cargado evento. La variable de trabajo actúa como una bandera, que le dice al script si hay una solicitud AJAX en curso (evitando así la publicación doble).

En la función de devolución de llamada para la solicitud POST AJAX, verificamos la propiedad de estado para determinar si el comentario se insertó correctamente. Si lo fue, agregamos el marcado recibido a la página después del último comentario con un slideDown animación.

Si hubo problemas, mostramos los mensajes de error agregando un intervalo de error al elemento de etiqueta apropiado (el atributo for de la etiqueta contiene la identificación de la entrada que causó el error).

¡Con esto, nuestro Sistema de Comentarios Simple AJAX está completo!

Conclusión

Para poder ejecutar este script en su servidor, debe crear los comentarios tabla en su base de datos MySQL. Puede hacerlo ejecutando el código SQL que se encuentra en table.sql desde la pestaña SQL de phpMyAdmin. Después de esto, debe ingresar los detalles de su conexión MySQL en connect.php .

Eres libre de modificar y usar este código de la forma que creas conveniente.

¿Qué opinas? ¿Cómo mejorarías este guión?