Apply a dynamic string to a field before validation - php

I am attempting to set the "title" value of a content type before it becomes "required" .
So what happens is the title field becomes hidden based on user name, after filling in the "first name" and "last name" fields I need to take those values and then apply them to the "title" field, before drupal states that the field is required. Here's what I have so far
/**
* Implements hook_form_alter().
*/
function editorhide_form_alter(&$form, &$form_state, $form_id){
global $user;
global $fullTitle;
if($form_id == 'artist_node_form'){
if( $user->name == 'Editor'){
drupal_add_js("jQuery(document).ready(function(){
jQuery('#edit-title').hide();
});","inline");
//adding the form handler
$form['#submit'][] = "editorhide_form_submit_handler";
}
}
}
//submit form handler.
function editorhide_form_submit_handler ($form, &$form_state) {
global $fullTitle;
$fullTitle = $form_state['values']['field_firstname']['und']['0']['value'];
$fullTitle .= ' '. $form_state['values']['field_lastname']['und']['0']['value'];
form_set_value($form['#edit-title'], $fullTitle,$form_state);
}
With my current implementation, it isn't doing quite what I want, as it's throwing the "required field" error.

The problem you are having is that your new submit function never gets executed. A form will go through all validation functions first.
You need to set a new validate function, and make sure it is inserted at the begining of the validation process (so you can set title prior to the node validation):
array_unshift($form['#validate'], "editorhide_form_validate_handler");
Then your validation can do the following (may need to adjust array indexes):
function editorhide_form_validate_handler ($form, &$form_state) {
$fullTitle = $form_state['values']['field_firstname']['und']['0']['value'];
$fullTitle .= ' '. $form_state['values']['field_lastname']['und']['0']['value'];
$form_state['values']['title']['und']['0']['value'] = $fullTitle;
}
I am not a HUGE fan of modifying form_state mid validation, but this should work.

Related

Form doesn't validate after submitting in moodle

I am trying to create a form whose structure depends on the parameter in the url. If no parameter is specified in the url, an error message should be displayed. Depending on the id, a database query is performed and the form is filled with data.
example url: http://127.0.0.1/local/group/signin.php?groupid=14
Unfortunately, my form doesn't validate after I submit the form via pressing the action button. It jumps to http://127.0.0.1/local/group/signin.php and because there is no parameter present in the url the error message 'No group found' is displayed.
What have I done wrong here?
signinform.php:
class signinform extends moodleform {
public function definition() {
global $DB;
global $USER;
$mform = $this->_form;
$urlid = $this->_customdata['id']; // get the passed group id
$message = 'No group found';
if(is_null($urlid)){
$mform->addElement('html', '<h3>'.\core\notification::error($message).'</h3>');
}
else{
// build the form, sql query etc.
$this->add_action_buttons(true, 'Submit');
}
}
function validation($data, $files) {
return array();
}
}
signin.php:
$PAGE->set_url(new moodle_url('/local/schedule/signin.php?'));
$PAGE->set_context(\context_system::instance());
$PAGE->set_pagelayout('base');
$PAGE->set_title("Sign up");
$PAGE->set_heading("Sign up for a group");
global $DB;
global $USER;
$urlid = $_GET["id"];
$to_form = array('id' => $urlid); // pass group id to form
$mform = new signinform(null, $to_form);
$homeurl = new moodle_url('/');
if ($mform->is_cancelled()) {
redirect($homeurl, 'Cancelled.'); // Just for testing, never enters here
} else if ($fromform = $mform->get_data()) {
redirect($homeurl, 'Validation in process'); // Just for testing, never enters here
}
echo $OUTPUT->header();
$mform->display();
echo $OUTPUT->footer();
You need to add a hidden field to the form that contains the 'id' that has to be passed to the page, otherwise, when the form is submitted, that id will no longer be present in the params for that page.
e.g. (in definition())
$mform->addElement('hidden', 'id', $urlid);
$mform->setType('id', PARAM_INT);
Also, in Moodle, you should never access $_GET directly - use the wrapper functions required_param() or optional_param(), as these:
Clean the parameter to the declared type
Automatically take parameters from either $_GET or $_POST (which will be important in this case, as the 'id' param will be part of the POST data, when you submit the form)
Handle missing parameters by either applying a default (optional_param) or stopping with an error message (required_param)
So your access to $_GET['id'], should be replaced with:
$urlid = optional_param('id', null, PARAM_INT);

Field validation to compare two field values

I've created a content type using two fields "capacity" and "count".
In a custom module I'd like to validate that field "count" should be less than field "capacity".
function MYMODULE_node_form_validate($form, &$form_state) {
$capacity = $form['capacity']['#value'];
$count = $form['count']['#value'];
if ($count > $capacity) {
form_set_error('title', 'Not possible');
}
}
First add a custom validation function, then fire your logic from there and prevent the form from further processing if necessary. Replace MYMODULE and MYCONTENTTYPE with your machine names.
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function MYMODULE_form_node_form_alter(&$form, &$form_state, $form_id) {
// Find the content type of the node we are editing.
$content_type = $form['#node']->type;
if ($content_type == 'MYCONTENTTYPE') {
// Add an additional custom validation callback.
$form['#validate'][] = 'MYCUSTOM_FORMVALIDATION';
}
}
/**
* Custom MYCONTENTYTPE node form validation.
*/
function MYCUSTOM_FORMVALIDATION($form, &$form_state) {
// Better check isset() and !empty() first. Depends on your needs.
// Convert values to comparable numbers.
// Maybe you prefer intval() or some other logic. Depends on your needs.
$field_a = floatval($form_state['values']['field_a'][LANGUAGE_NONE][0]['value']);
$field_b = floatval($form_state['values']['field_b'][LANGUAGE_NONE][0]['value']);
// Stop the form from further processing if field A < than field B.
if ($field_a < $field_b) {
form_set_error('stop', t('Field A shall be greater then Field B'));
}
}

Symfony 2 - Adding error to form element on preSubmit event subscriber

I have a preSubmit event in an event subscriber for a form, and for a specific case I want to add an error to a form field. My method within the subscriber is as follows:
public function onPreSubmit(FormEvent $event)
{
$sourceData = $event->getData();
$form = $event->getForm();
$identifier = &$sourceData['identifier'];
if ($identifier) {
if ($this->identifierIsUrl($identifier)) {
$parser = $this->getIdParser();
$identifier = $parser->getIdentifier($identifier);
if (is_null($identifier)) {
$form->get('identifier')->addError(new FormError('You have either entered an incorrect url for the source or it could not be parsed'));
}
}
$event->setData($sourceData);
}
}
However when I print the form error in the view, it is empty. Is it possible to do this in a preSubmit event? Am I looking at this the wrong way?
The issue is related to the Symfony\Component\Form\Form::submit method that removes all of the form field specific errors assigned after the PRE_SUBMIT event.
During Form::submit it iterates over all the forms child objects (which are also themselves Form objects as noted by the other answers) and calls their submit methods individually. Resulting in the form element errors that were added during the parent's PRE_SUBMIT event to be reset to an empty array.
This is why you can use $form->addError() in the parent PRE_SUBMIT event or set the form element to error_bubbling => true and it will display as the parent form errors, but not to a specific form element.
Here's the example of what occurs without looking through the entire codebase for Symfony Forms.
class Form
{
public function addError($error)
{
if($this->parent && $this->config->getErrorBubbling()) {
$this->parent->addError($error); //element had error_bubbling => true, attach the error to the parent.
} else {
$this->errors[] = $error; //add it to the current object's errors array
}
}
public function submit()
{
$this->errors = array(); //resets the errors of the current object
$this->preSubmitEvent();
foreach($this->children as $child) {
$child->submit(); //errors in child object are reset
}
}
}
So it results in
Form:
submitMethod:
preSubmitEvent
children:
submitMethod:
preSubmitEvent
To get around the issue you can add a PRE_SUBMIT event directly to your form element to validate that element and add errors to it.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('identifier', Form\TextType::class);
//...
$builder->get('identifier')->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'validateIdentifier']);
}
Then alter your onPreSubmit method accordingly.
public function validateIdentifier(FormEvent $event)
{
$identifier = $event->getData();
$element = $event->getForm();
if ($identifier) {
if ($this->identifierIsUrl($identifier)) {
$parser = $this->getIdParser();
$identifier = $parser->getIdentifier($identifier);
if (null === $identifier) {
$element->addError(new FormError('You have either entered an incorrect url for the source or it could not be parsed'));
}
}
$event->setData($identifier);
}
}
It should be possible if you look at the Form::submit(), you'll see that the errors are reset before the PRE_SUBMIT data is dispatched. Also Validation Listener doesn't reset the errors on the form, and the Violation Mapper only adds Errors to the form. And AFIK there aren't any listeners that reset the form's errors. So maybe you're doing something else wrong.
It is possible that errors are just not displayed in your layout.
To debug, make sure your form is actually made invalid by your custom check (if you show your controller I may be able to help with actual code).
If that is in fact your issue, you may want to add error_bubbling => true to your field definition.

Dropdown validation

I am having one dropdown option in the form called town. Already fetched values will be available in drop down from the databasetable townId. Now I want to check if the user doesn't select any choice from the dropdown and directly goes to the save button, then it should display "please choose your choice in dropdown" like that. The form is named university
I tried this code here:
if(Zend_Form_Element_Submit)
{
if($$townid=='')
{ alert("U Must Choose Town Name Here");
}
else
{
$submit = new Zend_Form_Element_Submit('Save');
$submit->setDecorators($this->submitDecorators)
->setAttrib('class','button slategray');
}
}
Inside models->university.php there are some actions for dropdown I didn't get exactly:
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
Before I edited form->university (the save code already exits)
$submit = new Zend_Form_Element_Submit('Save');
$submit->setDecorators($this->submitDecorators)
->setAttrib('class','button slategray');
}
Thanks in advance.
To work properly with Zend Framework's forms I recommand to try this approach:
Create a form by extending the Zend_Form class
class Form_User extends Zend_Form
Which automatically give you access to a container to manage all your elements from this form and give you acces to a isValid() method which allow you to validate all your form elements at once and to a populate() method which allow you to feed data to your form for editing
In your new class (Form_User) you can define all your form properties and elements in the init() method.
public function init()
{
$this->setName('user')
->setAttrib('id', 'user');
$username = new Zend_Form_Element_Text('username');
$town = new Zend_Form_Element_Select('town');
$town->addMultioptions(array(...));
$submit = new Zend_Form_Element_Select('submit');
$this->addElements(array($username, $town, $submit));
}
Each of these elements can be customized with a label, a description, some validators, some filters, etc.
If you want to make an element mandatory you can set the Required property to true
$town->setRequired(true);
To validate your form after the submit has been cliqued, you can do it as simply as (assuming you do this in the controller) :
$form = new Form_User();
$postData = $this->getRequest()->getPost();
$form->isValid($postData);
This will check for the required fields as well as execute any validators that you have setted on these diverses elements.
It will return true if everything is ok, and false if there is an error. If you get an error a display the form again, error messages will be shown automatically beside each erroneous fields.
If you want to set options values of your form elements, when you initialize your form, you can pass a configuration array like this :
$form = new Form_User(array('townListOptions' => array(...));
The associated method setTownListOptions will be called automatically and will receive the array (or any other object) you assigned it with.
I could explain even further things about forms but as #Rohan stated in his comment, RTM
In asp.net
<asp:DropDownList ID="ddlusertype" runat="server" CssClass="dropdown" ></asp:DropDownList>
<asp:CompareValidator ID="CompareValidator2" runat="server" ControlToValidate="ddlusertype" ErrorMessage="select" Font-Size="XX-Small" Operator="NotEqual" Type="Integer" ValueToCompare="0"></asp:CompareValidator>

Zend_Form: Element should only be required if a checkbox is checked

I've got a Form where the user can check a checkbox "create new address" and can then fill out the fields for this new address in the same form.
Now I want to validate the fields of this new address ONLY if the checkbox has been checked. Otherwise, they should be ignored.
How can I do that using Zend_Form with Zend_Validate?
Thanks!
I think that the best, and more correct way to do this is creating a custom validator.
You can do this validator in two different ways, one is using the second parameter passed to the method isValid, $context, that is the current form being validated, or, inject the Checkbox element, that need to be checked for validation to occur, in the constructor. I prefer the last:
<?php
class RequiredIfCheckboxIsChecked extends Zend_Validate_Abstract {
const REQUIRED = 'required';
protected $element;
protected $_messageTemplates = array(
self::REQUIRED => 'Element required'
);
public function __construct( Zend_Form_Element_Checkbox $element )
{
$this->element = $element;
}
public function isValid( $value )
{
$this->_setValue( $value );
if( $this->element->isChecked() && $value === '' ) {
$this->_error( self::REQUIRED );
return false;
}
return true;
}
}
Usage:
class MyForm extends Zend_Form {
public function init()
{
//...
$checkElement = new Zend_Form_Element_Checkbox( 'checkbox' );
$checkElement->setRequired();
$dependentElement = new Zend_Form_Element_Text( 'text' );
$dependentElement->setAllowEmpty( false )
->addValidator( new RequiredIfCheckboxIsChecked( $checkElement ) );
//...
}
}
I have not tested the code, but I think it should work.
I didn't actually run this, but it should work within reason. I've done something similar before that worked, but couldn't remember where the code was.
<?php
class My_Form extends Zend_Form
{
public function init()
{
$checkbox = new Zend_Form_Element_Checkbox("checkbox");
$checkbox->setValue("checked");
$textField = new Zend_Form_Element_Text("text");
$this->addElements(array("checkbox", "text"));
$checkbox = $this->getElement("checkbox");
if ($checkbox->isChecked() )
{
//get textfield
$textField = $this->getElement("text");
//make fields required and add validations to it.
$textField->setRequired(true);
}
}
}
Here's what I do if I need to validate multiple elements against each other
$f = new Zend_Form();
if($_POST && $f->isValid($_POST)) {
if($f->checkbox->isChecked() && strlen($f->getValue('element')) === 0) {
$f->element->addError('Checkbox checked, but element empty');
$f->markAsError();
}
if(!$f->isErrors()) {
// process
...
...
}
}
I've been wondering how to do that in ZF as well, though never had to implement such form feature.
One idea that comes to mind is to create a custom validator that accepts the checkbox field as a parameter, and run it in a validator chain, as documented. If the checkbox is checked, validator could return failure. Then you can check whether all validations failed and only then treat form as having failed validation.
That level of customization of form validation could be inconvenient, so maybe using form's isValidPartial method would be better.
I created a custom validator that will make your element required based on the value of another zend form element.
Here's the full code. I hope this helps someone.
The idea is to create a custom validator and pass in the name of the conditional element and the value of that conditional element into the constructor. Zend_Validor's isValid method already has access to the value of the element you are attaching the validtor to along with all the form element names and values.
So, inside the isValid method you have all the information you need to determine if your form element should be a required element.
On Zend 1 extend the isValid method, where you set required depending on posted data for example:
public function isValid($data)
{
if (!empty($data['companyCar'])) {
$this->getElement('carValue')->setRequired(true);
}
return parent::isValid($data);
}
Thank you JCM for your good solution.
However 2 things I've noticed:
The isValid method of your validator has to return true in case of success.
The validator will not be executed during form validation without pass the allowEmpty option to false to the text field.

Categories