问题
I have an issue in my code which I can't resolve. I'm using Zend framework 2.4 and I wrote a form but as soon as I validate it, I got the error The input was not found in the haystack. This are the 3 input where I got the error:
$this->add(array(
'name' => 'ACTIVITE1',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
),
'disable_inarray_validator' => false,
),
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session1'
)
));
$this->add(array(
'name' => 'ACTIVITE2',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
)
),
'disable_inarray_validator' => false,
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session2'
)
));
$this->add(array(
'name' => 'ACTIVITE3',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
)
),
'disable_inarray_validator' => false,
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session3'
)
));
I saw in other form that I should put 'disable_inarray_validator' => false
but this doesn't work too.
回答1:
Of course it's not working.
That message comes from the \Zend\Validator\InArray
and essentially, it means: "your user is doing something hacky with your select, pay attention".
An exemple would be a Select Preferred fruit
with two options, like "Banana" and "Ananas", but the user "hacks" the select and sends to the server the value "Audi". The InArray
validator is really important and shouldn't be disabled (well, only in a few exceptions..).
Now, why are you getting this error? The answer is... You didn't told what are the select options. You created a Select, but you didn't specified what are its options. You put the label/placeholder at the place of the options. A correct Select would be:
$this->add(array(
'name' => 'ACTIVITE1',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
1 => 'Fitness',
2 => 'Parcour',
3 => 'Velo',
4 => 'Tapis roulant',
// ... and so on
)
),
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session1',
'placeholder' => "Choisir l'activité"
)
));
What is "weird" is the fact that you are filling something in an empty select, and my question then is: why?
Selects are (typically) there for a predefined list of values. If you want to allow your users to fill a custom text, then you should consider to create a Text
field with the autocomplete option.
Edit: select with values from DB
If you want to create a select with a list of options that come from the database, the route is a bit more complex, but once you've learned how to do it, it will become way easier.
PAY ATTENTION: this will not be a "copy&paste solution". Since I'm not having access to your code, I'm making up names (classes, namespaces, methods, variables) just to create a complete example :)
First off, you must create a custom element. In this case, it will be a custom select:
namespace Yournamespace;
use Zend\Form\Element\Select;
use Yournamespace\ActivityMapper;
class ActivitySelect extends Select {
protected $activityMapper;
public function __construct(ActivityMapper $activityMapper, $name = null, $options = []) {
parent::__construct($name, $options);
$this->activityMapper = $activityMapper;
}
public function init() {
$valueOptions = [];
foreach ($this->activityMapper->fetchAll() as $activity) {
$valueOptions[$activity->getActivityId()] = $activity->getActivityName();
}
$this->setValueOptions($valueOptions);
}
}
What is really important here is that you must instantiate your element (options, classes, and so on..) inside init
method.
Since this element has a dependency (ActivityMapper
), you'll have to create a factory for this element:
namespace Yournamespace;
use Zend\ServiceManager\Factory\FactoryInterface;
use \Interop\Container\ContainerInterface;
class ActivitySelectFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null): object {
$activityMapper = $container->get(\Yournamespace\ActivityMapper::class);
return \Yournamespace\ActivitySelect($activityMapper);
}
}
This factory must be added in the configuration, more precisely, inside module.config.php
:
return [
// ...
'form_elements' => [
'factories' => [
\Yournamespace\ActivitySelect::class => \Yournamespace\ActivitySelectFactory::class,
]
]
// ...
];
Now, you must modify your form too. All elements must be added to form inside init
method, and not inside the constructor:
namespace Yournamespace;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class ActivityForm extends Form implements InputFilterProviderInterface {
public function init() {
parent::init();
// ...
$this->add([
'name' => 'ACTIVITE1',
'type' => \Yournamespace\ActivitySelect::class,
'attributes' => [
'class' => 'form-control',
'id' => 'select-session1',
'required' => true, // This will work only clientside, don't forget the inputfilter!
'placeholder' => 'Choisir l\'activité',
],
'options' => [
'label' => 'Choisir l\'activité',
]
]);
// ...
}
public function getInputFilterSpecification() {
// ...
$inputFilter[] = [
'name' => 'ACTIVITE1',
'required' => true
];
// ...
return $inputFilter;
}
}
Finally, you'll have to modify your controller too, because you need to retrieve the form from the FormElementManager
:
namespace Yournamespace;
use Zend\Form\FormElementManager;
class YourController extends AbstractActionController {
private $formManager;
public function __construct(FormElementManager $formManager) {
$this->formManager = $formManager;
}
public function choisirActiviteAction(){
// ...
$form = $this->formManager->get(\Yournamespace\ActivityForm::class);
// ...
}
}
A next nice step would be to create a controller plugin for the $formManager
, instead of making it as a dependency of each controller, but this is a different problem..
回答2:
thanks for your answer! I understand what you mean but i did this because i don't want to control the select because i can't control all the possible options.
Because the options are from a database, i get them doing an AJAX request and i add all the option to the select in the view. So the options can vary and are not fixed.
Here is my AJAX request :
$("#input-dtNaissance").change(function() {
$.when($(".activites-options").remove()).then(function() {
if ($("#input-dtNaissance").val() != " ") {
//on supprime les anciennes activités proposées au cas où il y en ai
var year = $("#input-dtNaissance").val().substring(0,4);
if (year == "") {
year = 1;
}
// si date supprimée/réinitialisée
if (year == 1) {
// on supprime les activités proposées du select
$(".activites-options").remove();
} else {
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession1/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "' class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session1").append(option);
});
});
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession2/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "'class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session2").append(option);
});
});
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession3/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "' class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session3").append(option);
});
});
}
}
});
})
I know that it's not very safety and if you have a better solution with more security i'm open to the propositions.
Good evening.
PS : Sorree faur my terribl anglish :)
来源:https://stackoverflow.com/questions/58991185/how-to-fix-the-input-was-not-found-in-the-haystack-in-zf2