1.- Reducción de archivos

Siguiendo las convenciones con FOF no hace falta, controladores, modelos y tablas. Además las vistas de backend se pueden simplificar a un sólo archivo XML. Por supuesto en caso de necesitar algo más complejo o personalizado siempre puedes crear los archivos necesarios.

Esta es la diferencia de organización / flujo de clases:

Joomla!

coreModel > myComponentModel

Joomla! + FOF

coreModel > FOFModel > myComponentModel

Las classes padres de FOF funcionan de tal forma que permiten gestionar las peticiones de forma estándar. Es decir, si yo no defino un modelo el modelo padre intentará darme servicio. Sin embargo sin FOF los modelos del core son incapaces de funcionar de forma autónoma requiriendo siempre alguna configuración.

2-. Acceso rápido a la información a través de los modelos

Con Joomla! es posible utilizar los modelos de una manera bastante rápida para obtener información de la base de datos. Sin embargo con FOF esto se simplifica mucho más.

Joomla!

// Include the path to the models folder
JModelLegacy::addIncludePath(JPATH_SITE.'/components/com_hangouts/models', 'HangoutsModel');

// Create the model instance
$sessionsModel = JModelLegacy::getInstance('Sessions', 'HangoutsModel', array('ignore_request' => true));

// Set the filters
$sessionsModel->setState('filter.published', 1);
$sessionsModel->setState('category_id', $categoryId);

// Get the filter items
$sessions = $sessionsModel->getItems();

Joomla! + FOF

$sessions = FOFModel::getTmpInstance('Sessions','HangoutsModel')->published(1)->getList(true);

3.- Gestión de Toolbar

FOF automáticamente gestiona la mayoría de enlaces de nuestra toolbar por nosotros según el tipo de vista en la que estemos browse, read, write,

Joomla!

helper.php

/**
     * Configure the Linkbar.
     *
     * @param   string  $vName  The name of the active view.
     *
     * @return  void
     *
     * @since    1.6
     */
    public static function addSubmenu($vName)
    {
        JSubMenuHelper::addEntry(
            JText::_('COM_HANGOUTS_DASHBOARD'),
            'index.php?option=com_hangouts&view=dashboard',
            $vName == 'dashboard'
        );
        JSubMenuHelper::addEntry(
            JText::_('COM_HANGOUTS_SESSIONS'),
            'index.php?option=com_hangouts&view=sessions',
            $vName == 'sessions'
        );
        JSubMenuHelper::addEntry(
            JText::_('COM_HANGOUTS_CATEGORIES'),
            'index.php?option=com_categories&view=categories&extension=com_hangouts.sessions',
            $vName == 'categories.sessions'
        );
    }

view.html.php

/**
     * Add the page title and toolbar.
     *
     * @since    2.5
     *
     * @return void
     */
    protected function addToolbar()
    {
        require_once JPATH_COMPONENT_ADMINISTRATOR . '/helpers/hangouts.php';

        $canDo = CursosHelper::getActions($this->state->get('filter.category_id'));
        $user    = JFactory::getUser();

        if ($user->authorise('core.admin', 'com_hangouts.dashboard'))
        {
            // Page title
            JToolBarHelper::title(JText::_('COM_HANGOUTS_SESSIONS'), 'article.png');

            // Back button
            JToolBarHelper::custom('sessions.topanel', 'back.png', 'back_f2.png', 'COM_HANGOUTS_SESSIONS_DASHBOARD_TITLE', false);
            JToolBarHelper::divider();

            // Add / edit
            if ($canDo->get('core.create') || (count($user->getAuthorisedCategories('com_hangouts', 'core.create'))) > 0)
            {
                JToolBarHelper::addNew('session.add', 'JTOOLBAR_NEW');
            }
            if (($canDo->get('core.edit')))
            {
                JToolBarHelper::editList('session.edit', 'JTOOLBAR_EDIT');
            }

            // Publish / Unpublish
            if ($canDo->get('core.edit.state'))
            {
                JToolBarHelper::divider();
                JToolBarHelper::custom('sessions.publish', 'publish.png', 'publish_f2.png', 'JTOOLBAR_PUBLISH', true);
                JToolBarHelper::custom('sessions.unpublish', 'unpublish.png', 'unpublish_f2.png', 'JTOOLBAR_UNPUBLISH', true);
            }

            // Delete / Trash
            if ($this->state->get('filter.state') == -2 && $canDo->get('core.delete'))
            {
                JToolBarHelper::divider();
                JToolBarHelper::deleteList('', 'sessions.delete', 'JTOOLBAR_EMPTY_TRASH');
                JToolBarHelper::divider();
            }
            elseif ($canDo->get('core.edit.state'))
            {
                JToolBarHelper::divider();
                JToolBarHelper::trash('sessions.trash', 'JTOOLBAR_TRASH');
                JToolBarHelper::divider();
            }

            // Preferences
            if ($canDo->get('core.admin'))
            {
                JToolBarHelper::preferences('com_hangouts');
                JToolBarHelper::divider();
            }
        }
    }

Joomla! + FOF

Nada :)

Además FOF permite extender las acciones según la vista activa. Por ejemplo si quisiéramos generar una toolbar con ajustes personalizados para la vista sessions podríamos generar algo como:

toolbar.php

/**
 * @package     Hangouts.Backend
 * @subpackage  Toolbar
 *
 * @copyright   Copyright (C) 2014 Roberto Segura. All rights reserved.
 * @license     GNU General Public License version 2 or later, see LICENSE.
 */

defined('_JEXEC') or die;

/**
 * Hangouts backend toolbars
 *
 * @package     Hangouts.Backend
 * @subpackage  Toolbar
 *
 * @since       1.0
 */
class HangoutsToolbar extends FOFToolbar
{

    /**
     * Toolbar customisations for sessions list view
     *
     * @return  void
     */
    public function onSessionsBrowse()
    {
        $this->onBrowse();

        JToolBarHelper::divider();
        JToolBarHelper::custom('copy', 'copy.png', 'copy_f2.png', 'JLIB_HTML_BATCH_COPY', false);
    }
}

Es decir, en lugar de tener que especificar en cada vista todos los de la toolbar + personalizaciones tenemos unos botones predefinidos para onBrowse() a los que podemos agregar los botones que queramos. Siguiendo el concepto de DRY estamos ahorrando ¿30 líneas por vista? No está mal teniendo en cuenta que el funcionamiento estándar es suficiente para el 90% de nuestras vistas

4.- Dispatcher + HMVC

El dispatcher es el controlador principal de nuestro componente. El "cerebro" que decide a qué controlador hay que llamar y qué tarea ejecutar. Esto en sí podríamos decir que no cambia la forma de funcionar puesto que en Joomla por defecto también tenemos un controlador principal. Pero el dispatcher de FOF es el punto de entrada a cosas tan interesantes como el HMVC.

Joomla!

hangouts.php > controller.php > controllers/sessions.php

Joomla! + FOF

hangouts.php > dispatcher.php > (opcional) controllers/sessions

Como vemos nos podemos ahorrar en este caso también los controlladores puesto que el dispatcher se encargará de darnos un servicio base. Pero además dentro del dispatcher de FOF es donde sucede la magia del HMVC. "Falseando" los datos del input (inicialmente concebidos para procesar la $_REQUEST) podemos conseguir lanzar un MVC dentro de otro MVC con algo tan simple como:

 

$ids = $params->get('ids',array());

$config = array(
    'option'    => 'com_hangouts',
    'view'        => 'sessions',
    'input'        => array(
        'savestate'    => 0,
        'limit'        => 0,
        'limitstart'=> 0,
        'no_clear'    => true,
        'only_once'    => true,
        'task'        => 'browse',
        'filter_order' => 'ordering',
        'filter_order_Dir' => 'ASC',
        'enabled'    => 1,
        'caching'    => false
    )
);

if(!empty($ids))
{
    $config['input']['id'] = $ids;
}

FOFDispatcher::getTmpInstance('com_hangouts', 'sessions', $config)->dispatch();

En este caso lo hemos usado para a través de los parámetros de un módulo cargar una lista filtrada de hangouts. Es decir que en el modelo nos hemos ahorrado:

  • El helper que se encarga de cargar toda la información de la base de datos y filtrarla según los parámetros del módulo
  • El template que se encarga de renderizar los items puesto que la vista ya viene renderizada.

En caso de que quisiéramos renderizar con nuestro propio template para aplicar un estilo personalizado a nuestro módulo podemos usar otra de las ventajas de FOF: las vistas automáticas en JSON y CSV agregando al input 'format' => 'json'

5.- Gestión automática de columnas clave

En FOF existe también una convención para las columnas de las tablas. Simplemente creando ciertas columnas FOF es capaz de manejarlas y empezar a gestionarlas por nosotros. Estas columnas son:

  • hits
  • access
  • ordering
  • enabled
  • language
  • asset_id
  • title
  • slug
  • created_on
  • created_by
  • modified_on
  • modified_by
  • locked_on
  • locked_by

 Es fácil imaginar lo que significa cada uno de los campos. Descataremos que el campo ordering permite automáticamente el drag&drop para ordenar registros en vistas de tipo browse en el backend (lo cual en un componente por defecto requiere 2 -3  horas para ponerlo en marcha). Además los campos locked_by & locked_on permiten el bloqueo de registros para proteger la integridad de los datos y que dos usuarios no puedan editarlos a la vez. Los campos slug son generados automáticamente a partir de los campos title en caso de existir ambos.