Formulario único conjunto de validación

From Joomla! Documentation

This page is a translated version of the page Single form validation set and the translation is 100% complete.
Other languages:
English • ‎Nederlands • ‎español • ‎français
Joomla! 
≥ 3.0
Tutorial
Formulario único conjunto de validación

Esta página explica cómo se utilizan las mismas reglas de validación de formularios del Lado Cliente y del Lado Servidor. De forma preetermianda Joomla! utiliza las reglas de validación PHP para la validación de formulario del Lado Aervidor y JavaScript para reglas de validación del Lado Cliente. Esto significa que tenemos que duplicar todas las reglas que queremos comprobar en PHP y JavaScript. Es hora de hacer las cosas diferentes.

Una ventaja más de la utilización de un conjunto de reglas de validación, puede ser utilizar el mismo texto para todos los mensajes de error.

El Lado Cliente no necesita ningún código especial, ya que siempre utiliza reglas de validación PHP, por lo que esto funciona de forma predeterminada.

Vamos a crear/modificar un par de archivos:

  • enquiry.xml (definición del formulario)
  • validate.js (transporte)
  • validate.json.php (controlador)
  • validate.php (modelo)
  • edit.php (formulario)
  • en-GB.com_helloworld.ini (idioma, además deberemos crear el es-ES.com_helloworld.in)

El proceso

La forma en que esto funciona es siguiendo este proceso:

  1. Usuario carga una página con un formulario de edición
  2. Después de llenar el formulario, el usuario hace clic en el botón de enviar
  3. La función Joomla.submitbutton = function(task) captura la acción de enviar
  4. Esta función llama a la función validateForm() del archivo validate.js
  5. El validateForm() obtiene los datos necesarios del formulario y los pasa a la función sendForm()
  6. Ahora estamos listos para el envío, el sendForm() va a enviar los datos al servidor para tener los datos validados
  7. En caso de que la respuesta desde el servidor contenga un problema con la validación, el mensaje será mostrado al usuario y el formulario no se envía
  8. En caso de que la respuesta desde el servidor no contenga problema alguno, se enviará el formulario.

Los archivos

enquiry.xml

Vamos a empezar con el archivo XML, enquiry.xml para el formulario, este tendrá 5 campos:

<?xml version="1.0" encoding="utf-8"?>
<form>
	<fieldset>
		<field name="enquiry_id"
		       type="hidden" />

		<field name="title"
		       type="text"
		       label="COM_HELLOWORLD_ENQUIRY_TITLE_LABEL"
		       maxlength="50"
		       required="true" />

		<field name="publish_up"
		       type="text"
		       label="COM_HELLOWORLD_ENQUIRY_PUBLISH_UP_LABEL"
		       hint="dd-mm-jjjj"
		       required="true"
		       validate="hello.date"
		       message="COM_HELLOWORLD_FORM_NO_VALID_DATE_MESSAGE"/>

		<field name="publish_down"
		       type="text"
		       label="COM_HELLOWORLD_ENQUIRY_PUBLISH_DOWN_LABEL"
		       hint="dd-mm-jjjj"
		       field="publish_up"
		       validate="hello.dategreater"
		       required="true"
		       message="COM_HELLOWORLD_FORM_NO_VALID_GREATERDATE_MESSAGE"/>

		<field name="phone"
		       type="tel"
		       label="COM_HELLOWORLD_PHONE"
		       validate="tel"
		       required="false"
		       message="COM_HELLOWORLD_FORM_NO_VALID_PHONE_MESSAGE"/>
	</fieldset>
</form>

Aquí tenemos 5 campos: enquiry_id, title, publish_up, publish_down and phone.

  • title: Este campo no tiene ninguna regla de validación.
  • publish_up: Este campo tiene una regla de validación de hello.date.
  • publish_down: Este campo tiene una regla de validación de hello.greaterdate.
  • phone: Este campo tiene una regla de validación de phone.

validate.js

Este archivo contiene el código que envía la solicitud de validación en el servidor.

function validateForm(form, xml, group, token) {
	// Load all the form fields
	var filter = '[name^=jform';

	if (group) {
		filter += '\\[' + group + '\\]';
	}

	filter += ']';

	var fields = jQuery('#' + form + ' ' + filter);

	// Collect the fields and their values
	var data = [];

	fields.each(function (key, field) {
		data.push(field.name + '=' + encodeURIComponent(field.value));
	});

	// Send the form with the field info
	return sendForm(data, xml, group, token);
}

/**
 * Validate the fields on the server
 *
 * @param   array   data   An array with the data to validate.
 * @param   string  xml    The name of the XML file to validate against.
 * @param   string  group  The name of the group the fields belong to.
 * @param   string  token  The security token.
 */
function sendForm(data, xml, group, token) {
// Send the data to the server for validation
	return jQuery.ajax({
		async: true,
		url: 'index.php',
		dataType: 'json',
		cache: false,
		method: 'post',
		data: 'option=com_helloworld&task=validate.validateform&xml=' + xml + '&group=' + group + "&" + data.join('&') + '&format=json&' + token + '=1',
		success: function (data) {
			if (data) {
				var msg = '';

				jQuery(data).each(function (index, item) {
					if (item.message.length > 0) {
						var msgtype = item.type;

						msg =
						{
							msgtype: [item.message]
						};
					}
				});

				// Render the message
				if (jQuery(msg).size() > 0) {
					Joomla.renderMessages(msg);
				}
			}
		},
		error: function (request, status, error) {
			var msg = {
				error: [request.status + ' ' + request.statusText + '<br />' + request.responseText]
			};

			// Render the message
			Joomla.renderMessages(msg);
		}
	});
}

validate.json.php

Este es el controlador que recibe la petición AJAX y pasa los datos al modelo para ser validado.

defined('_JEXEC') or die;

/**
 * Validate controller.
 *
 * @package     Helloworld
 * @since       1.0
 */
class HelloworldControllerValidate extends JControllerForm
{
	/**
	 * Add a new lead.
	 *
	 * @return  string  JSON encoded string.
	 *
	 * @since   1.0
	 */
	public function validateForm()
	{
		// Check for request forgeries.
		JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));

		$model = $this->getModel();
		$data = $this->input->post->get('jform', array(), 'array');
		$xml = $this->input->post->getCmd('xml', '');
		$group = $this->input->post->getCmd('group', '');
		$return = array(
			'message' => '',
			'type' => '',
			'continue' => true
		);

		// Validate the posted data.
		// Sometimes the form needs some posted data, such as for plugins and modules.
		$form = new JForm($xml);
		$form->addFormPath(JPATH_SITE . '/components/com_helloworld/models/forms');
		$form->loadFile($xml);

		if (!$form)
		{
			$return['message'] = $form->getErrors();
			$return['type'] = 'error';
			$return['continue'] = false;
		}
		else
		{
			// Test whether the data is valid.
			$validData = $model->validate($form, $data, $group);

			// Check for validation errors.
			if ($validData === false)
			{
				// Get the validation messages.
				$errors = $model->getErrors();

				// Push the validation messages out to the user.
				for ($i = 0, $n = count($errors); $i < $n && $i < 1; $i++)
				{
					if ($errors[$i] instanceof Exception)
					{
						$return['message'] = $errors[$i]->getMessage();
						$return['type'] = 'warning';
						$return['continue'] = false;
					}
					else
					{
						$return['message'] = $errors[$i];
						$return['type'] = 'warning';
						$return['continue'] = false;
					}
				}
			}
		}

		echo json_encode($return);

		jexit();
	}
}

@todo: Usa JResponseJson para devolver los resultados.

validate.php

El modelo simplemente envía la solicitud de Joomla.

defined('_JEXEC') or die;

/**
 * Validate form model.
 *
 * @package  Helloworld
 * @since    1.0
 */
class HelloworldModelValidate extends JModelForm
{
	/**
	 * Method to get the contact form.
	 * The base form is loaded from XML and then an event is fired
	 *
	 * @param   array    $data      An optional array of data for the form to interrogate.
	 * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
	 *
	 * @return  JForm  A JForm object on success, false on failure
	 *
	 * @since   1.0
	 */
	public function getForm($data = array(), $loadData = true)
	{
		return false;
	}
}

edit.php

El formulario que muestra el formulario concreto. En nuestro archivo de plantilla tendremos el siguiente formulario:

<form action="<?php echo JRoute::_('index.php?option=com_helloworld&view=enquiry&layout=edit&id=' . $this->item->enquiry_id, false); ?>" method="post" name="item-form" id="item-form">
	<div>
		<?php echo $this->form->getLabel('title'); ?>
		<div class="controls">
			<?php echo $this->form->getInput('title'); ?>
		</div>
	</div>

	<div>
		<?php echo $this->form->getLabel('publish_up'); ?>
		<div class="controls">
			<?php echo $this->form->getInput('publish_up'); ?>
		</div>
	</div>

	<div>
		<?php echo $this->form->getLabel('publish_down'); ?>
		<div class="controls">
			<?php echo $this->form->getInput('publish_down'); ?>
		</div>
	</div>

	<input type="hidden" name="task" value="enquiry.submit">
	<input type="hidden" name="enquiry_id" value="<?php echo $this->item->enquiry_id; ?>">
	<?php echo JHtml::_('form.token'); ?>
</form>
<script type="text/javascript">
	Joomla.submitbutton = function(task)
	{
		if (task == 'enquiry.cancel')
		{
			Joomla.submitform(task, document.getElementById('item-form'));
		}
		else
		{
			jQuery.when(validateForm('item-form', 'enquiry', '', '<?php echo JSession::getFormToken(); ?>')).done(function(result)
			{
				if (result.continue)
				{
					Joomla.submitform(task, document.getElementById('item-form'));
				}
			});
		}
	}
</script>

Las reglas de validación

Todas las reglas de validación personalizadas se almacenan en la carpeta administrator/components/com_helloworld/models/rules. Así que tenemos un lugar donde las reglas personalizadas viven. A las reglas predeterminadas, Joomla! sabe dónde encontrarlos, a las reglas personalizadas Joomla! no sabe donde hallarlas, por lo que la siguiente línea de código debe ser añadida a su archivo de entrada del componente:

JForm::addRulePath('administrator/components/com_helloworld/models/rules');

hello.date

defined('_JEXEC') or die;

/**
 * Form Rule class that checks for a valid date value.
 *
 * @package     Helloworld
 * @since       1.0
 */
class HelloFormRuleDate extends JFormRule
{
	/**
	 * Method to test the field is not empty.
	 *
	 * @param   SimpleXMLElement  $element  The SimpleXMLElement object representing the <field /> tag for the form field object.
	 * @param   mixed             $value    The form field value to validate.
	 * @param   string            $group    The field name group control value. This acts as as an array container for the field.
	 *                                      For example if the field has name="foo" and the group value is set to "bar" then the
	 *                                      full field name would end up being "bar[foo]".
	 * @param   JRegistry         $input    An optional JRegistry object with the entire data set to validate against the entire form.
	 * @param   JForm             $form     The form object for which the field is being tested.
	 *
	 * @return  boolean  True if the value is valid, false otherwise.
	 *
	 * @since   11.1
	 */
	public function test(SimpleXMLElement $element, $value, $group = null, JRegistry $input = null, JForm $form = null)
	{
		try
		{
			JDate::getInstance($value);
		}
		catch (Exception $e)
		{
			return false;
		}

		return true;
	}
}

hello.greaterdate

defined('_JEXEC') or die;

/**
 * Form Rule class that checks for a date is later than another date.
 *
 * @package     Helloworld
 * @since       1.0
 */
class HelloFormRuleDategreater extends JFormRule
{
	/**
	 * Method to test the field is not empty.
	 *
	 * @param   SimpleXMLElement  $element  The SimpleXMLElement object representing the <field /> tag for the form field object.
	 * @param   mixed             $value    The form field value to validate.
	 * @param   string            $group    The field name group control value. This acts as as an array container for the field.
	 *                                      For example if the field has name="foo" and the group value is set to "bar" then the
	 *                                      full field name would end up being "bar[foo]".
	 * @param   JRegistry         $input    An optional JRegistry object with the entire data set to validate against the entire form.
	 * @param   JForm             $form     The form object for which the field is being tested.
	 *
	 * @return  boolean  True if the value is valid, false otherwise.
	 *
	 * @since   11.1
	 */
	public function test(SimpleXMLElement $element, $value, $group = null, JRegistry $input = null, JForm $form = null)
	{
		try
		{
			// Check for a valid date
			$date1 = JDate::getInstance($value);

			// Check if the second date is after the first date
			$field = (string) $element['field'];
			$fvalue = $input->get($group . '.' . $field, false);

			if ($value)
			{
				try
				{
					$date2 = JDate::getInstance($fvalue);

					if ($date1 < $date2)
					{
						return false;
					}
				}
				catch (Exception $e)
				{
					return false;
				}
			}
		}
		catch (Exception $e)
		{
			return false;
		}

		return true;
	}
}

Archivo de idioma

En los anteriores fragmentos de código, se utiliza, una cierta cantidad de cadenas de idioma, no te olvides de ellas para agregarlos a los archivos de idioma apropiados. Necesitas agregar las cadenas de idioma al archivo del Lado Cliente, además para el archivo de idioma del lado Servidor en caso de querer utilizar las mismas reglas de validación allí. La otra opción es usar sólo el archivo de idioma del lado Cliente, pero deberás cargar este archivo de idioma en el Lado Servidor también.