Last active
May 1, 2019 12:28
-
-
Save marcguyer/02371bcb00bba22346dd52e309ad1450 to your computer and use it in GitHub Desktop.
Zend Framework Input Validation with Dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace ApiTest\InputFilter; | |
use PHPUnit\Framework\TestCase; | |
use Zend\InputFilter\InputFilter; | |
/** | |
* {@inheritdoc} | |
*/ | |
abstract class AbstractInputFilterTest extends TestCase | |
{ | |
/** | |
* Data provider for testValidation method | |
* @return array | |
*/ | |
abstract public function validationDataProvider(): array; | |
/** | |
* Get the subject input filter | |
* | |
* @return InputFilter | |
*/ | |
abstract public function getInputFilter(): InputFilter; | |
/** | |
* @dataProvider validationDataProvider | |
* Covers all public methods of @coversDefaultClass set in extension | |
* @covers ::<public> | |
* | |
* @param array $data | |
* @param bool $expectedIsValid | |
* @param array $expectedMessages | |
*/ | |
public function testValidation( | |
array $data, | |
bool $expectedIsValid = true, | |
array $expectedMessages = [] | |
): void { | |
$inputFilter = $this->getInputFilter(); | |
$inputFilter->init(); | |
$inputFilter->setData($data); | |
$this->assertSame( | |
$expectedIsValid, | |
$inputFilter->isValid(), | |
'Messages: ' . json_encode($inputFilter->getMessages()) . | |
' Values: ' . json_encode($inputFilter->getValues()) | |
); | |
if (!$expectedIsValid) { | |
$this->assertValidationMessages( | |
$inputFilter->getMessages(), | |
$expectedMessages, | |
$inputFilter->getValues() | |
); | |
} | |
} | |
/** | |
* @param array $messages | |
* @param array $expectedMessages | |
* @param array $values | |
*/ | |
private function assertValidationMessages( | |
array $messages, | |
array $expectedMessages, | |
array $values | |
): void { | |
foreach ($expectedMessages as $param => $messageKey) { | |
$this->assertArrayHasKey( | |
$param, | |
$messages, | |
'Messages: ' . json_encode($messages) . | |
' Values: ' . json_encode($values) | |
); | |
if (is_array($messageKey)) { | |
$this->assertValidationMessages( | |
$messageKey, | |
$messagers[$param] | |
); | |
continue; | |
} | |
$this->assertArrayHasKey( | |
$messageKey, | |
$messages[$param], | |
'Messages: ' . json_encode($messages) . | |
' Values: ' . json_encode($values) | |
); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Api\InputFilter; | |
use Api\Validator\CustomerUniqueCodeValidator; | |
use Zend\InputFilter\InputFilter; | |
use Zend\Filter\StringTrim; | |
use Zend\Validator\EmailAddress; | |
use Zend\Validator\StringLength; | |
use Utility\Interfaces\ProductAwareInterface; | |
use Utility\Traits\ProductAwareTrait; | |
use Data\Entity\Product; | |
/** | |
* Input filter and validator for customer. | |
*/ | |
class CustomerInputFilter extends InputFilter implements ProductAwareInterface | |
{ | |
use ProductAwareTrait; | |
/** | |
* Set Product, then add that product to any dependent input validators. | |
* | |
* @param Product $product | |
* | |
* @return mixed | |
*/ | |
public function setProduct(Product $product) | |
{ | |
$this->product = $product; | |
foreach ($this->getInputs() as $i) { | |
foreach ($i->getValidatorChain()->getValidators() as $v) { | |
if ($v['instance'] instanceof ProductAwareInterface) { | |
$v['instance']->setProduct($product); | |
} | |
} | |
foreach ($i->getFilterChain()->getFilters() as $f) { | |
if ($f instanceof ProductAwareInterface) { | |
$f->setProduct($product); | |
} | |
} | |
} | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function init() | |
{ | |
$this->add([ | |
'name' => 'code', | |
'required' => true, | |
'filters' => [['name' => StringTrim::class]], | |
'validators' => [ | |
[ | |
'name' => StringLength::class, 'options' => ['max' => 255], | |
], [ | |
'name' => CustomerUniqueCodeValidator::class, | |
], | |
], | |
]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace ApiTest\InputFilter; | |
use Api\InputFilter\CustomerInputFilter; | |
use Data\Entity; | |
use Zend\InputFilter\InputFilter; | |
/** | |
* {@inheritdoc} | |
* | |
* @coversDefaultClass \Api\InputFilter\CustomerInputFilter | |
* @uses Api\Validator\CustomerUniqueCodeValidator | |
*/ | |
class CustomerInputFilterTest extends AbstractInputFilterTest | |
{ | |
/** | |
* @inheritDoc | |
*/ | |
public function validationDataProvider(): array | |
{ | |
return [ | |
[[], false, ['code' => 'isEmpty'],], | |
[['code' => 'something']], | |
]; | |
} | |
public function getInputFilter(): InputFilter { | |
$inputFilter = new CustomerInputFilter(); | |
$product = $this->prophesize(Entity\Product::class); | |
$product->getUnderscoreId()->willReturn(123); | |
$inputFilter->setProduct($product->reveal()); | |
return $inputFilter; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Api\Validator; | |
use Data\Repository\CustomerRepository; | |
use Zend\Validator\AbstractValidator; | |
use Utility\Interfaces\ProductAwareInterface; | |
use Utility\Traits\ProductAwareTrait; | |
/** | |
* Validator to enforce unique code within a product. | |
*/ | |
class CustomerUniqueCodeValidator extends AbstractValidator implements ProductAwareInterface | |
{ | |
use ProductAwareTrait; | |
/** | |
* @var string | |
*/ | |
const NOT_UNIQUE = 'notUnique'; | |
/** | |
* @var string | |
*/ | |
const MISSING_PRODUCT = 'missingProduct'; | |
/** | |
* @var array | |
*/ | |
protected $messageTemplates = [ | |
self::NOT_UNIQUE => 'A customer already exists with code=%value%', | |
self::MISSING_PRODUCT => 'Missing product', | |
]; | |
/** | |
* @var CustomerRepository | |
*/ | |
private $customerRepository; | |
public function __construct(CustomerRepository $customerRepository) | |
{ | |
parent::__construct(); | |
$this->customerRepository = $customerRepository; | |
} | |
public function isValid($value) | |
{ | |
$this->setValue($value); | |
if (null === $this->getProduct()) { | |
$this->error(self::MISSING_PRODUCT); | |
return false; | |
} | |
$customer = $this->customerRepository | |
->findOneByCode($value, $this->getProduct()); | |
if (null === $customer) { | |
return true; | |
} | |
$this->error(self::NOT_UNIQUE); | |
return false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Api\Validator\Factory; | |
use Data\Entity\Customer; | |
use Api\Validator\CustomerUniqueCodeValidator; | |
use Doctrine\ORM\EntityManagerInterface; | |
use Psr\Container\ContainerInterface; | |
/** | |
* CustomerUniqueCodeValidatorFactory. | |
*/ | |
class CustomerUniqueCodeValidatorFactory | |
{ | |
/** | |
* @param ContainerInterface $container | |
* | |
* @return CustomerUniqueCodeValidator | |
*/ | |
public function __invoke(ContainerInterface $container): CustomerUniqueCodeValidator | |
{ | |
return new CustomerUniqueCodeValidator( | |
$container->get(EntityManagerInterface::class) | |
->getRepository(Customer::class) | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Utility\Interfaces; | |
use Data\Entity\Product; | |
/** | |
* ProductAwareInterface | |
*/ | |
interface ProductAwareInterface | |
{ | |
/** | |
* Set input filter | |
* | |
* @param Product $product | |
* @return ProductAwareInterface | |
*/ | |
public function setProduct(Product $product); | |
/** | |
* Retrieve input filter | |
* | |
* @return Product | |
*/ | |
public function getProduct(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Utility\Traits; | |
use Data\Entity\Product; | |
/** | |
* ProductAwareTrait | |
*/ | |
trait ProductAwareTrait | |
{ | |
/** | |
* @var Product | |
*/ | |
private $product = null; | |
/** | |
* Set Product | |
* | |
* @param Product $product | |
* @return mixed | |
*/ | |
public function setProduct(Product $product) | |
{ | |
$this->product = $product; | |
return $this; | |
} | |
/** | |
* Retrieve product | |
* | |
* @return Product | |
*/ | |
public function getProduct() | |
{ | |
return $this->product; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment