zf2表单集合验证-字段集中的唯一元素 [英] zf2 Form Collection Validation - Unique Elements in Fieldsets

查看:100
本文介绍了zf2表单集合验证-字段集中的唯一元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Zend表单集合中添加唯一元素. 我从 Aron Kerr 找到了这项出色的工作

I want to add unique Elements in a Zend Form Collection. I found this awesome work from Aron Kerr

我像Aron Kerr的示例中那样处理表单和字段集,并且工作正常.

I do the forms and fieldsets like in Aron Kerr´s Example and it works fine.

就我而言,我创建了一个表单以插入公司的商店集合.

In my case i create a Form to insert a collection of stores from a company.

我的表单

首先,我有一个 Application \ Form \ CompanyStoreForm ,其StoreFieldset如下所示:

First of all i have a Application\Form\CompanyStoreForm with a StoreFieldset like this:

$this->add(array(
                'name' => 'company',
                'type' => 'Application\Form\Stores\CompanyStoresFieldset',
            ));

字段集

Application \ Form \ Stores \ CompanyStoresFieldset 具有这样的商店实体集合:

Application\Form\Stores\CompanyStoresFieldset has a Collection of Store Entities like this:

$this->add(array(
            'type' => 'Zend\Form\Element\Collection',
            'name' => 'stores',
            'options' => array(
                'target_element' => array(
                    'type' => 'Application\Form\Fieldset\StoreEntityFieldset',
                ),
            ),
        ));

Application \ Form \ Fieldset \ StoreEntityFieldset

$this->add(array(
            'name' => 'storeName',
            'attributes' => ...,
            'options' => ...,
        ));

        //AddressFieldset
        $this->add(array(
            'name' => 'address',
            'type' => 'Application\Form\Fieldset\AddressFieldset',
        ));

与Arron Kerrs CategoryFieldset 的不同之处在于,我又添加了一个字段集: Application \ Form \ Fieldset \ AddressFieldset

The difference to Arron Kerrs CategoryFieldset is I adding one more fieldset: Application\Form\Fieldset\AddressFieldset

Application \ Form \ Fieldset \ AddressFieldset 具有文本元素 streetName .

输入过滤器

CompanyStoresFieldsetInputFilter 没有要验证的元素.

StoreEntityFieldsetInputFilter 具有用于 storeName Application \ Form \ Fieldset \ AddressFieldset 的验证器,例如

The StoreEntityFieldsetInputFilter has validators for storeName and the Application\Form\Fieldset\AddressFieldset like this

public function __construct() {
        $factory = new InputFactory(); 

        $this->add($factory->createInput([ 
            'name' => 'storeName', 
            'required' => true, 
            'filters' => array( ....
            ),
            'validators' => array(...
            ),
        ]));

        $this->add(new AddressFieldsetInputFilter(), 'address');

    }

AddressFieldset 具有另一个Inputfilter AddressFieldsetInputFilter . 在 AddressFieldsetInputFilter 中,我为 streetName 添加了InputFilter.

The AddressFieldset has another Inputfilter AddressFieldsetInputFilter. In AddressFieldsetInputFilter I adding a InputFilter for streetName.

FormFactory

像这样将所有Inputfilters添加到表单中

Adding all Inputfilters to the Form like this

    public function createService(ServiceLocatorInterface $serviceLocator) {
            $form = $serviceLocator->get('FormElementManager')->get('Application\Form\CompanyStoreForm');

            //Create a Form Inputfilter
            $formFilter = new InputFilter();

            //Create Inputfilter for CompanyStoresFieldsetInputFilter()
            $formFilter->add(new CompanyStoresFieldsetInputFilter(), 'company');

            //Create Inputfilter for StoreEntityFieldsetInputFilter()
            $storeInputFilter = new CollectionInputFilter();
            $storeInputFilter->setInputFilter(new StoreEntityFieldsetInputFilter());
            $storeInputFilter->setUniqueFields(array('storeName'));
            $storeInputFilter->setMessage('Just insert one entry with this store name.');
            $formFilter->get('company')->add($storeInputFilter, 'stores');


            $form->setInputFilter($formFilter);


            return $form;
        }

我使用Aron Kerrs CollectionInputFilter.

I use Aron Kerrs CollectionInputFilter.

storeName 在整个集合中应该是唯一的. 到目前为止一切正常!

The storeName should be unique in the whole collection. All works fine, so far!

但是现在我的问题了!

街道名称在整个集合中应该是唯一的. 但是元素在AddressFieldset中. 我不能做这样的事情:

The streetName should be unique in the whole collection. But the Element is in the AddressFieldset. I can´t do something like this:

$storeInputFilter->setUniqueFields(array('storeName', 'address' => array('streetName')));

我认为我应该从 CollectionInputFilter

Aron Kerrs原始isValid()

public function isValid()
{
    $valid = parent::isValid();

// Check that any fields set to unique are unique
if($this->uniqueFields)
{
    // for each of the unique fields specified spin through the collection rows and grab the values of the elements specified as unique.
    foreach($this->uniqueFields as $k => $elementName)
    {
        $validationValues = array();
        foreach($this->collectionValues as $rowKey => $rowValue)
        {
            // Check if the row has a deleted element and if it is set to 1. If it is don't validate this row.
            if(array_key_exists('deleted', $rowValue) && $rowValue['deleted'] == 1) continue;

            $validationValues[] = $rowValue[$elementName];
        }


        // Get only the unique values and then check if the count of unique values differs from the total count
        $uniqueValues = array_unique($validationValues);
        if(count($uniqueValues) < count($validationValues))
        {            
            // The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
            $duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
            $valid = false;
            $message = ($this->getMessage()) ? $this->getMessage() : $this::UNIQUE_MESSAGE;
            foreach($duplicates as $duplicate)
            {
                $this->invalidInputs[$duplicate][$elementName] = array('unique' => $message);
            }
        }
    }

    return $valid;
}
}

首先,我尝试(仅用于测试)在集合的第一项中向streetName添加错误消息.

First of all I try (just for testing) to add a error message to streetName in the first entry of the collection.

$this->invalidInputs[0]['address']['streetName'] = array('unique' => $message);

但是它没有用.

将其添加到storeName即可

Adding it to storeName it works

$this->invalidInputs[0]['storeName'] = array('unique' => $message);

我认为原因是Fieldset具有自己的InputFilter()?

I think the reason is the Fieldset has an own InputFilter()?

当我执行 var_dump($ this-> collectionValues())时,我收到了所有值(以及addressFieldset)的多维数组. 没关系!但是我无法将错误消息添加到字段集中的元素.

When i do a var_dump($this->collectionValues()) i received a multidimensional array of all values (also of the addressFieldset). That´s fine! But i can´t add error messages to the element in the fieldset.

我该怎么做? 我不想在StoreEntityFieldset中插入AddressFieldset的所有元素. (我也以其他形式使用AddressFieldset)

How can i do this? I don´t want to insert all Elements of the AddressFieldset in the StoreEntityFieldset. (I use the AddressFieldset also in other Forms)

推荐答案

我知道了.您只需使用

$this->invalidInputs[<entry-key>]['address']['streetName'] = array('unique' => $message);

我不知道昨天怎么不行.这是另一个错误.

I don´t know how it does not work yesterday. It was another bug.

我为我的问题写了一个解决方案.也许这不是最好的,但是对我有用.

I wrote a solution for my problem. Maybe it´s not the best, but it works for me.

CollectionInputFilter

class CollectionInputFilter extends ZendCollectionInputFilter
{    
    protected $uniqueFields;
    protected $validationValues = array();
    protected $message = array();

    const UNIQUE_MESSAGE = 'Each item must be unique within the collection';

    /**
     * @return the $message
     */
    public function getMessageByElement($elementName, $fieldset = null)
    {
        if($fieldset != null){
            return $this->message[$fieldset][$elementName];
        }
        return $this->message[$elementName];
    }

    /**
     * @param field_type $message
     */
    public function setMessage($message)
    {
        $this->message = $message;
    }

    /**
     * @return the $uniqueFields
     */
    public function getUniqueFields()
    {
        return $this->uniqueFields;
    }

 /**
     * @param multitype:string  $uniqueFields
     */
    public function setUniqueFields($uniqueFields)
    {
        $this->uniqueFields = $uniqueFields;
    }

    public function isValid()
    {
        $valid = parent::isValid();

        // Check that any fields set to unique are unique
        if($this->uniqueFields)
        {
            foreach($this->uniqueFields as $key => $elementOrFieldset){
                // if the $elementOrFieldset is a fieldset, $key is our fieldset name, $elementOrFieldset is our collection of elements we have to check
                if(is_array($elementOrFieldset) && !is_numeric($key)){
                    // We need to validate every element in the fieldset that should be unique
                    foreach($elementOrFieldset as $elementKey => $elementName){
                        // $key is our fieldset key, $elementName is the name of our element that should be unique
                        $validationValues = $this->getValidCollectionValues($elementName, $key);

                        // get just unique values
                        $uniqueValues = array_unique($validationValues);

                        //If we have a difference, not all are unique
                        if(count($uniqueValues) < count($validationValues))
                        {
                            // The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
                            $duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
                            $valid = false;
                            $message = ($this->getMessageByElement($elementName, $key)) ? $this->getMessageByElement($elementName, $key) : $this::UNIQUE_MESSAGE;
                            // set error messages
                            foreach($duplicates as $duplicate)
                            {
                                //$duplicate = our collection entry key, $key is our fieldsetname
                                $this->invalidInputs[$duplicate][$key][$elementName] = array('unique' => $message);
                            }
                        }
                    }
                }
                //its just a element in our collection, $elementOrFieldset is a simple element
                else {
                    // in this case $key is our element key , we don´t need the second param because we haven´t a fieldset
                    $validationValues = $this->getValidCollectionValues($elementOrFieldset);

                    $uniqueValues = array_unique($validationValues);
                    if(count($uniqueValues) < count($validationValues))
                    {            
                        // The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
                        $duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
                        $valid = false;
                        $message = ($this->getMessageByElement($elementOrFieldset)) ? $this->getMessageByElement($elementOrFieldset) : $this::UNIQUE_MESSAGE;
                        foreach($duplicates as $duplicate)
                        {
                            $this->invalidInputs[$duplicate][$elementOrFieldset] = array('unique' => $message);
                        }
                    }
                }
            }

        }
        return $valid;
    }

    /**
     * 
     * @param type $elementName
     * @param type $fieldset
     * @return type
     */
    public function getValidCollectionValues($elementName, $fieldset = null){
        $validationValues = array();
        foreach($this->collectionValues as $rowKey => $collection){
            // If our values are in a fieldset
            if($fieldset != null && is_array($collection[$fieldset])){
                $rowValue = $collection[$fieldset][$elementName];
            }
            else{
                //collection is one element like $key => $value
                $rowValue = $collection[$elementName];
            }
            // Check if the row has a deleted element and if it is set to 1. If it is don't validate this row.
            if($rowValue == 1 && $rowKey == 'deleted') continue;
            $validationValues[$rowKey] = $rowValue;
        }
        return $validationValues;
    }


    public function getMessages()
    {
        $messages = array();
        if (is_array($this->getInvalidInput()) || $this->getInvalidInput() instanceof Traversable) {
            foreach ($this->getInvalidInput() as $key => $inputs) {
                foreach ($inputs as $name => $input) {
                    if(!is_string($input) && !is_array($input))
                    {
                        $messages[$key][$name] = $input->getMessages();                                                
                        continue;
                    }         
                    $messages[$key][$name] = $input;
                }
            }
        }
        return $messages;
    }
}

在工厂中定义CollectionInputFilter

Define a CollectionInputFilter (in a factory)

$storeInputFilter = new CollectionInputFilter();
        $storeInputFilter->setInputFilter(new StoreEntityFieldsetInputFilter());
        $storeInputFilter->setUniqueFields(array('storeName', 'address' => array('streetName')));
        $storeInputFilter->setMessage(array('storeName' => 'Just insert one entry with this store name.', 'address' => array('streetName' => 'You already insert a store with this street name')));
        $formFilter->get('company')->add($storeInputFilter, 'stores');

所以让我解释一下:

现在,我们可以在集合中的字段集中将元素添加为唯一元素. 我们不能在集合中添加集合字段集,而不能在我们的字段集中添加另一个字段集. 我认为,如果有人想这样做,最好将其重构为:-)

Now, we can add elements as unique in fieldsets in our collection. We can NOT add collection fieldsets in our collection and not another fieldsets in our fieldsets. In my opinion if anyone want to do this cases, they better should refactor the form :-)

setUniqueFields 将一个简单元素添加为唯一

setUniqueFields Add a simple element as unique

array('your-unique-element','another-element'); 

如果要在字段集中将元素添加为唯一

If you want to add a element as unique in a fieldset

array('your-unique-element', 'fieldsetname' => array('your-unique-element-in-fieldset'))

我们可以使用 setMessage

We can add special messages for every element with setMessage

为集合中的元素添加消息

Add Message for a Element in the collection

array('storeName' => 'Just insert one entry...')

为字段集中的元素添加消息

Add message for a Element in a fieldset

array('fieldset-name' => array('your-unique-element-in-fieldset' => 'You already insert ..'))

这篇关于zf2表单集合验证-字段集中的唯一元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆