Jeu de validation unique de formulaire

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 • ‎español • ‎français • ‎Nederlands
Joomla! 
≥ 3.0
Didacticiel
Jeu de validation unique de formulaire

Cette page explique comment utiliser les mêmes règles de validation de formulaire pour la validation côté client et côté serveur. Par défaut Joomla! utilise les règles de validation de formulaire PHP pour la validation côté serveur et les règles JavaScript pour la validation côté client. Cela implique de devoir dupliquer toutes les règles que nous souhaitons vérifier en PHP et en JavaScript. Il est temps de faire les choses différemment.

Un autre avantage de l'utilisation d'un seul ensemble de règles de validation est que vous pouvez utiliser le même texte pour tous les messages d'erreur.

Le backend n'a besoin d'aucun code spécial car il utilise toujours les règles de validation PHP, de sorte que cela fonctionne par défaut.

Nous allons créer/modifier quelques fichiers :

  • enquiry.xml (définition du formulaire)
  • validate.js (transport)
  • validate.json.php (contrôleur)
  • validate.php (modèle)
  • edit.php (formulaire)
  • en-GB.com_helloworld.ini (langue)

Le processus

Cela fonctionne en suivant ce processus :

  1. L'utilisateur charge une page avec un formulaire d'édition.
  2. Après avoir complété le formulaire, l'utilisateur clique sur le bouton soumettre.
  3. La fonction Joomla.submitbutton = function(task) saisie l'action soumettre.
  4. Cette fonction va appeler la fonction validateForm() dans le fichier validate.js.
  5. La fonction validateForm() permet de récupérer les données nécessaires du formulaire et de les transmettre à la fonction sendForm().
  6. Maintenant, nous sommes prêt pour l'envoi, la fonction sendForm() va envoyer les données au serveur pour validation des données.
  7. Si la réponse du serveur contient un problème de validation, le message sera affiché à l'utilisateur et le formulaire ne sera pas envoyé.
  8. Si la réponse du serveur ne contient aucun problème, le formulaire sera alors envoyé.

Les fichiers

enquiry.xml

Commençons par le fichier XML, enquiry.xml, pour le formulaire, il contiendra 5 champs :

<?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>

Ici, nous avons 5 champs : enquiry_id, title, publish_up, publish_down et phone.

  • title: ce champ ne contient pas de règle de validation.
  • publish_up: ce champ contient une règle de validation hello.date.
  • publish_down: ce champ contient une règle de validation hello.greaterdate.
  • phone: ce champ contient une règle de validation de téléphone.

validate.js

Ce fichier contient le code qui envoie la demande de validation au serveur.

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

C'est le contrôleur qui reçoit la requête AJAX et transmet les données au modèle pour validation.

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 : utiliser JResponseJson pour retourner les résultats.

validate.php

Le modèle est simplement là pour envoyer la demande à 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

Le formulaire qui affiche le formulaire actuel. Dans notre fichier de template, nous aurons le formulaire suivant :

<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>

Les règles de validation

Toutes les règles de validation personnalisées sont stockées dans le dossier administrator/components/com_helloworld/models/rules. Ainsi, nous n'avons qu'un seul endroit pour les règles personnalisées. Joomla! sait où trouver les règles par défaut, mais ne sait pas où trouver les règles personnalisées, il faut donc ajouter la ligne de code suivante au début du fichier d'entrée de votre composant :

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;
	}
}

Fichier langues

Dans les extraits de code précédent, un certain nombre de chaînes de langue sont utilisées, n'oubliez pas de les ajouter aux fichiers de langue appropriés. Vous devez ajouter les chaînes au fichier de langue de frontend mais également au fichier de langue de backend au cas où vous souhaitez utiliser les mêmes règles de validation dans le backend. L'autre option est de n'utiliser que le fichier de langue du frontend et de charger ce même fichier pour le backend.