Manuales Joomla

Sistema de comentarios - Tutorial 3, Listado de comentarios

(Lo puedes leer en: 11 - 21 minutos)

por .

Ya tenemos nuestro formulario enviando comentarios vía Ajax, y sin chistar. En esta parte solo nos falta visualizar los últimos comentarios del artículo debajo del formulario para comentar. ¡Manos a la obra!.

Espero que hayan agregado las cadenas de texto que hablamos en el final del segundo tutorial, recuerden que las mismas van dentro del archivo de idioma del componente, aunque pudiéramos ponerlas dentro del plugin, pero para mantener la centralización, pues las colocamos en el componente, que de paso tiene más lógica ponerlas aquí.

Recuerden que en GITHUB tenemos nuestro repositorio, en el cual pueden ir encontrando el paquete hasta donde lo hayamos dejado. No duden en seguirlo, ya que este es nuestro espacio para almacenar todo lo que vayamos creando a lo largo de este grandioso proyecto que Manuales Joomla trae para ustedes.

Desarrollo de la lógica para el modelo del listado de comentarios

Entonces, nos toca mostrar una lista de comentarios del artículo. Ya más o menos deben irse figurando por donde va la cosa. Necesitamos mostrar una lista con los últimos comentarios del artículo. Por lo que necesitamos acceder a la tabla de la base de datos, recopilar todos los comentarios de un artículo determinado (filtrando por el ID del mismo), hacer alguna comprobación en caso de que sea necesario, y devolver esa lista a una vista para que la muestre. En otras palabras, necesitamos un modelo para el trabajo de la recopilación en la base de datos, y un layout en el plugin para mostrar lo recopilado. Empezamos por el modelo.

Lo primero a implementar es la función que nos va a obtener el listado de los comentarios de un determinado artículo. Para ello, creamos una archivo llamado “formlist.php” dentro de la carpeta “models” el cual va a ser el modelo para todo el trabajo de recopilación de comentarios. Abrimos el archivo y lo editamos, quedándonos de esta forma:

  1. span style="color: #808080; font-style: italic;">/**
  2.  * @package MJ.Component
  3.  * @subpackage mjcomments
  4.  *
  5.  * @copyright Copyright (C) 2015 Carlos Rodriguez. All rights reserved.
  6.  * @license License http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
  7.  */'_JEXEC'/**
  8.  * Form list model class
  9.  *
  10.  *//**
  11.   * Method to get a list of comment.
  12.   *
  13.   * @return mixed An array of objects on success, false on failure.
  14.   *
  15.   */'filter.content_id'// Get a storage key.
  16. $store = $this->getStoreId();
  17.  
  18. // Try to load the data from internal storage.
  19. // Load the list items.
  20. 'list.limit'// Add the items to the internal cache.
Source code

Modelo del componente - Obtener lista de ocmentarios
Modelo del componente - Ontener lista de comentarios

Lo primero que hemos escrito aquí, es la declaración de la clase a utilizar. Recuerden que el nombre de la clase es siguiendo la nomenclatura que Joomla establece. Pero esta vez, hemos heredado nuestra clase, de una clase padre diferente. En el otro modelo la clase padre es “JModelForm” ya que ella implementa funciones referentes a la creación y validación de formularios. Esta vez, la clase padre es “JModelList” (libraries\legacy\model\list.php). Esta clase sus funciones están dedicadas a la obtención de una lista de “items” en nuestro caso de comentarios, a la cual se le puede aplicar paginación, orden, dirección, etc.

Nuestra primera función en esta clase se llama “getComments”. Esta función es propia de nosotros y no aparece dentro de la clase “JModelList”, pero es derivada (casi igual) de la función “getItems” de esta clase. La única diferencia, es que la función “getComments” recibe un argumento que es el ID del artículo para el cual debemos obtener la lista de comentarios en caso de que tenga comentarios claro está. Este argumento nos lo pasa el plugin, cuando desde allá, llamemos a esta función. Luego, en la línea 25 pueden ver como se establece una especie de filtro, el cual toma el ID del artículo pasado como argumento en la función. Estos filtros son una genialidad. Gracias a estos filtros, podemos por ejemplo desde la implementación de un módulo establecer valores diferentes para estos filtro en una determinada consulta que queramos hacer a la base de datos, sin necesidad de hacer en el módulo una función que haga lo mismo. Sencillamente, utilizamos la misma función del componente, cambiamos algunas variables (filtros) obteniendo un resultado determinado. Y con esto, seguimos asegurando nuestra centralización. Esto lo verán con más claridad más adelante en este ciclo.

Luego, en la línea 28, aparece la función “getStoreId”. Esta función pertenece a la clase padre. Su objetivo es obtener los valores establecidos para cada filtro que utilicemos en nuestro modelo. En la función de la clase padre, verán que ya hay 4 filtros establecidos. Uno (list.start), especificando el comienzo de la lista, por ejemplo, si queremos que la lista de comentarios que obtenemos de un artículo, empiece en el tercer comentario dejando de mostrar el primero y el segundo. Otro (list.limit), especificando el límite de la lista, es decir, el número máximo de comentarios que se van a mostrar. Otro (list.ordering), especificando en qué orden se va a mostrar la lista, por ejemplo, por fecha de creación del comentario (esto por supuesto tenemos que definirlo antes). Otro (list.direction), especificando la dirección en que se va a mostrar la lista, por ejemplo, de forma descendente.

Por supuesto, los valores para estos filtros los asignamos en nuestro modelo. Aquí solo están definiendo 4 filtros que son básicos en cualquier lista de “items”.

Línea 31, el modelo padre de nuestra clase implementa un almacenamiento interno evitando repetir consultas innecesarias.

Línea 37, aquí llamamos a otra función de la clase padre. Siguiendo lo del almacenamiento interno, esta nos asegura que la consulta se construya una sola vez para un determinado estado del modelo. Es decir, que no va a construir una consulta igual una y otra vez, sino que, si ya la consulta fue creada en un determinado estado del modelo se devolverá esa consulta y no la creación de una nueva.

Luego en la línea 41, llamamos a la función “_getList” de la clase “JModelLegacy”. Nuestra clase padre “JModelList” hereda a su vez de otra clase padre llamada “JModelLegacy” que es la clase base para todos los tipos de modelos “JModelForm”, “JModelList”, “JModelItem”. Entonces, la función lo que hace es ejecutar la consulta creada previamente, devolviendonos un array indexado de objetos PHP a partir de los registros de la tabla devueltos por la consulta.

Como pudieron darse cuenta, no es tan difícil lo que acabamos de hacer, teniendo en cuenta que Joomla nos hace más de la mitad del trabajo. Pero nuestra función la podemos optimizar aún más. Si se fijan, como bien decíamos antes, nuestra función “getItems” es casi igual a la función de la clase padre “JModelList”, y exactamente a partir de la línea 28. Por lo que si en ese punto llamamos a la función de la clase padre, ¿no creen que funcionaria de la misma manera?

Otra cosa que pudiéramos mejorar, autodocumentado al mismo tiempo nuestra extensión, es ponerle un nombre que referencie lo que estamos haciendo aquí, ya que “getComments” es muy general. Por ejemplo, podemos llamarle “getCommentsArticle”. De esta forma nos referimos a la obtención de los comentarios de un artículo.

Aplicando estos cambios nos quedaría todo de esta forma:

  1. span style="color: #808080; font-style: italic;">/**
  2.  * @package MJ.Component
  3.  * @subpackage mjcomments
  4.  *
  5.  * @copyright Copyright (C) 2015 Carlos Rodriguez. All rights reserved.
  6.  * @license License http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
  7.  */'_JEXEC'/**
  8.  * Form list model class
  9.  *
  10.  *//**
  11.   * Method to get a list of comment.
  12.   *
  13.   * @param int $content_id ID of the article for load comments
  14.   *
  15.   * @return mixed An array of objects on success, false on failure.
  16.   *
  17.   */'filter.content_id'
Source code

Modelo del componente - Mejora en la función para obtener los comentarios de un artículo

De la creación de esta función, se nos derivaron varias implementaciones más. Recuerden que esta función hace uso de otras funciones de la clase padre que debemos implementar, añadiendo algunos aspectos particulares para nosotros. Empecemos por la función “getStoreId”.

  1. /**
  2.  * Method to get a store id based on model configuration state.
  3.  *
  4.  * This is necessary because the model is used by the component and
  5.  * different modules/plugins that might need different sets of data or different
  6.  * ordering requirements.
  7.  *
  8.  * @param string $id A prefix for the store id.
  9.  *
  10.  * @return string A store id.
  11.  *
  12.  */'')
  13. {
  14. // Compile the store id.
  15. $id .= ':''filter.content_id'
Source code

Modelo "formlist" del componente - Función para obtener los valores establecidos para cada filtro que utilizamos en el modelo

Ya con esto, el modelo va a saber el valor para nuestro filtro “content_id”.

Ahora vamos a poblar nuestro modelo. Recuerden que hay algunos valores que el modelo necesita saber al ser instanciado (vean más arriba la explicación de la función “getStoreId” de la clase padre). Para esto usaremos la función de la clase padre “populateState”:

  1. /**
  2.  * Method to auto-populate the model state.
  3.  *
  4.  * @param string $ordering An optional ordering field.
  5.  * @param string $direction An optional direction (asc|desc).
  6.  *
  7.  * @return void
  8.  *
  9.  * @since 12.2
  10.  */'created''DESC')
  11. {
  12. // List state information
  13. 'list.ordering''list.direction''list.start''list.limit', 0);
  14. }
Source code

Modelo "formlist" del componente - Función para poblar el modelo

Ya hemos poblado nuestro modelo. Esta función sólo se debe llamar una vez por instanciación y está diseñada para ser llamada al llamar por primera vez al método “getState()”. Observen que hemos puesto a ordenar nuestra lista de comentarios por el campo “created”. También observen que tanto el filtro “list.start” como “list.limit” lo establecemos en 0. Para nuestro, esto significa que nos va a mostrar toda la lista de comentarios que tenga el artículo. Más adelante podemos moderar esto, por ejemplo, agregando 2 parámetros en la administración del componente, donde podamos definir qué cantidad de comentarios queremos mostrar por cada artículo. ¿Se puede dividir la lista de comentarios, con una paginación? Por supuesto. Si quieren que los comentarios se vean de 5 en 5, y usar una especie de paginación para irlos viendo, podemos configurar estos valores y dos o tres más, para hacer que se paginen los comentarios. Ya veremos más adelante en el ciclo algo de esta forma.

Ya tenemos nuestro modelo poblado, con sus filtros establecidos. Ya estamos en condiciones de crear la consulta que le va a ser a la base de datos nuestro modelo. La función “getCommentsArticle” del modelo, recuerden que tiene dentro una llamada a dos funciones con nombres “_getListQuery” y “_getList”. La primera es para conformar la consulta, y la segunda, para obtener el resultado de la consulta creada. Como “_getList” ya está implementada, y se le pasa como argumento lo que devuelve “_getListQuery”, nadamas tenemos que utilizar “_getListQuery”. Y digo utilizar y no crear, porque esta función es de la clase padre y ya está implementada. Tan solo tenemos que fijarnos que “_getListQuery” hace una llamada a la función “getListQuery” que es en sí, la que crea la consulta que vamos hacer.

La función “getListQuery” en la clase padre sólo crea una nueva instancia de la clase JDatabaseQuery. Por lo que ya tenemos el comienzo para la función de nuestra clase:

  1. /**
  2.  * Get the master query for retrieving a list of comment subject to the model state.
  3.  *
  4.  * @return JDatabaseQuery
  5.  *
  6.  */// Select the required fields from the table.
  7. 'list.select',
  8. 'a.id, a.content_id, a.visitor_name, a.state, ' .
  9. 'a.visitor_email, a.visitor_comments, a.created'
  10. )
  11. );
  12.  
  13. $query->from('#__mjcomments AS a');
  14.  
  15. // Join over the article.
  16. $query->select('c.title AS content_title, c.alias AS content_alias, c.catid AS content_category, c.language AS content_language''LEFT', '#__content AS c ON c.id = a.content_id');
  17.  
  18. // Filter by a single article.
  19. 'filter.content_id''a.content_id = ' . (int) $contentId);
  20. }
  21.  
  22. // Add the list ordering clause.
  23. 'list.ordering', 'a.created') . ' ''list.direction', 'DESC'));
  24. }
Source code

Modelo "formlist" del componente - Creación de la consulta

Observen que hemos utilizado otro filtro (“list.select”) en la selección de los campos, aunque más bien es como una variable. En la clase “JModelList” en la función populateState se le da tratamiento a la misma.

También hicimos un “join” con la tabla de artículos de Joomla, para obtener algunos datos relacionados del artículo para el cual estamos obteniendo sus comentarios. Ahora esto no nos hace falta, porque desde el plugin tenemos disponible todos estos datos, y tampoco tenemos que mostrar ningún dato del artículo. Pero, esto nos va a ser útil más adelante. Por el momento lo dejamos. Pero si gustan pueden quitar esta parte.

Después utilizamos el filtro “content_id” para que la consulta se filtre solo para los registros que tengan este ID dentro del campo “content_id” de nuestra tabla en la base de datos.

También utilizamos los filtros de dirección y orden, para establecer precisamente el orden de la lista que vamos a recuperar.
¿Se pudieron dar cuenta de la función de los filtros? Si por ejemplo, desde un módulo queremos hacer uso de este modelo por una razón determinada, tan solo es establecer desde el módulo el valor que van a tomar los filtros y tendremos una consulta diferente y determinada a la necesidad del módulo. De esta forma, no tenemos que implementar esta misma consulta desde el módulo, repitiendo un proceso innecesario.

Solo nos falta algo para dejar listo nuestro modelo. Si se fijan en la función “populateState” de nuestro modelo, el filtro de orden se establece como “created”. Sin embargo, en la consulta debemos utilizar “a.created”. Para evitarnos problemas, Joomla establece que desde el constructor de la clase, declaremos los filtros válidos a utilizar en ella. Por lo que crearemos una última función en el modelo:

  1. /**
  2.  * Constructor
  3.  *
  4.  * @param array $config An optional associative array of configuration settings.
  5.  *
  6.  */'filter_fields']))
  7. {
  8. $config['filter_fields''content_id', 'a.content_id',
  9. 'created', 'a.created',
  10. 'state', 'a.state'
Source code

Modelo "formlist" del componente - Constructor de la clase

De esta forma, creamos una “lista blanca (white list)” de campos filtrables por el modelo. Vean que incluimos ambas formas de representación de los campos.

Y listo, hemos terminado el modelo que se va a encargar de extraer la lista de comentarios para un determinado artículo. ¿Contentos? ¿Tienen ganas de probar a ver si funciona? ¿O tienes ganas de que salga un error, para así llevarnos las manos a la cabeza?. Bueno, ya veremos qué sale.

Desarrollo del layout para la vista del listado de comentarios

Para poder ver si todo esto funciona como debe, tenemos que crear el layout responsable de la salida de la lista de comentarios. Así que le llegó la hora al plugin nuevamente.

Antes de llegar al layout, tenemos que obtener el array con la lista de comentarios del artículo, para luego mostrarlo. Por lo que tendremos que editar el archivo del plugin responsable de la lógica del mismo, el archivo mjformcomment.php:

  1. /**
  2.  * An array of objects on success, false on failure
  3.  *
  4.  * @var mixed
  5.  */
  6. protected $comments;
Source code


  1. // Get the comment list
Source code


  1. // Name of the variables that will be used in the layout
  2. 'view' => $context,
  3. 'form''formReturn''content_id''comments' => $this->comments
  4. );
Source code


  1. /**
  2.  * Method to set the comment list
  3.  */'Formlist'
Source code

Archivo mjformcomment.php - Obteniendo lista de comentarios para asignársela al layout
Archivo mjformcomment.php - Obteniendo lista de comentarios para asignársela al layout

Como pueden ver, lo primero que hacemos es declarar una variable global para almacenar la lista de comentarios (línea 37). Luego, en la línea 85, hacemos una llamada a la función “setCommentList” definida a partir de la línea 111 hasta la 115, donde su objetivo es llamar al modelo responsable de la creación de la lista de comentario (el que creamos hace un momento, el formlist.php), y asignarle el valor devuelto (un array de objeto en caso satisfactorio, o el valor booleano false en caso contrario), a la variable global. Luego, en la línea 93 creamos un espacio más en el array de datos que usamos en los layout, para incluir esta lista de comentarios.

También noten, que hemos cambiado el comentario que describe el objetivo de la función que obtiene el formulario. No tiene nada que ver con lo que estamos haciendo, pero modificacion al fin, tenemos que reportarla. 

Ya con esto, hemos incluido en el layout la lista de comentarios en caso de que tenga claro. Ahora solo nos toca mostrar esta lista. Pero antes, ya que estamos tan desesperado de ver si estamos realmente obteniendo algo en la variable global “$comments”, hagamos una pequeña inspección.

Escribamos un comentario desde el sitio desde cualquier artículo que tengan publicado. Luego, actualizamos el paquete con esta última modificación que hemos hecho en el plugin.

Abran desde los archivos del sitio, el archivo “mjformcomment.php” del plugin (raíz_sitio\plugins\content\mjformcomment\mjformcomment.php). Dentro, hagamos un “var_dump” a la variable global de la lista de comentarios, para ver si tiene algún valor, de esta forma podemos saber que se está devolviendo desde el modelo. Yo lo coloque antes de la llamada al layout:

Inspeccionando el código en el plugin

Refrescamos la vista del artículo, y tristemente veremos lo siguiente: “bool(false)”. Malas noticias, porque esto significa que algo está fallando, y que posiblemente el modelo no esté devolviendo nada.

Si nos ponemos a revisar un poco las cosa para atrás, es decir, viendo poco a poco, los valores que van tomando las variables en el plugin, luego en el componente, nos encontramos con algo “impresionante”. En el modelo “formlist.php”, en la función “getListQuery” crea la consulta de donde obtenemos la lista de comentarios para un determinado artículo, pero, pueden percatarse, de que nos ha faltado lo que retorna la misma. Creamos satisfactoriamente la consulta, pero, no hicimos mas nada, ya que al no devolver nada, nada se obtiene. Cosas que pasan. Así que al final de la función “getListQuery” pondremos “return $query;”.

Si volvemos a probar nuevamente con este último cambio hecho, se darán cuenta que la variable “$comments” ya contiene algo dentro. Un array de objetos, el cual tienen los campos del formulario con sus valores correspondientes. Además, tiene algunos datos que pedimos en la consulta que creamos en el modelo, referente al artículo.

Con la lógica ya terminada, queda la parte más fácil, la visualización de la lista. Por lo tanto, abrimos el archivo “default.php” dentro de la carpeta “layouts” del plugin añadiendo lo siguiente:

  1. span style="color: #ff0000;">'default_list''data'
Source code

Archivo default.php de los layouts del plugin - Añadiendo el renderizado del layout de la lista de comentarios

Aquí le decimos a la clase “JLayoutHelper” que nos renderice otro layout para el listado de comentarios. Este archivo hay que crearlo y colocarlo dentro de la carpeta “layouts”, teniendo dentro las siguientes líneas:

  1. span style="color: #808080; font-style: italic;">/**
  2.  * @package MJ.Plugin
  3.  * @subpackage Content.Mjformcomment
  4.  *
  5.  * @copyright Copyright (C) 2015 Carlos Rodriguez. All rights reserved.
  6.  * @license License http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
  7.  */'_JEXEC''comments'"row comments-list"'comments'"col-md-12""panel panel-default""list-group""list-group-item""pull-right"'PLG_CONTENT_MJFORMCOMMENT_PUBLISHED_DATE''date''DATE_FORMAT_LC2'"panel-body"
Source code

Archivo "default_list.php" - Layout de la lista de comentarios

De aquí solo recordar el uso de la función “sprintf” de la clase “JText”. También señalar, que la mayoría de las clases CSS aquí presentes pertenecen a bootstrap.

No vayan a probar aún, primero asignemos un valor a las cadenas de texto aquí presentes, para ver un resultado mejor formateado. Para ello, editamos el archivo de idioma del plugin, “es-ES.plg_content_mjformcomment.ini”:

  1. PLG_CONTENT_MJFORMCOMMENT_PUBLISHED_DATE="%s"
Source code

Archivo de idioma del plugin - Añadiendo cadena de texto de la lista de comentarios

Notaran, que no le hemos asignado un valor a la cadena “DATE_FORMAT_LC2” que colocamos anteriormente en el layout de la lista de comentarios. Esto es debido, a que es una cadena de texto preestablecida ya en Joomla, y cómo estos archivos de idioma de Joomla están cargados, pues no hay que reinventar la rueda. Puedes encontrar esta cadena en la línea 269 del archivo “raiz_sitio\language\es-ES\es-ES.ini”.

También hemos reordenado un poco las cadenas de texto en este archivo de idioma, para una mejor organización.

¡Y ahora sí! ya podemos probar nuestro resultado final. Actualizamos el paquete y:

Vista pública del listado de comentarios

¡Epale! hemos dado en el clavo. Nuestro listado de comentarios está funcionado a las mil maravillas. Ordenado descendentemente por la fecha de creación como bien lo establecimos en el modelo. ¿Qué les parece?

Recuerden, no se preocupen ahora por la visualización o la manera en que se muestra la lista, ya llegara ese momento. Garanticemos primero la funcionalidad básica de nuestras extensiones y luego pasamos a los detalles, como es el diseño de todo esto, el cual vendrá acompañado de una plantilla que nosotros mismos crearemos. Ánimo, que todo va muy bien.

Teniendo ya la parte pública funcionando con sus características básicas, creo que por el momento dejaremos a un lado el desarrollo de esta vista aquí. Pasaremos entonces a darle forma a la vista de la administración, en donde gestionaremos los comentarios introducidos. Así que ya queda planteado el objetivo del siguiente tutorial de este ciclo. Nos vemos.

Etiquetas: Desarrollo, Extensiones, Ciclo de desarrollo, Programación

Imprimir