Creación de componentes para Joomla (II)

Si en el artículo anterior vimos cómo desarrollar la interfaz pública de un componente para Joomla, en esta ocasión veremos cómo crear la interfaz de administración, extendiendo nuestro pequeño componente de ejemplo, que mostraba citas aleatorias de entre las introducidas por los usuarios, y añadiendo opciones para editar y eliminar las citas.

Lo primero que tendremos que hacer es editar el archivo XML que contiene la información del componente. Recordaréis que la sección referente a la interfaz de administración (administration) estaba vacía en el ejemplo anterior.

Necesitamos añadir una sección files, en la que indicar los archivos que compondrán la parte de administración, y una etiqueta menu, especificando el nombre a mostrar en el menú de Componentes y la página que se cargará al hacer clic sobre él. Algo a tener en cuenta es que file tiene un argumento folder indicando que todos los archivos a los que hacemos referencia se encuentran en una nueva carpeta admin dentro del directorio de nuestro componente.

<?xml version="1.0" encoding="utf-8"?>  
<install type="component" version="1.5">
  <name>Ejemplo</name>
  <author>Raul Gonzalez</author>
  <creationDate>Febrero 2010</creationDate>
  <copyright>(C) Raul Gonzalez</copyright>  
  <license>GPL</license>
  <authorUrl>http://mundogeek.net/</authorUrl>
  <authorEmail>zootropo en gmail</authorEmail>
  <version>0.1</version>
  <description>Componente de ejemplo que muestra mensajes aleatorios escritos por los lectores</description>
  <install>
    <queries>
      <query>CREATE TABLE IF NOT EXISTS jos_mensajes(
          id int NOT NULL auto_increment,
          mensaje varchar(300) NOT NULL, PRIMARY KEY(id));</query>
      <query>INSERT INTO jos_mensajes(mensaje) VALUES
          ('At your command'),
          ('Hay 10 tipos de personas en el mundo: los que entienden binario y los que no'),
          ('Es mio, solo mio. Miii.... tessssssooooroo'),
          ('Hasta luego, y gracias por el pescado');</query>
    </queries>
  </install>
  <files>
    <file>ejemplo.php</file>
    <file>ejemplo.xml</file>
    <folder>controllers</folder>
    <folder>models</folder>
    <folder>views</folder>
  </files>
  <administration>
    <menu link="option=com_ejemplo">Ejemplo</menu>
    <files folder="admin">
      <file>ejemplo.php</file>
      <file>controller.php</file>
      <folder>views</folder>
    </files>
  </administration>
</install>

En el tutorial anterior ya comentamos que el punto de entrada al componente, tanto en el caso de la interfaz pública como el de la administración, es un archivo PHP con el nombre del componente, y que este, normalmente, no hará más que cargar el controlador adecuado. En este caso sólo vamos a utilizar un controlador, por lo que el código es mucho más sencillo. Creemos un nuevo archivo ejemplo.php dentro de nuestra nueva carpeta admin, con el siguiente contenido:

<?php
defined('_JEXEC') or die('Restricted access');

require_once('controller.php');

$controller = new EjemploController();

$controller->execute($task);
$controller->redirect();

Como vemos lo único que hace es cargar el código de nuestro único controlador, instanciar la clase, y ejecutar el método adecuado en el objeto dependiendo del parámetro task de la petición.

Creemos ahora el archivo controller.php en la carpeta admin:

<?php
defined('_JEXEC') or die('Restricted access');
 
jimport('joomla.application.component.controller');

class EjemploController extends JController {
  function __construct() {
    parent::__construct();
    $this->addModelPath(JPATH_COMPONENT_SITE.DS.'models');
  }

  function display() {
    $vista = $this->getView('ejemplo', 'html');
    $modelo = $this->getModel('mensaje', 'EjemploModel');
    $vista->setModel($modelo, true);

    parent::display();
  }
  
  function edit() {
    $vista = $this->getView('ejemplo', 'html');
    $modelo = $this->getModel('mensaje', 'EjemploModel');
    $vista->setModel($modelo, true);

    $vista->edit();
  }

  function save() {
    $modelo = $this->getModel('mensaje', 'EjemploModel');
    $id = JRequest::getVar('id');
    $mensaje = JRequest::getVar('mensaje');

    $correcto = $modelo->actualizarMensaje($id, $mensaje);

    if($correcto)
      $this->setRedirect('index.php?option=com_ejemplo', 'Mensaje actualizado con éxito');  
    else
      $this->setRedirect('index.php?option=com_ejemplo', 'Ocurrió un error al actualizar el mensaje', 'error');  
  }

  function remove() {
    $modelo = $this->getModel('mensaje', 'EjemploModel');
    $cid = JRequest::getVar('cid', 0, '', 'array');
    $correcto = true;
    foreach($cid as $id) {
      $resultado = $modelo->eliminarMensaje($id);
      if(!$resultado)
        $correcto = false;
    }

    if($correcto)
      $this->setRedirect('index.php?option=com_ejemplo', 'Mensajes eliminados con éxito');
    else
      $this->setRedirect('index.php?option=com_ejemplo', 'Ocurrió un error al eliminar los mensajes', 'error');  
  }
}

En esta ocasión sobreescribimos el constructor del controlador para añadir una llamada al método addModelPath. Hacemos uso de este método para que el controlador tenga en cuenta también la carpeta en la que almacenamos el modelo en la interfaz pública. Editaremos ese archivo para añadir los nuevos métodos que necesitemos, en lugar de crear otro archivo de modelo. También podríamos haber almacenado el archivo en la carpeta de la parte de administración y hacer uso del método addModelPath en el controlador de la parte pública.

Si quisiéramos hacer uso de una vista de la parte pública, aunque es algo menos común, podríamos haber hecho uso del método addViewPath.

El resto del código no tiene mayor misterio.

Sólo nos queda crear la vista y las plantillas que vamos a utilizar. Creamos una carpeta views, dentro una carpeta ejemplo, y en ella un archivo view.html.php.

<?php
defined('_JEXEC') or die('Restricted access');

jimport('joomla.application.component.view');

class EjemploViewEjemplo extends JView {
  function display($tpl=null) {
    JToolBarHelper::title('Mensajes');
    JToolBarHelper::deleteList();
    JToolBarHelper::editList();

    $mensajes =& $this->get('Mensajes');
    $this->assignRef('mensajes', $mensajes);

    parent::display($tpl);
  }
  
  function edit() {
    JToolBarHelper::title('Editar mensaje');
    JToolBarHelper::save();
    JToolBarHelper::cancel();

    $modelo = $this->getModel();
    $cid = JRequest::getVar('cid', 0, '', 'array');

    $mensaje = $modelo->getMensajePorId($cid[0]);
    $this->assignRef('mensaje', $mensaje);

    parent::display('editar');
  }
}

En esta ocasión para generar nuestra vista hacemos uso de la clase JToolBarHelper, que contiene métodos para facilitarnos la creación de la barra de herramientas de nuestro componente. Tiene métodos para añadir títulos (title), divisores (divider), espaciadores (spacer), … botones de ayuda (help), para guardar elementos (save), borrar (trash), publicar (publish), … Este es el motivo de que haya utilizado palabras inglesas para nombrar los métodos del controlador: al pulsar el botón generado por editList, por ejemplo, se intentará ejecutar por defecto una tarea edit.

Este es el aspecto que tendrá ahora nuestro modelo, una vez editado el archivo correspondiente en la carpeta de la interfaz pública:

<?php
defined('_JEXEC') or die('Restricted access');
jimport('joomla.application.component.model');

class EjemploModelMensaje extends JModel {
  function getMensaje() {
    $db =& JFactory::getDBO();
    $query = 'SELECT mensaje FROM #__mensajes ORDER BY RAND() LIMIT 1';
    $db->setQuery($query);
    return $db->loadResult();
  }

  function getMensajePorId($id) {
    $db =& JFactory::getDBO();
    $query = "SELECT id, mensaje FROM #__mensajes WHERE id='{$id}'";
    $db->setQuery($query);
    return $db->loadObject();
  }

  function eliminarMensaje($id) {
    $db =& JFactory::getDBO();
    $query = "DELETE FROM #__mensajes WHERE id='{$id}'";
    $db->setQuery($query);
    $db->query();

    if ($db->getErrorNum())
      return false;

    return true;
  }

  function actualizarMensaje($id, $mensaje) {
    $db =& JFactory::getDBO();
    $query = "UPDATE #__mensajes SET mensaje='{$mensaje}' WHERE id='{$id}'";
    $db->setQuery($query);
    $db->query();

    if ($db->getErrorNum())
      return false;

    return true;
  }

  function getMensajes() {
    $query = 'SELECT id, mensaje FROM #__mensajes ORDER BY id ASC';
    return $this->_getList($query);
  }

  function anyadirMensaje($mensaje) {
    $db =& JFactory::getDBO();
    $query = "INSERT INTO #__mensajes(mensaje) VALUES('{$mensaje}')";
    $db->setQuery($query);
    $db->query();

    if ($db->getErrorNum())
      return false;

    return true;
  }
}

Lo único destacable es el uso del método _getList, que toma una consulta SQL y devuelve una lista con los resultados.

Vamos por último con las plantillas. Creamos la carpeta tmpl en views/ejemplo y en esta un archivo default.php, que será la plantilla que mostrará la lista de los mensajes almacenados en la base de datos.

<?php defined('_JEXEC') or die('Restricted access'); ?>

<form action="index.php" method="post" name="adminForm">
  <table class="adminlist">
  <thead>
    <tr>
      <th width="5">ID</th>
      <th width="20"><input type="checkbox" name="toggle" value="" onclick="checkAll(<?php echo count($this->mensajes); ?>);"/></th>     
      <th>Mensaje</th>
    </tr>
  </thead>
  <?php
  $i = 0;
  foreach($this->mensajes as $mensaje) {
  ?>
    <tr>
      <td><?php echo $mensaje->id; ?></td>
      <td><?php echo JHTML::_('grid.id', $i, $mensaje->id); ?></td>
      <td><?php echo "<a href='index.php?option=com_ejemplo&task=edit&cid[]={$mensaje->id}'>{$mensaje->mensaje}</a>"; ?></td>
    </tr>
    <?php
    $i++;
  }
  ?>
  </table>

  <input type="hidden" name="option" value="com_ejemplo" />
  <input type="hidden" name="task" value="" />
  <input type="hidden" name="boxchecked" value="0" />
</form>

Aquí hay que tener en cuenta varias convenciones. Por ejemplo, el formulario tiene que tener como valor para el atributo name «adminForm«, para que funcione el script que marca todos los checkboxes al marcar el primero, y la tabla tiene que tener como valor para el atributo class «adminlist«, si queremos que esta tenga el aspecto definido en la hoja de estilo.

A parte de eso lo único interesante es el uso del método JHTML::_(), que se utiliza para generar código HTML de uso común. Toma como primer parámetro una cadena con formato «clase.metodo», lo que indica que queremos ejecutar el método indicando de la clase JHTMLClase. Si no se especifica ninguna clase, se llamará al metodo con ese nombre de la clase JHTML, si existe. Cualquier otro parámetro extra que se le pase a JHTML::_() se pasará como parámetro al método que queremos ejecutar.

Estas clases que agrupan código HTML de uso común son JHTMLBehavior, JHTMLContent, JHTMLEmail, JHTMLForm, JHTMLGrid, JHTMLImage, JHTMLList, JHTMLMenu y JHTMLSelect.

El método id de JHtmlGrid, concretamente, genera un widget de tipo checkbox que sigue las convenciones necesarias para que se utilice para seleccionar un elemento por su identificador.

Por último veamos la plantilla que se encargará de crear el formulario con el que editar el mensaje, default_editar.php. Este formulario es incluso más sencillo, y no requiere de mayores explicaciones.

<?php defined('_JEXEC') or die('Restricted access'); ?>
 
<form action="index.php" method="post" name="adminForm" id="adminForm">
  <table class="admintable">
    <tr>
      <td width="100" align="right" class="key"><label for="mensaje">Mensaje:</label></td>
      <td><input type="text" name="mensaje" id="mensaje" maxlength="300" value="<?php echo $this->mensaje->mensaje;?>" /></td>
    </tr>
  </table>
 
  <input type="hidden" name="option" value="com_ejemplo" />
  <input type="hidden" name="id" value="<?php echo $this->mensaje->id; ?>" />
  <input type="hidden" name="task" value="" />
</form>

21 comentarios en «Creación de componentes para Joomla (II)»

  1. Tío, me vuelvo a quita el sombrero, te lo has currado mucho.

    Muy bien explicado todo.

    Por cierto, que opinas de Joomla como plataforma de desarrollo?? y como CMS?? Qué te ha parecido su API?

    Un saludo y gracias por la documentación.

  2. Hola, he seguido los pasos, y me da el siguiente error en la parte de administración al seleccionar Componentes/Ejemplo:

    500 – Se ha producido un error

    View not found [name, type, prefix]: ejemplo,html,ejemploView

    Saludos y muchas gracias

    1. Pues, como dice el texto del error, eso es que no encuentra la vista. Comprueba que esté en la carpeta views/ejemplo (views/name), que el archivo se llame view.html.php (view.type.php) y que la clase se llame EjemploViewEjemplo (prefixname).

  3. Otra pregunta: ¿Cómo puede Joomla comprobar qué módulos debe mostrar cuando se ejecuta nuestro componente en la parte pública?.

    Ahora mismo me muestra menús que no tengo ni publicados.

    Gracias de nuevo

    1. En principio sólo tendrías que ir al gestor de módulos, seleccionar el módulo que quieras modificar y en la sección de menús elegir las páginas para las que quieres que se muestre el módulo.

      Que te muestre un módulo que no tienes ni publicado es raro, raro, raro. Puede que sea alguna cosa extraña de la caché de Joomla.

  4. Hola Zootropo,

    Espero no incomodar a nadie con la pregunta.

    Conoces drupal?

    que te parece drupal frente a Joomla como CMS y como plataforma de desarrollo?

    Muy buen blog

    1. Sí, lo conozco, Francisco. Y como framework me parece mejor que Joomla. Como CMS, creo que deja mucho que desear en usabilidad, aunque tengo entendido que es uno de los puntos en los que más están trabajando para la próxima versión 🙂

  5. Hola Zootropo, muy bueno el tutorial!
    Estuve creando uno, cambiando los nombres del componente y de la entidad, y en el momento de actualizar y borrar, lo hace de hecho. Pero me desloguea cuando pasa por el $this->setRedirect de los metodos en el controller de la parte de admin.
    Tendras idea de porque pasa esto?
    Gracias!

    1. A mi me pasa lo mismo que a Nico, me pide el login cada vez que hago una acción, y cuando borro si me lo hace, pero el editar no me hace nada

  6. Muy útil el tutorial y también la cola de comentarios donde se resuelven algunas dudas y problemas.

    Para los que se plantean drupal Vs. joomla, sin duda joomla le lleva mucha ventaja, drupal tiene una estructura muy interesante para los que somos desarrolladores pero deja bastante que desear en comparación con el renombre que ha cogido.

    Saludos.

  7. una nueva pregunta:

    ¿como seria un controlador con más de 1 vista? digamos 1 controlador con 2 vistas como para hacerlo «facil»

  8. A esto llamán arquitectura ??? es penoso el tiempo que lleva joomla y lo pésima que es su arquitectura. Por no hablar del tratamiento de taxonomías y tesauros, los grupos de usuarios o los flujos de trabajo. Sin mencionar el API, que es de lo peor.

  9. Es posible que alguien me ayude a confeccionar un componente de multinivel que realmente se haga a la medida, ya que he adquirido varios de estos componentes y todos apuntan a productos, como hacer click, y ganar puntos,,, pero requiero un componente mas ala medida de lo real, de las compras fisicas. Podriamos inicar un grupo de desarrollo para este componente? tengo la idea, pero me quedo mucho en la parte de la programacion, aun cuando me estoy esmerando por aprender. O si hay alguien que puerda decirme, donde encuentro mas informacion en español, donde me hablenm de la API de jooomla y todas sus JXXXXXX? disculpenme si no es aqui donde debo publicar esto.

  10. Zootropo te felicito por este articulo, sinceramente puedo decirte que he leído éste y los referentes a los componentes y al modulo, puedo concluir que es lo mejor que he visto en la web sobre este tema en español. Felicidades Nuevamente por este excelente trabajo

  11. Hola Zootropo, primero felicitarte, magnífico tutorial, bien detallado y lo puede entender me parece hasta el que no sabe ni J de Joomla
    Pero tengo una pregunta, a ver si me pueden ayudar en esto tu o alguno de los lectores
    Como puedo hacer para para mostrar/ocultar por ejemplo un textbox en el backend, para cuando el admin esta creando un elemento, por ejemplo un checkbox q ponga «casado?» y si se marca aprarezca un textbox con el label inserte nombre del conyugue, es un ejemplo un poco flojo, pero es lo único q me vino a la cabeza ahora
    Felicidades again por este magnífico articulo

Responder a Javier Cancelar respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.