Jeu de validation unique de formulaire
From Joomla! Documentation
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 :
- L'utilisateur charge une page avec un formulaire d'édition.
- Après avoir complété le formulaire, l'utilisateur clique sur le bouton soumettre.
- La fonction Joomla.submitbutton = function(task) saisie l'action soumettre.
- Cette fonction va appeler la fonction validateForm() dans le fichier validate.js.
- La fonction validateForm() permet de récupérer les données nécessaires du formulaire et de les transmettre à la fonction sendForm().
- Maintenant, nous sommes prêt pour l'envoi, la fonction sendForm() va envoyer les données au serveur pour validation des données.
- 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é.
- 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.