1

I am trying to validate the input of my form on server side, but I always get true from the validator. What am I doing wrong?

.../templates/customer_registration.xml

<?xml version="1.0" encoding="UTF-8"?>
<form name="customer_registration_form" title="COM_CANTEEN_CUSTOMER_REGISTRATION_TITLE" description="COM_CANTEEN_CUSTOMER_REGISTRATION_DESCRIPTION" buttonLabel="COM_CANTEEN_CUSTOMER_REGISTRATION_BUTTON_LABEL">
    <fields name="customer_registration">
        <fieldset name="customer_registration">
            <field name="name" type="text" class="required validate-username" size="80" minLength="4" maxLength="255"
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_MESSAGE" />
            <field name="age" type="integer" class="required" default="6" first="1" last="150" step="1"
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_MESSAGE" />
            <field name="permanent_orderer" type="checkbox" default="1"
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_MESSAGE" />
        </fieldset>
    </fields>
</form>

.../templates/basic_form_layout.php

<?php
extract($displayData);
JHtml::_('behavior.tooltip');
?>
<form action="<?php echo JRoute::_('index.php?option=com_canteen&controller=main&action=form_test'); ?>"
      method="post" name="<?php echo $form->getAttribute('name'); ?>" class="<?php echo $form->getAttribute('class'); ?>">
    <legend><?php echo JText::_($form->getAttribute('title')); ?></legend>
    <?php
        echo JText::_($form->getAttribute('description'));
        foreach (array_keys($form->getFieldsets()) as $fieldset)
            echo $form->renderFieldset($fieldset);
    ?>
    <div>
        <?php echo JHtml::_('form.token'); ?>
        <button type="submit"><?php echo JText::_($form->getAttribute('buttonLabel')); ?></button>
    </div>
</form>

.../MainController.php

/**
* @action 
* @allow("public")
*/
public function formTest(iRequest $request, iResponse $response){

    $templateDir = JPATH_COMPONENT . '/Canteen/presentation/templates';
    $form = JForm::getInstance('customer_registration', $templateDir . '/customer_registration.xml');

    $formData = $request->getPostData()->getArray();
    $filtered = $form->filter($formData);

    foreach ($filtered as $fieldset => $fields)
        foreach ($fields as $field => $value)
            $form->setValue($field, $fieldset, $value);

    $result = $form->validate($filtered);

    $content = new HtmlFragment();
    $content->addTemplate($response->getTemplate('basic_form_layout.php'), array('form' => $form));
    $content->addText(var_export($result, true));
    $response->setContent($content);

}

I checked the $filtered variable; it contains the filtered data without the CSRF token, something like: {'customer_registration': {name: "", age: "6", permanent_orderer: "1"}}. Actually no matter what data I give to the $form->validate($data), it always returns true. I'd like to use JForm to validate and display error messages. Any idea why this does not work?

1 Answer 1

0

I checked the code of the JForm.

According to it class="required validate-username" does not work, and I should use required="true" instead by the fields required.

Everything else is validated by rules. I need to add validate="ruleType" to the XML file. The minLength, maxLength etc. apparently works only by client side, and the server side validation does not follow the HTML5 specs.

I guess I need to define the same rule types on a client side lib too if I want to validate on client side. I checked with HTML5, it worked, but I got no error messages. I thought it is broken, but I guess it does not connect to the HTML5 validation properly. Does not really matter, I don't want to work twice, so I won't have client side validation.

I can read the error messages with $form->getErrors(). I thought they are added to the fields, so auto rendering them would show the error messages, but apparently I need to enqueue these messages on the joomla app. It is okay, but could have been better.

note:

I found the related part in the docs, probably I read too superficial. I did not sleep. :S

I ended up with the following code:

.../templates/customer_registration.xml

<?xml version="1.0" encoding="UTF-8"?>
<form name="customer_registration_form" title="COM_CANTEEN_CUSTOMER_REGISTRATION_TITLE" description="COM_CANTEEN_CUSTOMER_REGISTRATION_DESCRIPTION" buttonLabel="COM_CANTEEN_CUSTOMER_REGISTRATION_BUTTON_LABEL">
    <fields name="customer_registration">
        <fieldset name="customer_registration">
            <field name="name" type="text" required="true" size="80"
                validate="text" minLength="4" maxLength="80" pattern="\p{L}+(?:\p{Zs}\p{L}+)+" 
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_NAME_MESSAGE" />
            <field name="age" type="integer" required="true" default="6" first="1" last="150" step="1"
                validate="integer" min="1" max="150"
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_AGE_MESSAGE" />
            <field name="permanent_orderer" type="checkbox" default="1"
                label="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_LABEL" description="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_DESCRIPTION" message="COM_CANTEEN_CUSTOMER_REGISTRATION_PERMANENT_ORDERER_MESSAGE" />
        </fieldset>
    </fields>
</form>

.../templates/rules/text.php

<?php

use \SimpleXMLElement;
use \JForm;
use \JRegistry;
use \JFormRule;

class JFormRuleText extends JFormRule {

    public function test(SimpleXMLElement $element, $value, $group = null, JRegistry $input = null, JForm $form = null)
    {
        if (!isset($value))
            $value = '';
        if (!is_string($value))
            return false;

        $length = mb_strlen($value, mb_detect_encoding($value));

        if (isset($element['minLength'])) {
            $minLength = (int) $element['minLength'];
            if ($minLength > $length)
                return false;
        }

        if (isset($element['maxLength'])) {
            $maxLength = (int) $element['maxLength'];
            if ($maxLength < $length)
                return false;
        }

        if (isset($element['pattern'])) {
            $pattern = '/'.$element['pattern'].'/usD';
            if (!preg_match($pattern, $value))
                return false;
        }

        return true;
    }
}

.../templates/rules/integer.php

<?php

use \SimpleXMLElement;
use \JForm;
use \JRegistry;
use \JFormRule;

class JFormRuleInteger extends JFormRule {

    public function test(SimpleXMLElement $element, $value, $group = null, JRegistry $input = null, JForm $form = null)
    {
        $intValue = (int) $value;
        if ((string) $intValue !== $value || (float) $intValue !== (float) $value)
            return false;

        if (isset($element['min'])) {
            $min = (int) $element['min'];
            if ($min > $intValue)
                return false;
        }
        if (isset($element['max'])) {
            $max = (int) $element['max'];
            if ($max < $intValue)
                return false;
        }
        return true;
    }
}

.../MainController.php

/**
* @action 
* @allow("public")
*/
public function formTest(iRequest $request, iResponse $response){
    $form = $response->getForm('customer_registration.xml', $request->getAddress(), $request->getQuery());
    if ($request->isPost()){
        $form->fill($request->getPostData());
        $isValid = true;
        if (!$request->hasValidToken()) {
            $response->reportError(new Exception('Invalid token.'));
            $isValid = false;
        }
        if (!$form->validate()) {
            foreach ($form->getErrors() as $error)
                $response->reportError($error);
            $isValid = false;
        }
        if ($isValid) {
            $response->reportSuccess('Customer created.');
            $form->reset();
        }
    }

    $content = new HtmlFragment();
    $content->addForm($form, $response->getTemplate('basic_form_layout.php'));
    $response->setContent($content);
}

Ofc. this is still just a dummy proof of concept code to demonstrate that the validation works.

It is possible to override the default error messages too if somebody wants to:

// required: JText::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED', $name|label);
// rule: $message | JText::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID', $label)

And by returning an Exception instance instead of false it is possible to send different error messages from the same rule. So it is not mandatory to use the message given in the XML. For me it was enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.