Formulario único conjunto de validación
From Joomla! Documentation
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:
- Usuario carga una página con un formulario de edición
- Después de llenar el formulario, el usuario hace clic en el botón de enviar
- La función Joomla.submitbutton = function(task) captura la acción de enviar
- Esta función llama a la función validateForm() del archivo validate.js
- El validateForm() obtiene los datos necesarios del formulario y los pasa a la función sendForm()
- Ahora estamos listos para el envío, el sendForm() va a enviar los datos al servidor para tener los datos validados
- 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
- 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.