Whenever I submit the form I get this message:
The input was not found in the haystack.
This is for the shipping-method element (radio button). Can't figure out what it means, the POST data for that element is not null.
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
// Some other basic filters
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim')
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'max' => 20,
),
),
array(
'name' => 'Db\RecordExists',
'options' => array(
'table' => 'shipping',
'field' => 'shipping_method',
'adapter' => $this->dbAdapter
)
),
),
));
$inputFilter->get('shipping-address-2')->setRequired(false);
$inputFilter->get('shipping-address-3')->setRequired(false);
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
I only keep finding solutions for <select>.
Here's the sample POST data:
object(Zend\Stdlib\Parameters)#143 (1) {
["storage":"ArrayObject":private] => array(9) {
["shipping-name"] => string(4) "TEST"
["shipping-address-1"] => string(4) "test"
["shipping-address-2"] => string(0) ""
["shipping-address-3"] => string(0) ""
["shipping-city"] => string(4) "TEST"
["shipping-state"] => string(4) "TEST"
["shipping-country"] => string(4) "TEST"
["shipping-method"] => string(6) "Ground"
["submit-cart-shipping"] => string(0) ""
}
}
UPDATE:
form.phtml
<div class="form-group">
<?= $this->formRow($form->get('shipping-method')); ?>
<?= $this->formRadio($form->get('shipping-method')
->setValueOptions(array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'))
->setDisableInArrayValidator(true)); ?>
</div>
ShippingForm.php
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
)
));
The problem lies with when you use the setValueOptions() and the setDisableInArrayValidator(). You should do this earlier within your code as it is never set before validating your form and so the inputfilter still contain the defaults as the InArray validator. As after validation, which checks the inputfilter, you set different options for the shipping_methods.
You should move the setValueOptions() and the setDisableInArrayValidator() before the $form->isValid(). Either by setting the right options within the form itsself or doing this in the controller. Best way is to keep all of the options in one place and doing it inside the form class.
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => [
'Ground' => 'Ground',
'Expedited' => 'Expedited'
],
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
Another small detail you might want to change is setting the value options. They are now hardcoded but your inputfilter is checking against database records whether they exist or not. Populate the value options with the database records. If the code still contains old methods but the database has a few new ones, they are not in sync.
class ShippingForm extends Form
{
private $dbAdapter;
public function __construct(AdapterInterface $dbAdapter, $name = 'shipping-form', $options = [])
{
parent::__construct($name, $options)
// inject the databaseAdapter into your form
$this->dbAdapter = $dbAdapter;
}
public function init()
{
// adding form elements to the form
// we use the init method to add form elements as from this point
// we also have access to custom form elements which the constructor doesn't
$this->add([
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => [
'value_options' => $this->getDbValueOptions(),
'disable_inarray_validator' => true,
'label' => 'Shipping Method',
'label_attributes' => [
'class' => 'lbl-shipping-method',
],
],
]);
}
private function getDbValueOptions()
{
$statement = $this->dbAdapter->query('SELECT shipping_method FROM shipping');
$rows = $statement->execute();
$valueOptions = [];
foreach ($rows as $row) {
$valueOptions[$row['shipping_method']] = $row['shipping_method'];
}
return $valueOptions;
}
}
Just had this happen yesterday.
The select and multi select ZF2+ elements have a built in in_array validator.
Remember filters occur before validators.
You may be doing too much here -- it is very rare to need to filter or add validators ot select and multi select form elements in ZF2 forms. The built in element validator is robust, ZF does a lot of work for us.
Try removing both filter and validator for the element, such as:
$inputFilter->add(array(
'name' => 'shipping-method',
'required' => true,
));
There is another edge case that I have seen: changing the select element's valueOptions somewhere in the controller (or view) resulting in different valueOptions used in view vs form validation (in our case it was replacing the element with a new one before validation).
I think your problem lies in the fact you are adding your value options after the InArray validator has been set, hence the validator has no haystack.
Try this
$this->add(array(
'name' => 'shipping-method',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'label' => 'Shipping Method',
'label_attributes' => array(
'class' => 'lbl-shipping-method'
),
'value_options' => array(
'Ground' => 'Ground',
'Expedited' => 'Expedited'
),
'disable_inarray_validator' => TRUE,
)
));
and remove setValueOptions and setDisableInArrayValidator from your view.
Hope this works.
I would like to display selection with options from DB. In this case, I am using EntityType with query_builder:
$builder
->add('internships', EntityType::class, array(
'class' => 'IndexBundle\Entity\Internship',
'property' => 'id',
'empty_value' => 'Choose',
'query_builder' => function (EntityRepository $er) use ($trainee) {
return $er->createQueryBuilder('i')
->where('i.trainee = :trainee')
->andWhere('i.state = :state')
->setParameter('trainee', $trainee)
->setParameter('state', 'unverified');
},
'constraints' => array(
new NotBlank(array(
'message' => 'choice.not.blank'
))
)
))
Now all is fine. I get select element with necessary options within with text of id value.
<select>
<option value="id">id</option>
...
</select>
How do I customize it?
For example I would like it to be combination of id and type table columns:
<select>
<option value="id">#id (type)</option>
...
</select>
You can use the choice_label option to customize your options.
You can either pass a function to retrieve the text you want, or you can add a getter to your entity if you reuse it at another place.
With a function:
$builder
->add('internships', EntityType::class, array(
'class' => 'IndexBundle\Entity\Internship',
'property' => 'id',
'empty_value' => 'Choose',
'query_builder' => function (EntityRepository $er) use ($trainee) {
return $er->createQueryBuilder('i')
->where('i.trainee = :trainee')
->andWhere('i.state = :state')
->setParameter('trainee', $trainee)
->setParameter('state', 'unverified');
},
'choice_label' => function ($internship) {
return '#'.$internship->getId().' ('.$internship->getType().')';
},
'constraints' => array(
new NotBlank(array(
'message' => 'choice.not.blank'
))
)
))
With a getter:
$builder
->add('internships', EntityType::class, array(
'class' => 'IndexBundle\Entity\Internship',
'property' => 'id',
'empty_value' => 'Choose',
'query_builder' => function (EntityRepository $er) use ($trainee) {
return $er->createQueryBuilder('i')
->where('i.trainee = :trainee')
->andWhere('i.state = :state')
->setParameter('trainee', $trainee)
->setParameter('state', 'unverified');
},
'choice_label' => 'idAndType',
'constraints' => array(
new NotBlank(array(
'message' => 'choice.not.blank'
))
)
))
Internship.php:
Class Internship
{
//...
public function getIdAndType()
{
return '#'.$this->id.' ('.$this->type.')';
}
}
Note:
For older Symfony versions (<= 2.6), this option was named property and use something supported by the PropertyAccessor component, so you can't use a function, only a getter for it.
I am migrating an application from Zend 1 to Zend 2 and starting to desperate with one issue. The application works with different locales and therefore, I need to store the data in a normalized way in the database. In Zend 1 I used this code:
public function normalizeNumber( $value )
{
// get the locale to change the date format
$this->_locale = Zend_Registry::get('Zend_Locale' );
return Zend_Locale_Format::getNumber($value, array('precision' => 2, 'locale' => $this->_locale));
}
Unfortunately Zend 2 does not has this Zend_Locale_Format::getNumber any more and I was not able to figure out what function did replace it. I have tried with NumberFormat, but I get only localized data not normalized. I need this function to normalize data I receive from a form via POST. Can someone give some advice?
thanks
Just to complete my question. The Form element definition I am using is the following:
namespace Profile\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class Profile Extends Form implements InputFilterProviderInterface
{
protected $model;
public function __construct( $model, $name = 'assignmentprofile')
{
parent::__construct( $name );
$this->setAttribute( 'method', 'post');
$this->model = $model;
...
$this->add( array(
'name' =>'CommutingRate',
'type' =>'Zend\Form\Element\Text',
'options' => array( // list of options to add to the element
'label' => 'Commuting rate to be charged:',
'pattern' => '/[0-9.,]/',
),
'attributes' => array( // Attributes to be passed to the HTML lement
'type' =>'text',
'required' => 'required',
'placeholder' => '',
),
));
}
public function getInputFilterSpecification()
{
return array(
...
'CommutingRate' => array(
'required' => true,
'filters' => array(
array( 'name' => 'StripTags', ),
array( 'name' => 'StringTrim'),
array( 'name' => 'NumberFormat', 'options' => array('locale' => 'en_US', 'style' => 'NumberFormatter::DECIMAL', 'type' => 'NumberFormatter::TYPE_DOUBLE',
))
),
'validators' => array(
array( 'name' => 'Float',
'options' => array( 'messages' => array('notFloat' => 'A valid numeric entry is required')),
),
),),
...
);
}
}
As mentioned before, I am able to localized the data and validate it in the localized manner, but i am failing to convert it back to a normalized manner...
i have tried various ways, but i cant get form to come back isValid() so i wanted to make sure i have this portion correct first.
<?php
namespace ABC\DeBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Route,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Template,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method,
Symfony\Component\HttpFoundation\Request,
Symfony\Component\HttpKernel\Exception\HttpException,
Symfony\Component\Form\FormBuilderInterface,
Symfony\Component\Form\Forms,
Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension,
Symfony\Component\Form\Extension\Validator\ValidatorExtension,
Symfony\Component\Validator\Validation,
Symfony\Component\Validator\Constraints\Length,
Symfony\Component\Validator\Constraints\NotBlank,
Symfony\Component\Config\Definition\Exception\Exception,
Symfony\Component\HttpFoundation\Response,
ABC\CoreBundle\Controller\CoreController;
class DoSomethingController extends CoreController
{
protected $debug = FALSE;
public function doSomethingAction(Request $request)
{
/**
* build the form here
*
* Either use out custom build form object
* $form = $formFactory->createBuilder('form', $defaultData)
*
* Or alternatively, use the default formBuilder
* $form = $this->createFormBuilder($defaultData)
*/
$validator = Validation::createValidator();
$formFactory = Forms::createFormFactoryBuilder()
->addExtension(new HttpFoundationExtension())
->addExtension(new ValidatorExtension($validator))
->getFormFactory();
$defaultData = array(
'comment' => 'Type your comment here',
'name' => 'Type your name here',
'resources' => '{}',
'warning' => 'Warning, your still in debug mode on your controller.php'
);
//$form = $this->createFormBuilder($defaultData)
$form = $formFactory->createBuilder('form', $defaultData)
->setAction($this->generateUrl('do_something'))
->add("emails", 'text', array(
'label' => "Recipient's email address (separate with a comma)",
'constraints' => array(// constraints here
),
))
->add('comment', 'textarea', array(
'label' => "Leave a comment",
))
->add('name', 'text', array(
'label' => "Your name",
'constraints' => array(// constraints here
),
))
->add('email', 'email', array(
'label' => "Your email address",
'constraints' => array(// constraints here
),
))
->add('copy', 'checkbox', array(
'label' => "Send me a copy",
'required' => false,
))
->add('cancel', 'button', array(
'label' => "Cancel",
'attr' => array('class' => 'btn-branded'),
))
->add('save', 'submit', array(
'label' => "Email Resources",
'attr' => array('class' => 'btn-branded'),
));
//make resources visible by putting them into a text box
$resourceInputType = $this->debug ? 'text' : 'hidden';
$form->add('resources', $resourceInputType, array(
'constraints' => array(// constraints here
),
));
//add a visible warning input box, so we know were on debug, we don't want this released to live on debug.
$this->debug ? ($form = $form->add('warning', 'text')) : null;
$form .= $form->getForm();
//alternatively
//$form->bind($_POST[$form->getName()]);
//$form->handleRequest($request);
//validate
if ($form->isValid()) {
$data = $form->getData();
//run some data checks, then fire it off
$something = $this->DoSomething($data);
return $something;
//$return=array("responseCode"=>200, "data"=>$data);
// $return=json_encode($return); //json encode the array
// return new Response($return,200,array('Content-Type'=>'application/json'));//make sure it has the correct content type
}else
{
echo '<pre>';
var_dump('form is coming back as not isValid(), make sure debug is off, heres our errors list:<br>');
var_dump($form->getErrorsAsString());
echo '</pre>';
}
//its not a POST, or POST is invalid, so display the form
return $this->render($this->someTemplate, array(
'form' => $form->createView(),
));
}
}
1) Im a bit confused (oop wise) on the way to pass around this form object during conditional manipulation. Do i need to concatenate it when manipulating it or what does symfony offer to avoid this, or am i just passing the object around wrong? see this example to understand better this question. e.g. $form .= $form->getForm(); How would i properly concatenate this form object during manipulation, like i did when i used the ternary, to convine myself that when i pass this object around, its teh same object, and not a new one.
2) see any other problems that might be causing the breakage?
EDIT #Chausser
this is the latest code, streamlined a bit, that i am working with now using some apparently rare exmples found here http://api.symfony.com/2.5/Symfony/Component/Form/Forms.html. its still not coming back isValid() . Please compare my former, and new usage example of the form object.
<?php
/**
* #param Request $request
* #Route("/do/something", name="do_something", options={"expose"=true})
* #Method({"GET", "POST"})
* #return mixed
*/
public function doSomethingAction(Request $request)
{
$defaultData = array(
'comment' => 'Type your comment here',
'name' => 'Type your name here',
'resources' => '{}',
'warning' => 'Warning, your still in debug mode on your controller.php'
);
$resourceInputType = $this->debug ? 'text' : 'hidden';
$formFactory = Forms::createFormFactory();
$form = $formFactory
->createBuilder()
->setAction($this->generateUrl('do_something'))
->add("emails", 'text', array(
'label' => "Recipient's email address (separate with a comma)",
'constraints' => array(// constraints here
),
))
->add('comment', 'textarea', array(
'label' => "Leave a comment",
))
->add('name', 'text', array(
'label' => "Your name",
'constraints' => array(// constraints here
),
))
->add('email', 'email', array(
'label' => "Your email address",
'constraints' => array(// constraints here
),
))
->add('copy', 'checkbox', array(
'label' => "Send me a copy",
'required' => false,
))
->add('cancel', 'button', array(
'label' => "Cancel",
'attr' => array('class' => 'btn-branded'),
))
->add('save', 'submit', array(
'label' => "Email Resources",
'attr' => array('class' => 'btn-branded'),
))
->add('resources', $resourceInputType, array(
'constraints' => array(// constraints here
),
))
->getForm();
$form->handleRequest($request);
//validate
if ($form->isValid()) {
$data = $form->getData();
//run some data checks, then fire it off
$something = $this->DoSomething($data);
return $something;
//other options for returning
//$return=array("responseCode"=>200, "data"=>$data);
// $return=json_encode($something); //json encode the array
// return new Response($return,200,array('Content-Type'=>'application/json'));//make sure it has the correct content type
} else {
echo '<pre>';
var_dump('form is coming back as not isValid(), make sure debug is off, heres our errors list:<br>');
var_dump($form->getErrorsAsString());
echo '</pre>';
}
//its not a POST, or POST is invalid, so display the form
return $this->render($this->someTemplate, array(
'form' => $form->createView(),
));
}
EDIT
so now after my edits to get the form to not try and haldne the request, unless its teh POST request, not the GET request
if($request && $request->getMethod() == 'POST'){
$form->handleRequest($request);
//validate
if ($form->isValid()) {
$data = $form->getData();
//run some data checks, then fire it off
$something = $this->DoSomething($data);
return $something;
//other options for returning
//$return=array("responseCode"=>200, "data"=>$data);
// $return=json_encode($something); //json encode the array
// return new Response($return,200,array('Content-Type'=>'application/json'));//make sure it has the correct content type
} else {
echo '<pre>';
var_dump('form is coming back as not isValid(), make sure debug is off, heres our errors list:<br>');
var_dump($form->getErrorsAsString());
echo '</pre>';
}
}
which has led to the controller error mentioned below
Ok, it looks as if the validator extension, when used, was breaking, since the HttpFoundationExtension() wasnt being included. Apparently to use either the validator, or the factory builder (not sure which) it needs this extension as well.
working example, copied from initial example, with working parts added.
<?php
namespace ABC\DeBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Route,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Template,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method,
Symfony\Component\HttpFoundation\Request,
Symfony\Component\HttpKernel\Exception\HttpException,
Symfony\Component\Form\FormBuilderInterface,
Symfony\Component\Form\Forms,
Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension,
Symfony\Component\Form\Extension\Validator\ValidatorExtension,
Symfony\Component\Validator\Validation,
Symfony\Component\Validator\Constraints\Length,
Symfony\Component\Validator\Constraints\NotBlank,
Symfony\Component\Config\Definition\Exception\Exception,
Symfony\Component\HttpFoundation\Response,
ABC\CoreBundle\Controller\CoreController;
class DoSomethingController extends CoreController
{
protected $debug = FALSE;
public function doSomethingAction(Request $request)
{
$validator = Validation::createValidator();
$builder = Forms::createFormFactoryBuilder()
->addExtension(new ValidatorExtension($validator))
->addExtension(new HttpFoundationExtension());
$defaultData = array(
'comment' => 'Type your comment here',
'name' => 'Type your name here',
'resources' => '{}',
'warning' => 'Warning, your still in debug mode on your controller.php'
);
$resourcesInputType = $this->debug ? 'text' : 'hidden';
$form = $builder->getFormFactory()
->createBuilder('form', $defaultData)
->setAction($this->generateUrl('do_something'))
->add("emails", 'text', array(
'label' => "Recipient's email address (separate with a comma)",
'constraints' => array(// constraints here
),
))
->add('comment', 'textarea', array(
'label' => "Leave a comment",
))
->add('name', 'text', array(
'label' => "Your name",
'constraints' => array(// constraints here
),
))
->add('email', 'email', array(
'label' => "Your email address",
'constraints' => array(// constraints here
),
))
->add('copy', 'checkbox', array(
'label' => "Send me a copy",
'required' => false,
))
->add('cancel', 'button', array(
'label' => "Cancel",
'attr' => array('class' => 'btn-branded'),
))
->add('save', 'submit', array(
'label' => "Email Resources",
'attr' => array('class' => 'btn-branded'),
))->add('resources', $resourcesInputType, array(
'constraints' => array( // constraints here
),
))
->getForm()
->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
//run some data checks, then fire it off
$something = $this->DoSomething($data);
return $something;
}
// initial render
return $this->render($this->someTemplate, array(
'form' => $form->createView(),
));
}
}
I'm trying to add two classes in a form. So I do for the second class:
foreach ($formOwnersDetails as $i => $details) {
$form = $form->add("member_{$i}_has_voting_denied", 'checkbox', array(
'data_class' => 'Pro\ConvocationBundle\Entity\OwnerDetails',
'property_path' => "votingDenied",
'label' => ' ',
'required' => false
));
but it gives me an error:
Neither property "votingDenied" nor method "getVotingDenied()" nor method "isVotingDenied()" exists in class "Pro\ConvocationBundle\Entity\Convocation"
Why is it looking for Pro\ConvocationBundle\Entity\Convocation when I'm adding data_class on the field definition?
I didn't check it but it should work:
Please check documentation for more information entity
$form = $form->add("member_{$i}_has_voting_denied", 'entity', array(
'class' => 'Pro\ConvocationBundle\Entity\OwnerDetails',
'property' => "votingDenied",
'label' => ' ',
'required' => false
));