Crear un campo de formulario tipo personalizado

From Joomla! Documentation

This page is a translated version of the page Creating a custom form field type and the translation is 100% complete.

Other languages:
English • ‎español • ‎français • ‎Nederlands
Joomla! 
3.x
series
Copyedit.png
This Article Needs Your Help

This article is tagged because it NEEDS REVIEW. You can help the Joomla! Documentation Wiki by contributing to it.
More pages that need help similar to this one are here. NOTE-If you feel the need is satistified, please remove this notice.

JForm, una característica introducida en Joomla! 2.5, permite crear fácilmente formularios HTML (<form>). Los formularios creados con JForm consisten en campos de formulario, implementados como JFormField. Hay un JFormField para cada tipo de campo que se puede encontrar en un formulario, como un campo tipo texto y un campo tipo fecha. JForm soporta una gran selección campos tipo estándar. Para una lista completa, ver Campos de formulario, Tipos Estándar.

Joomla! 2.5 hace posible extender el campo de formulario tipo estándar o definir el tuyo propio. Por ejemplo, si el componente gestiona las entradas de la libreta de teléfonos, es posible que desees definir un campo de formulario tipo que genere una lista de selección de ciudades. Hay varias ventajas en la definición de un campo de formulario tipo personalizado:

  • Serás capaz de mezclar campo tipo estándar con el campo tipo personalizado en un JForm.
  • Llegarás a tener un paquete de código reutilizable que puede ser utilizado fácilmente en todo tu código.
  • Las extensiones que colaboran con tu extensión serán capaz de crear campos de formulario sin entrometerse con tus tablas de base de datos y otros componentes internos.

Campo de formulario tipo, requisitos de clase

Un campo de formulario tipo se define en clase, que debe ser (no necesariamente directa) subclase de JFormField. Para que funcione correctamente, la clase debe definir al menos tres métodos:

  • public function getLabel()
    Se llama a esta función para crear la etiqueta que pertenece a tu campo y debe devolver una cadena HTML que la contiene. Desde JFormField se define un lista-para-usar la implementación getLabel(), los campos de formularios de tipo personalizados no suelen definir sus propias getLabel(). Si quiere hacerlo, el método heredado de creación de etiquetas será utilizado. Se recomienda dejar el método getLabel() para consistencia y velocidad a menos que realmente desees modificar la etiqueta HTML.
  • public function getInput()
    Se llama a esta función para crear el campo en sí mismo y debe devolver una cadena HTML que lo contiene. Este es también el lugar donde habitualmente ocurre la mayoría de la transformación. En el campo Ciudad de nuestra libreta de teléfonos de ejemplo, esta función tendrá que recuperar una lista de las ciudades y devolver un <select> HTML con las ciudades insertadas como una lista de <option>.
  • public function getValue()
    Se llamará a esta función para obtener el valor del campo. El valor se obtiene de la función LoadFormData en el Modelo

Dentro de tu código, tendrás que procesar el conjunto de atributos para el campo del usuario en el archivo XML de definición del formulario. Algunos de estos atributos son accesibles a través de miembros protegidos de variables de JFormField. Por ejemplo, el atributo name está disponible en el código como $this->name. Del mismo modo, label, description, default, multiple y class están también disponibles como propiedades de $this. Otros parámetros que hayas definido se puede acceder a través del arrary $this->element: el atributo size estará en $this->element['size'].

¿Cuál clase a subclase?

Para que un campo de formulario tipo sea utilizable en JForm, debe ser una subclase de JFormField. Sin embargo, no tiene que ser una hija directa esa clase: también puede una subclase de otro campo de formulario tipo existente (estándar o personalizada) y por tanto heredar código útil.

Si el campo de formulario tipo es bastante similar a la de un tipo existente, la subclase de heredar de ese tipo. Especialmente si el tipo de campo de formulario es una lista, por favor vea la subclase JFormFieldList. Sólo tienes que reemplazar el método getOptions() para devolver las opciones a ser mostradas; el método getInput() va a convertir estas opciones a HTML.

Una subclase de un tipo existente, por ejemplo JFormFieldList, se la carga mediante la adición de lo siguientes después de jimport('joomla.form.formfield');:

jimport('joomla.form.helper');
JFormHelper::loadFieldClass('list');

Si el campo de formulario tipo es diferencia de cualquier tipo existente, cargar directamente la subclase JFormField.

Ubicación de los archivos

  • Los archivos de los campos de formulario tipo estándar se encuentra en libraries/joomla/form/fields/. No se deben almacenar los campos personalizados aquí, ni debes usar esta ruta en tu propio código, pero los tipos estándar generalmente son buenos ejemplos.
  • Los campos de tipo personalizado que pertenecen a tus componentes están situados generalmente en administrator/components/<nombre de tu componente>/models/fields. Puedes especificar este u otra ruta en tu código:
JForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
  • Los archivos XML que definen los formularios están generalmente ubicados en administrator/components/<nombre de tu componente>/models/forms. Usar algo como el siguiente fragmento de código para especificar una ruta de acceso a los formularios:
JForm::addFormPath(JPATH_COMPONENT . '/models/forms');

Convenciones de nombres y esqueleto

En esta sección, <ComponentName> representa el nombre del componente en grafía MayúsculasMinúsculas y <FieldName> representa el nombre del campo de formulario tipo en grafía MayúsculasMinúsculas. La clase del campo debe ser colocada en administrator/components/<nombre de tu componente>/models/fields/<nombre de tu campo>.php, y se debe ver como algo así:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

jimport('joomla.form.formfield');

// The class name must always be the same as the filename (in camel case)
class JFormField<FieldName> extends JFormField {

	//The field class must know its own type through the variable $type.
	protected $type = '<FieldName>';

	public function getLabel() {
		// code that returns HTML that will be shown as the label
	}

	public function getInput() {
		// code that returns HTML that will be shown as the form field
	}
}

Agrupar campos tipo personalizados

Advertencia: esta información es parcialmente incorrecta y debe ser mejorada.

Los campos tipo personalizados se pueden agrupar mediante el uso de un guión bajo en el nombre de campo. Un clase de campo con un nombre como por ejemplo "JFormFieldMy_randomField" debe ser almacenado en administrator/components/<nombre de tu componente>/models/fields/my/randomField.php. Podemos prefijar nuestros nombres de los campo de formulario con algunos nombre del grupo, a continuación, ponemos un carácter de subrayado y, a continuación, un nombre de un campo.

Un ejemplo de campo tipo personalizado

Supongamos que estás trabajando en su componente denominado com_phonebook, y deseas definir un campo que contenga las ciudades. Creamos el archivo administrator/components/com_phonebook/models/fields/city.php y escribimos algo similar a lo siguiente:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

jimport('joomla.form.formfield');

class JFormFieldCity extends JFormField {
	
	protected $type = 'City';

	// getLabel() left out

	public function getInput() {
		return '<select id="'.$this->id.'" name="'.$this->name.'">'.
		       '<option value="1" >New York</option>'.
		       '<option value="2" >Chicago</option>'.
		       '<option value="3" >San Francisco</option>'.
		       '</select>';
	}
}

Una forma más avanzada de enfoque es la extensión de la clase JFormFieldList. Supongamos que deseas crear una lista desplegable de ciudades de forma dinámica a partir de la base de datos basado en una condición dinámica, entonces puedes hacer esto de la siguiente manera:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

JFormHelper::loadFieldClass('list');

class JFormFieldCity extends JFormFieldList {

	protected $type = 'City';

	public function getOptions() {
                $app = JFactory::getApplication();
                $country = $app->input->get('country'); //country is the dynamic value which is being used in the view
                $db = JFactory::getDbo();
                $query = $db->getQuery(true);
                $query->select('a.cityname')->from('`#__tablename` AS a')->where('a.country = "'.$country.'" ');
		$rows = $db->setQuery($query)->loadObjectlist();
                foreach($rows as $row){
                    $cities[] = $row->cityname;
                }
                // Merge any additional options in the XML definition.
		$options = array_merge(parent::getOptions(), $cities);
                return $options;
	}
}

El ejemplo anterior muestra una consulta simple que mostrará una lista de ciudades desde una tabla donde el nombre de la ciudad pertenece a su país, respectivamente. Puedes crear una lista desplegable basado en consultas más complejas.

Escollos

Cargar un campo personalizado puede provocar un error fatal, si existe un campo en el código base con el mismo nombre de archivo y el campo personalizado amplía el campo del código base.

Considere un archivo testfields/radio.php que contenga

<?php

class TestFormFieldRadio extends JFormFieldRadio {}

Llamar a JFormHelper::loadFieldClass('radio') producirá un error fatal: no se encuentra la clase 'JFormFieldRadio'.

Hay dos razones para esto.

  1. JLoader no puede cargar automáticamente <código>JFormFieldRadio, porque el nombre de clase (<código>JFormField*) no coincide con el nombre de ruta (joomla/form/fields/* - observe el plural en fields).
  2. JFormHelper no puede cargar <código>JFormFieldRadio, porque las rutas personalizadas se escanean primero, y el tipo de campo solicitado ('radio') se resuelve antes de que se alcancen las clases principales.

Solución

Requiera el archivo de campo del código base directamente:

<?php
require_once JPATH_LIBRARIES . '/joomla/form/fields/radio.php';

class TestFormFieldRadio extends JFormFieldRadio {}

y use JFormHelper::loadFieldClass correctamente con 'test.radio' en lugar de 'radio'.

Utilizar el campo tipo personalizado

Enlazado con un formulario

Para utilizar campo tipo city, necesitamos actualizar el archivo XML que contiene los campos de formulario. Abre tu archivo XML que se encuentra en administrator/components/com_phonebook/models/forms y agregar el campo en la forma habitual:

<field name="title" type="City" label="JGLOBAL_TITLE"
	description="JFIELD_TITLE_DESC"
	required="true" />

El atributo name es sensible a mayúsculas/minúsculas.

Además, puedes necesitar agregar al campo la ruta de acceso al <fieldset> padre:

<fieldset addfieldpath="/administrator/components/<component name>/models/fields">

No enlazado con un formulario

Por ej.ː cuando necesitas el campo como una lista desplegable en un componente como admin/filtro del sitio.

//Get custom field
JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields');
$cities = JFormHelper::loadFieldType('City', false);
$cityOptions=$cities->getOptions(); // works only if you set your field getOptions on public!!

Reemplazar getLabel()

Como se mencionó en la sección Campo de formulario tipo, requisitos de clase, los campos de formulario tipos personalizados no suelen definir sus propios getLabel(). Si deseas crear una etiqueta personalizada, todavía puedes hacer uso del getLabel() que cada campo tipo hereda de la clase JFormField, por ejemplo, definiéndolo de la siguiente manera:

public function getLabel() {
     return '<span style="text-decoration: underline;">' . parent::getLabel() . '</span>';
}

Este código subrayará las etiquetas del formulario. (Ten en cuenta que si tu objetivo es subrayar etiquetas del formulario, usando CSS es la manera preferida.)

Si quieres hacer algo completamente diferente, por supuesto, también puede reemplazarlo completamente:

public function getLabel() {
	// Initialize variables.
	$label = '';
	$replace = '';

	// Get the label text from the XML element, defaulting to the element name.
	$text = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name'];

	// Build the class for the label.
	$class = !empty($this->description) ? 'hasTip' : '';
	$class = $this->required == true ? $class.' required' : $class;
		
	// Add replace checkbox
	$replace = '<input type="checkbox" name="update['.$this->name.']" value="1" />';
		
	// Add the opening label tag and main attributes attributes.
	$label .= '<label id="'.$this->id.'-lbl" for="'.$this->id.'" class="'.$class.'"';

	// If a description is specified, use it to build a tooltip.
	if (!empty($this->description)) {
		$label .= ' title="'.htmlspecialchars(trim(JText::_($text), ':').'::' .
				JText::_($this->description), ENT_COMPAT, 'UTF-8').'"';
	}

	// Add the label text and closing tag.
	$label .= '>'.$replace.JText::_($text).'</label>';
	
	return $label; 
}

En este ejemplo se va a agregar una casilla de verificación dentro de la etiqueta.