Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save andrconstruction/b77046a8eef58665be7a23f0324a45ea to your computer and use it in GitHub Desktop.
Save andrconstruction/b77046a8eef58665be7a23f0324a45ea to your computer and use it in GitHub Desktop.
ZF2 Form Collection Validation - Unique
<?php
namespace Application\Entity
class Brand
{
/**
* @var string
\*/
protected $name;
/**
* @var string
\*/
protected $url;
/**
* @param string $name
* @return Brand
\*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
\*/
public function getName()
{
return $this->name;
}
/**
* @param string $url
* @return Brand
\*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @return string
\*/
public function getUrl()
{
return $this->url;
}
}
<?php
namespace Application\Entity
class Category
{
/**
* @var string
\*/
protected $name;
/**
* @param string $name
* @return Category
\*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
\*/
public function getName()
{
return $this->name;
}
}
<?php
namespace Application\Entity;
class Product
{
/**
* @var string
\*/
protected $name;
/**
* @var int
\*/
protected $price;
/**
* @var Brand
\*/
protected $brand;
/**
* @var array
\*/
protected $categories;
/**
* @param string $name
* @return Product
\*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
\*/
public function getName()
{
return $this->name;
}
/**
* @param int $price
* @return Product
\*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* @return int
\*/
public function getPrice()
{
return $this->price;
}
/**
* @param Brand $brand
* @return Product
\*/
public function setBrand(Brand $brand)
{
$this->brand = $brand;
return $this;
}
/**
* @return Brand
\*/
public function getBrand()
{
return $this->brand;
}
/**
* @param array $categories
* @return Product
\*/
public function setCategories(array $categories)
{
$this->categories = $categories;
return $this;
}
/**
* @return array
\*/
public function getCategories()
{
return $this->categories;
}
}
<?php
namespace Application\Form;
use Application\Entity\Brand;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class BrandFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('brand');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Brand());
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the brand'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'url',
'type' => 'Zend\Form\Element\Url',
'options' => array(
'label' => 'Website of the brand'
),
'attributes' => array(
'required' => 'required'
)
));
}
/**
* @return array
\*/
public function getInputFilterSpecification()
{
return array(
'name' => array(
'required' => true,
)
);
}
}
<?php
namespace Application\Form;
use Application\Entity\Brand;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class BrandFieldset extends Fieldset
{
public function __construct()
{
parent::__construct('brand');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Brand());
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the brand'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'url',
'type' => 'Zend\Form\Element\Url',
'options' => array(
'label' => 'Website of the brand'
),
'attributes' => array(
'required' => 'required'
)
));
}
}
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class BrandFieldsetFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'name',
'required' => true,
)));
}
}
<?php
namespace Application\Form;
use Application\Entity\Category;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class CategoryFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('category');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Category());
$this->setLabel('Category');
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the category'
),
'attributes' => array(
'required' => 'required'
)
));
}
/**
* @return array
\*/
public function getInputFilterSpecification()
{
return array(
'name' => array(
'required' => true,
)
);
}
}
<?php
namespace Application\Form;
use Application\Entity\Category;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class CategoryFieldset extends Fieldset
{
public function __construct()
{
parent::__construct('category');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Category());
$this->setLabel('Category');
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the category'
),
'attributes' => array(
'required' => 'required'
)
));
}
}
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class CategoryFieldsetFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'name',
'required' => true,
)));
}
}
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class CreateProduct extends Form
{
public function __construct()
{
parent::__construct('create_product');
$this->setAttribute('method', 'post')
->setHydrator(new ClassMethodsHydrator(false))
->setInputFilter(new InputFilter());
$this->add(array(
'type' => 'Application\Form\ProductFieldset',
'options' => array(
'use_as_base_fieldset' => true
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf'
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Send'
)
));
}
}
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class CreateProduct extends Form
{
public function __construct()
{
parent::__construct('create_product');
$this->setAttribute('method', 'post')
->setHydrator(new ClassMethodsHydrator(false))
->setInputFilter(new CreateProductFormFilter());
$this->add(array(
'type' => 'Application\Form\ProductFieldset',
'name' => 'product',
'options' => array(
'use_as_base_fieldset' => true
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf'
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Send'
)
));
}
}
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class CreateProductFormFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add(new ProductFieldsetFilter(), 'product');
}
}
<?php
namespace Application\Form;
use Application\Entity\Product;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class ProductFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('product');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Product());
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the product'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'price',
'options' => array(
'label' => 'Price of the product'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'type' => 'Application\Form\BrandFieldset',
'name' => 'brand',
'options' => array(
'label' => 'Brand of the product'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'categories',
'options' => array(
'label' => 'Please choose categories for this product',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Application\Form\CategoryFieldset'
)
)
));
}
/**
* Should return an array specification compatible with
* {@link Zend\InputFilter\Factory::createInputFilter()}.
*
* @return array
\*/
public function getInputFilterSpecification()
{
return array(
'name' => array(
'required' => true,
),
'price' => array(
'required' => true,
'validators' => array(
array(
'name' => 'Float'
)
)
)
);
}
}
<?php
namespace Application\Form;
use Application\Entity\Product;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class ProductFieldset extends Fieldset
{
public function __construct()
{
parent::__construct('product');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Product());
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Name of the product'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'price',
'options' => array(
'label' => 'Price of the product'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'type' => 'Application\Form\BrandFieldset',
'name' => 'brand',
'options' => array(
'label' => 'Brand of the product'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'categories',
'options' => array(
'label' => 'Please choose categories for this product',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Application\Form\CategoryFieldset'
)
)
));
}
}
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
class ProductFieldsetFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'name',
'required' => true,
)));
$this->add($factory->createInput(array(
'name' => 'price',
'required' => true,
'validators' => array(
array('name' => 'Float')
)
)));
}
}
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\CollectionInputFilter;
class ProductFieldsetFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'name',
'required' => true,
)));
$this->add($factory->createInput(array(
'name' => 'price',
'required' => true,
'validators' => array(
array('name' => 'Float')
)
)));
// Add the brand fieldset filter
$this->add(new BrandFieldsetFilter(), 'brand');
// Add the categories collection filter
$categoriesFilter = new CollectionInputFilter();
$categoriesFilter->setInputFilter(new CategoryFieldsetFilter());
$this->add($categoriesFilter, 'categories');
}
}
<?php
namespace Application\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Application\InputFilter\CollectionInputFilter;
class ProductFieldsetFilter extends InputFilter
{
public function __construct()
{
$factory = new InputFactory();
$this->add($factory->createInput(array(
'name' => 'name',
'required' => true,
)));
$this->add($factory->createInput(array(
'name' => 'price',
'required' => true,
'validators' => array(
array('name' => 'Float')
)
)));
// Add the brand fieldset filter
$this->add(new BrandFieldsetFilter(), 'brand');
// Add the categories collection filter
$categoriesFilter = new CollectionInputFilter();
$categoriesFilter->setInputFilter(new CategoryFieldsetFilter());
$categoriesFilter->setUniqueFields(array('name'));
$categoriesFilter->setMessage('Each category must be unique.');
$this->add($categoriesFilter, 'categories');
}
}
<?php
namespace Application\InputFilter;
use Traversable;
use Zend\InputFilter\CollectionInputFilter as ZendCollectionInputFilter;
class CollectionInputFilter extends ZendCollectionInputFilter
{
protected $uniqueFields;
protected $message;
const UNIQUE_MESSAGE = 'Each item must be unique within the collection';
/**
* @return the $message
*/
public function getMessage()
{
return $this->message;
}
/**
* @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)
{
// 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->collectionData 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->collectionInvalidInputs[$duplicate][$elementName] = array('unique' => $message);
}
}
}
return $valid;
}
}
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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment