如何修复 ZF2 中的 'The input was not found in the haystack'?

How to fix 'The input was not found in the haystack' in ZF2?

我的代码中有一个我无法解决的问题。我正在使用 Zend Framework 2.4 并编写了一个表单,但是一旦我验证它,我就收到错误 The input was not found in the haystack.这是我收到错误的 3 个输入:

 $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'
        )
    ));

我在其他表格中看到我应该输入 'disable_inarray_validator' => false 但这也不起作用。

当然不行了

该消息来自 \Zend\Validator\InArray,本质上,它的意思是:“您的用户正在使用您的 select 做一些骇人听闻的事情,注意 ”。

一个例子是 Select Preferred fruit 有两个选项,例如 "Banana" 和 "Ananas",但是用户 "hacks" select 并向服务器发送值 "Audi"。 InArray 验证器非常重要,不应该被禁用(好吧,只有少数例外......)。

现在,为什么会出现此错误?答案是......你没有告诉 select 选项是什么。您创建了一个 Select,但您没有指定它的选项是什么。您将 label/placeholder 放在选项的位置。正确的 Select 应该是:

$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é"
    )
));

什么是"weird",就是你在空select里填东西,我的问题是:为什么?

Selects(通常)用于预定义的值列表。如果您想让您的用户填写自定义文本,那么您应该考虑创建一个带有自动完成选项的 Text 字段。

编辑:select 使用 DB

中的值

如果你想创建一个 select 带有来自数据库的选项列表,路线有点复杂,但是一旦你学会了如何去做,它就会成为一种方式更容易。

注意:这不会是 "copy&paste solution"。由于我无法访问您的代码,因此我正在编写名称(类、名称空间、方法、变量)只是为了创建一个完整的示例:)

首先,您必须创建一个自定义元素。在这种情况下,它将是自定义 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);
    }

}

这里真正重要的是你必须在init方法中实例化你的元素(选项,类,等等......)。

由于此元素具有依赖项 (ActivityMapper),您必须为此元素创建一个工厂:

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);
    }

}

必须在配置中添加这个工厂,更准确地说,在module.config.php:

里面
return [
    // ...
    'form_elements' => [
        'factories' => [
            \Yournamespace\ActivitySelect::class => \Yournamespace\ActivitySelectFactory::class,
        ]
    ]
    // ...
];

现在,您也必须修改表单。所有元素都必须添加到 init 方法内部的表单中,而不是构造函数内部:

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;
    }

}

最后,您还必须修改控制器,因为您需要从 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);
        // ...
    }
}

下一个不错的步骤是为 $formManager 创建一个控制器插件,而不是将其作为每个控制器的依赖项,但这是一个不同的问题..

感谢您的回答!我明白你的意思,但我这样做是因为我不想控制 select 因为我无法控制所有可能的选项。

因为选项来自数据库,所以我让他们执行 AJAX 请求,然后将所有选项添加到视图中的 select。所以选项可以变化而不是固定的。

这是我的 AJAX 请求:

$("#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);
                        });
                    });
                }
            }
        });
    })

我知道这不是很安全,如果您有更好的解决方案和更高的安全性,我愿意接受这些提议。

晚上好。

PS : Sorree faur my terribl anglish :)