我想在Zend表单集合中添加唯一元素。 我从Aron Kerr
找到了这个很棒的作品我在Aron Kerr的例子中执行表单和字段集,它工作正常。
在我的情况下,我创建一个表格来插入公司的商店集合。
我的表格
首先,我有一个带有StoreFieldset的 Application \ Form \ CompanyStoreForm ,如下所示:
$this->add(array(
'name' => 'company',
'type' => 'Application\Form\Stores\CompanyStoresFieldset',
));
字段集
Application \ Form \ Stores \ CompanyStoresFieldset 有一个商店实体集合,如下所示:
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'stores',
'options' => array(
'target_element' => array(
'type' => 'Application\Form\Fieldset\StoreEntityFieldset',
),
),
));
应用程序\表格\字段集\ 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
Application \ Form \ Fieldset \ AddressFieldset 有一个text-element streetName 。
输入过滤器
CompanyStoresFieldsetInputFilter 没有要验证的元素。
StoreEntityFieldsetInputFilter 具有 storeName 和 Application \ Form \ Fieldset \ AddressFieldset 的验证器,如下所示
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。
FormFactory
将所有输入过滤器添加到表单中
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。
storeName 在整个集合中应该是唯一的。 到目前为止,一切正常!
但是现在我的问题了!
streetName 在整个集合中应该是唯一的。 但是Element在AddressFieldset中。 我不能做这样的事情:
$storeInputFilter->setUniqueFields(array('storeName', 'address' => array('streetName')));
我认为我应该从 CollectionInputFilter 扩展Aron Kerrs isValid()
Aron Kerrs Original 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添加错误消息。
$this->invalidInputs[0]['address']['streetName'] = array('unique' => $message);
但它没有用。
将其添加到storeName可以正常工作
$this->invalidInputs[0]['storeName'] = array('unique' => $message);
我认为原因是Fieldset有自己的InputFilter()?
当我执行 var_dump($ this-&gt; collectionValues())时,我收到了所有值的多维数组(也是addressFieldset的数组)。 没关系!但我无法将错误消息添加到字段集中的元素。
我该怎么做? 我不想在StoreEntityFieldset中插入AddressFieldset的所有元素。 (我也在其他表格中使用AddressFieldset)
答案 0 :(得分:1)
我明白了。您只需使用
添加值即可$this->invalidInputs[<entry-key>]['address']['streetName'] = array('unique' => $message);
我不知道昨天怎么行不通。这是另一个错误。
我为我的问题写了一个解决方案。也许这不是最好的,但它对我有用。
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(在工厂中)
$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');
让我解释一下:
现在,我们可以在我们的集合中的字段集中添加唯一元素。 我们不能在我们的集合中添加集合字段集,也不能在我们的字段集中添加其他字段集。 在我看来,如果有人想要做这种情况,他们最好重构一下: - )
<强> setUniqueFields 添加一个简单的元素作为唯一
array('your-unique-element','another-element');
如果要在字段集中添加唯一元素
array('your-unique-element', 'fieldsetname' => array('your-unique-element-in-fieldset'))
我们可以使用 setMessage
为每个元素添加特殊消息为集合中的元素添加消息
array('storeName' => 'Just insert one entry...')
为字段集中的元素添加消息
array('fieldset-name' => array('your-unique-element-in-fieldset' => 'You already insert ..'))