Silex / Symfony2 Post-Validation - forms

Im sure this must be a RTM question, but I must be looking in the wrong places. In symfony 1.4 I used post validator callbacks quite a lot. For example checking that start and end dates were in the correct order. I am developing an app in Silex but cant figure out how to add similar functionality as a validator. This is what I am working with (basically):
$app['form.example'] = function ($app) {
$constraints = new Assert\Collection(array(
'date1' => new Assert\Date(),
'date2' => new Assert\Date(),
));
$builder = $app['form.factory']->createNamedBuilder('form', 'example', array(), array('validation_constraint' => $constraints));
return $builder
->add('date1', 'date')
->add('date2', 'date')
->getForm();
};
I can put my own validation test in the 'process form' part, like: if ($form->isValid() && --my datetest--) but it doesnt feel right to me there.
Any help? Thanks!

I guess you can use form events for this kind of thing; certainly for Symfony2, so I assume Silex too? There's a cookbook article about using it to generate dynamic forms:
http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html
Some useful detail in another SO question:
Description of Symfony2 form events?
Could have sworn I had a SO discussion about this with someone before but I cannot find the question. I used BIND_CLIENT_DATA to calculate some form fields in some code a while back; I'll see if I can dig it out.
EDIT
Okay, I found something but I'm modifying this from Symfony code so I'm not 100% on this but it might be a starting point (I hope):
$builder->addEventListener(FormEvents::BIND_NORM_DATA, function($event) {
// your form data
$data = $event->getData();
// get date objects - if you cannot dereference this way try getters
$d1 = $data['date1'];
$d2 = $data['date2'];
// naive comparison :)
$isCorrectDateOrder = $d1->getTimestamp() < $d2->getTimestamp();
// check the comparison
if (!$isCorrectDateOrder) {
// trouble... create and add a FormError object to the form
$event->getForm()->get('date1')->addError(new \Symfony\Component\Form\FormError('uh oh...'));
}
});
Hope this helps :)

Related

Object of class AppBundle\Entity\Tarifa could not be converted to string

I saw a lot of similar issues, but I can't find the solution to this without adding __toString() method.
This works for me:
$tarifa = new Tarifa();
$form = $this->createForm('AppBundle\Form\TarifaType', $tarifa);
And this does not work...
$tarifa = new Tarifa();
$peso1 = new TarifaPeso();
$tarifa->addPeso($peso1);
$form = $this->createForm('AppBundle\Form\TarifaType', $tarifa);
Any help is welcome...
I believe you need this
$tarifa->addPeso($peso1->getPeso());
or this
$tarifa->addPeso($peso1->getAmount());
Depending on which property in TarifaPeso you have for this, if its also an entity and have getters
or its a ValueObject?
Configure option choice_label to be a property from TarifaPeso in your TarifaType class. This way, symfony does not intend to convert entity to a string when printing the option label. Instead, it will take the property that you point to.
Finally I solved it! The fact is I had added the field in the TarifaType that is related to the Collection. I removed the field below and now it works! Hope it could help anybody... Thanks to all
->add('tarifa', null, array(
'attr' => array('autofocus' => true),
'label' => 'label.code',
))

Phalcon form validation messages

There are some misunderstandings about how the messaging works for the Forms in Phalcon. Say we have a form and trying to add some extended error message for one of fields named 'code' in controller:
$form = new SampleForm();
Implementation of SampleForm is done in a corresponding class through initialize and the code element is $code = new \Phalcon\Forms\Element\Text('code');
The next code adds the message :
$form->get('code')->appendMessage(new \Phalcon\Validation\Message("The Code desn\'t exist or not valid"));
but trying to receive this message like
$form->getMessagesFor('code')
gives me nothing (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] =>
)
Another attempt via
$form->get('code')->getMessages()
gives (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] => Array
(
[0] => Phalcon\Validation\Message Object
(
[_type:protected] =>
[_message:protected] => The Code desn\'t exist or not valid
[_field:protected] =>
[_code:protected] => 0
)
)
)
My question: what am I doing wrong and why $form->[get/has]MessagesFor($name) doesn't work as expected?
I'm sorry to tell you, form handling is really poorly implemented and messed up in Phalcon, as I can see now, after a few months work.
In this case, you have multiple getMessage functions and they DO NOT return the same value. Even some of them returns a reference and some of them returns a copy of the error messages, and if you are appending messages to the copy, they won't be available elsewhere through the getMessage functions.
This even changed form version 1.2 to 1.3, we had a hard time figuring out which functions should we use. If you like, you could check out the C code behind this, in the Phalcon repository, it has been a great help for me to figure out why things don't work the way I expected.
My advices is, print the values of getMessage functions: Form::getMessages(), Form::getMessagesFor(), Form::get('element_name')->getMessages(). Then try appending new messages to them and print them again. See which one contains your messages.
I ended up getting messages for Phalcon's built in validation classes by $form->get('email')->getMessages() and getting my own added in the controller by $form->getMessagesFor('email'). I am still searching for a solution to get all the messages in one place.
Serin is right, messages are saved into different objects, so i came up with getting all of those like this
<?php foreach ($form as $element) {
//Get any generated messages for the current element
foreach ([$form->getMessagesFor($element->getName()), $element->getMessages()] as $messages) {
if (count($messages)) {
//Print each element
foreach ($messages as $message) {
echo $message;
}
}
}
}?>
Let's hope easier method will be added in nearest future:

Zend Framework - Form Element - Remove ID

Is it possible to remove the 'id' attribute that Zend Framework adds to every Form Element by default?
I've looked at the documentation but don't seem to be able to find an answer to this rather straight forward question.
Possible Solution
Is there any cleaner way to do it other than setOption?
$submit = new Zend_Form_Element_Submit('submit');
$submit->setRequired(FALSE)
->setIgnore(TRUE)
->setDecorators($this->elementDecorators)
->setOptions(array('id' => ''));
A solution would be to override Zend_View_Helper_Form with your own View Helper.
But sincerely, don't take too much attention to this id attribute in your form, you will sooner or later need this id (if you're using Javascript for example) and the performance gain (to render the page) is too tiny to be taken into consideration. It will even be a performance loss since you're going to override a Helper.
If your purpose is different and you want to do that anyway, you need to write you own View Helper as follow:
class My_View_Helper_Form extends Zend_View_Helper_FormElement
{
public function form($name, $attribs = null, $content = false)
{
$info = $this->_getInfo($name, $content, $attribs);
extract($info);
$xhtml = '<form'
. $this->_htmlAttribs($attribs)
. '>';
if (false !== $content) {
$xhtml .= $content
. '</form>';
}
return $xhtml;
}
}
Finally, you simply need to overload the default view helper using a plugin loader. Read the manual for more information about plugin loader.

Symfony2 - Multiple forms in one action

I implemented a page to create an instance of an entity and a user related to this. My problem is to bind the request after the submit.
Now i have this :
$formA = $this->createForm(new \MyApp\ABundle\Form\AddObjectForm());
$formB = $this->createForm(new \MyApp\UserBundle\Form\AddUserForm());
if ($request->getMethod() == 'POST')
{
$formA->bindRequest($request);
$formB->bindRequest($request);
if ($formA->isValid() && $formB->isValid())
{
}
// ...
}
With formA and formB extends AbstractType. But, naturally, $formA->isValid() returns false. How can I do to "cut" the request for example ?
If your forms are related and need to be processed and validated at once, consider using embedded forms. Otherwise, use a separate action for each form.
If you need to provide a select field to choose a user from existing ones, consider using an entity type field.
I know that has been a long time since the answer, but maybe if someone is looking for it this could be helpfull.
I've got two forms: user_form and company_form, and the submit has take place in the same function of the controller. I can know wich form has been submit with the follow code:
if ($request->getMethod() == 'POST') {
$data = $request->request->all();
if (isset($data['user_form'])) //This if means that the user_form has been submit.
{
The company_form will pass through the else.

Drupal select list only submitting first character

I have a select list I've created in a form alter, however, when I select an option and submit the value, only the first digit gets stored in the database. I know this has something to do with how the array is formatted, but I can't seem to get it to submit properly.
function addSR_form_service_request_node_form_alter(&$form, $form_state) {
$form['field_sr_account'] = array( '#weight' => '-50',
'#type' => 'select',
'#title' => 'Select which account',
'#options' => addSR_getMultiple());
//Custom submit handler
$form['#submit'][] = 'addSR_submit_function';
}
function addSR_submit_function{
$form_state['values']['field_sr_account'] = array('0' => array('value' => $form['#field_sr_account']));
Below is the function that returns the associative array. It is returning the proper options, as I can view the correct value/option in the HTML source when the page loads
//The values returned are not the problem, however, the format of the array could be..
function addSR_getMultiple(){
$return = array();
$return['one'] = 'Choice1'
$return['two'] = 'Choice2'
return $return;
}
Update:
Drupal 6: Only inserting first character of value to MySQL
I had a similar issue with the same field. However, in that case, I knew the value I wanted to submit, and I was able to assign the value to the field in the form alter, before the form was submitted. The difference with this issue, is that I don't know the value of the field until it is submitted, so I can't "assign" it in the form alter. How can I assign it the same way in the submit handler.
Edit after question update (and discovery of root problem within the linked separate question):
As you are trying to manipulate CCK fields, and those have pretty special handling mechanisms compared to 'standard' Drupal FAPI form elements, you should probably read up on CCK form handling in general, and hook_form_alter() and CCK fields and CCK hooks in particular. Glancing at those documentations (and the other CCK articles linked in the left sidebar), it looks like there should be a straight forward solution to your problem, but it might require some digging.
As a potential 'quick fix', you could try keeping your current approach, and adjust the submitted value on validation, somewhat like so:
function addSR_form_service_request_node_form_alter(&$form, $form_state) {
$form['field_sr_account'] = array(
'#weight' => '-50',
'#type' => 'select',
'#title' => 'Select which account',
'#options' => addSR_getMultiple()
);
// Add custom validation handler
$form['#validate'][] = 'addSR_validate_function';
}
function addSR_validate_function (&$form, &$form_state) {
// Assemble result array as expected by CCK submit handler
$result = array();
$result[0] = array();
$result[0]['value'] = $form_state['values']['field_sr_account'];
// Set this value in the form results
form_set_value($form['field_sr_account'], $result, $form_state);
}
NOTE: This is untested code, and I have no idea if it will work, given that CCK will do some stuff within the validation phase as well. The clean way would surely be to understand the CCK form processing workflow first, and manipulating it accordingly afterward.