Unit Test for FormErrorSerializer in Symfony 4 - always valid form - forms

I am trying to write a unit test for FormErrorSerializer that converts Symfony $form->getErrors() to a readable array.
My current approach is to create the form, give it data, and look for validation errors, but form is always valid. I don't get any errors no matter what data I provide to form.
In normal REST request/response it is working well and I am getting appropriate error message. I need help with getting the error messages in unit test.
namespace App\Tests\Unit;
use App\Form\UserType;
use App\Serializer\FormErrorSerializer;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Translation\Translator;
class FormErrorSerializerTest extends TypeTestCase
{
/**
* ValidatorExtensionTrait needed for invalid_options
* https://github.com/symfony/symfony/issues/22593
*/
use ValidatorExtensionTrait;
public function testConvertFormToArray(){
$form_data = [
'email' => 'test',
'plainPassword' => [
'pass' => '1',
'pass2' => '2'
]
];
$translator = new Translator('de');
$form = $this->factory->create(UserType::class);
$form->submit($form_data);
if( $form->isValid() ) {
echo "Form is valid"; exit;
}
$formErrorSerializer = new FormErrorSerializer($translator);
$errors = $formErrorSerializer->convertFormToArray($form);
print_r($errors); exit;
}
}
Find below the Serializer:
namespace App\Serializer;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
/**
* Serializes invalid Form instances.
*/
class FormErrorSerializer
{
private $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function convertFormToArray(FormInterface $data)
{
$form = $errors = [];
foreach ($data->getErrors() as $error) {
$errors[] = $this->getErrorMessage($error);
}
if ($errors) {
$form['errors'] = $errors;
}
$children = [];
foreach ($data->all() as $child) {
if ($child instanceof FormInterface) {
$children[$child->getName()] = $this->convertFormToArray($child);
}
}
if ($children) {
$form['children'] = $children;
}
return $form;
}
private function getErrorMessage(FormError $error)
{
if (null !== $error->getMessagePluralization()) {
return $this->translator->transChoice(
$error->getMessageTemplate(),
$error->getMessagePluralization(),
$error->getMessageParameters(),
'validators'
);
}
return $this->translator->trans($error->getMessageTemplate(), $error->getMessageParameters(), 'validators');
}
}

Ok, I was able to do this in 2 different ways.
First solution was to load the validator in getExtensions method. The factory in TypeTestCase doesn't bring the validator with it. So, not only you have to load the validator but you also have to explicitly specify the validations. You can specify validation using methods provided by symfony or you can directly point validator to the YAML or xml file if you are using one.
public function getExtensions()
{
$validator = (new ValidatorBuilder())
->addYamlMapping("path_to_validations.yaml")
->setConstraintValidatorFactory(new ConstraintValidatorFactory())
->getValidator();
$extensions[] = new CoreExtension();
$extensions[] = new ValidatorExtension($validator);
return $extensions;
}
However, I didn't use the above approach. I went with even better solution. Due to high complexity of my test case (as it needed multiple services), I went with a special container provided by Symfony's KernelTestCase. It provides private services in tests, and the factory it provides comes with validator and validations, just like you code in controller. You do not need to load validator explicitly. Find below my final test that extends KernelTestCase.
namespace App\Tests\Unit\Serializer;
use App\Entity\User;
use App\Form\UserType;
use App\Serializer\FormErrorSerializer;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Translation\TranslatorInterface;
class FormErrorSerializerTest extends KernelTestCase
{
/**
* {#inheritDoc}
*/
protected function setUp()
{
$kernel = self::bootKernel();
}
public function testConvertFormToArray_invalidData(){
$form_data = [
'email' => 'test',
'plainPassword' => [
'pass' => '1111',
'pass2' => ''
]
];
$user = new User();
$user->setEmail($form_data['email']);
$user->setPlainPassword($form_data['plainPassword']['pass']);
$factory = self::$container->get(FormFactoryInterface::class);
/**
* #var FormInterface $form
*/
$form = $factory->create(UserType::class, $user);
$form->submit($form_data);
$this->assertTrue($form->isSubmitted());
$this->assertFalse($form->isValid());
$translator = self::$container->get(TranslatorInterface::class);
$formErrorSerializer = new FormErrorSerializer($translator);
$errors = $formErrorSerializer->convertFormToArray($form);
$this->assertArrayHasKey('errors', $errors['children']['email']);
$this->assertArrayHasKey('errors', $errors['children']['plainPassword']['children']['pass']);
}
public function testConvertFormToArray_validData(){
$form_data = [
'email' => 'test#example.com',
'plainPassword' => [
'pass' => 'somepassword#slkd12',
'pass2' => 'somepassword#slkd12'
]
];
$user = new User();
$user->setEmail($form_data['email']);
$user->setPlainPassword($form_data['plainPassword']['pass']);
$factory = self::$container->get(FormFactoryInterface::class);
/**
* #var FormInterface $form
*/
$form = $factory->create(UserType::class, $user);
$form->submit($form_data);
$this->assertTrue($form->isSubmitted());
$this->assertTrue($form->isValid());
$translator = self::$container->get(TranslatorInterface::class);
$formErrorSerializer = new FormErrorSerializer($translator);
$errors = $formErrorSerializer->convertFormToArray($form);
$this->assertArrayNotHasKey('errors', $errors['children']['email']);
$this->assertArrayNotHasKey('errors', $errors['children']['plainPassword']['children']['pass']);
}
}
Please note that Symfony 4.1 has a special container that allows fetching private services.
self::$kernel->getContainer(); is not special container. It will not fetch private services.
However, self::$container; is special container that provides private services in testing.
More about this here.

Related

Symfony ValidatorComponent > AnnotationMapping in FormComponent

Im working on a project where I'm using some Symfony Components. My problem is how to make the Form Component's validation of Forms use AnnotationMapping to find the constraints.
SetUp:
global $loader; //composer - autoload
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
$formFactory = Forms::createFormFactoryBuilder()
[...]
->addExtension(new ValidatorExtension($validator))
->getFormFactory();
Entity
/**
* #ORM\Entity
* #ORM\Table(name="..")
*/
class Conductor extends AbstractEntity {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #Assert\NotBlank()
* #ORM\Column(type="string")
*/
protected $pattern;
[...]
}
Building the Form
$builder = $App->getFormFactory()->createBuilder(FormType::class, $entity_data);
foreach ($fields as $field) {
$builder->add(
$field,
null,
[
"attr" => array("class" => "..."),
]
);
}
$builder->getForm();
FormSubmit / Validation
if($request->isMethod('POST')) {
$formTable = $this->createFormTable( array() );
$form = $formTable->buildForm($entity);
$form->submit($this->dataMapper->formDataFromPost());
/*
$entity = $this->dataMapper->mapFromPost();
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
*/
if($form->isValid()) {
[...]
} else {
[...]
}
}
Im trying to make the NotBlank() Constraints work. But my form passes the validation in any case. If I use a new validator and validate with it, it will show me the correct Errors. But the Form->isValid() function does not. Maybe it is not configured correctly to use AnnotationMapping? Thank you very much in advance for tipps or solutions!
Problem localization
The form handleRequest / submit and validation are working as expected!
The form does not have any constraints!!
-> Mapping the Constraints from Annotation is not happening / working.
I did find a similar question: Why does Symfony form not validate my DTO with constraint annotations?
I wasn't able to find a solution to enable the mapping that should happen inside the FormComponent with the ValidatorExtension.
But I did find a functional workaround. My approach is to get the Constraints from the readPropertyMetadata function of the validator:
use Symfony\Component\Validator\Validation;
public function buildForm(AbstractEntity $entity) {
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
$fields = [*ENTITY PRPERTIES*];
$classMeta = $validator->getMetadataFor($entity);
foreach ($fields as $field) {
$metadata = $classMeta->getPropertyMetadata($field);
if(is_array($metadata) && count($metadata) > 0) {
$constraints = $classMeta->getPropertyMetadata($field)[0]->constraints;
} else {
$constraints = [];
}
$builder->add(
$field,
null,
[
"attr" => array("class" => "..."),
"constraints" => $constraints
]
);
}
}
As now the constraints are added to the form the validation finally works as expected.

Symfony2 pass two variables from the service to the Controller $slug

To clean up my controller code I want to move the "newPostAction" to a service. The problem I get is that now I cannot pass as a result of the funtion in the service two variables to the controller. I use the function to create a form, and the get the slug from the form's post and render it. I do not know how to pass it to the controller. I tried using the "list()" function but it does not get the info right.
How can I call the pos's "slug" from inside the controller?
Here is the controller code:
/**
* #param Request $request
* #return array
*
* #Route("/new/_post", name="_blog_backend_post_new")
* #Template("BlogBundle:Backend/Post:new.html.twig")
*/
public function newPostAction(Request $request)
{
$form_post = $this->getPostManager()->createPost($request);
$slug_post = ¿How do I get it from inside the createPost()?;
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}
Here is the PostManager service to create the new post entity:
/**
* Create and validate a new Post
*
* #param Request $request
* #return bool|FormInterface
*/
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return true;
}
return $form_post;
}
You just need to return an array from the service and access the values from the controller.
UPDATE
Some changes need to be made to your code in order to get things to work.
Explanation: when the form is valid, the previous code (I deleted it) returned true therefore $ret["form_post"] didn't make sense because $ret was not an array. It surprises me that it didn't throw you an error.
Anyway, that could explain why Doctrine didn't persist your entity. Talking about the redirection, the error could be due to the same reason. $ret was true (a boolean) and $ret["form_slug"] didn't make sense either.
I hope this fixes the problems. Please, let me know if it works.
Service
public function createPost (Request $request)
{
$post = new Post();
$post->setAuthor($this->um->getloggedUser());
$form_post = $this->formFactory->create(new PostType(), $post);
$form_post->handleRequest($request);
$slug_post = $post->getSlug();
if ($form_post->isValid())
{
$this->em->persist($post);
$this->em->flush();
return array("form_post" => true, "slug_post" => $slug_post);;
}
return array("form_post" => $form_post, "slug_post" => $slug_post);
}
Controller:
public function newPostAction(Request $request)
{
$ret = $this->getPostManager()->createPost($request);
$form_post = $ret["form_post"];
$slug_post = $ret["slug_post"];
if (true === $form_post)
{
$this->get('session')->getFlashBag()->add('success', 'Your post was submitted successfully');
return $this->redirect($this->generateUrl('blog_blog_post_show', array('slug' => $slug_post)));
}
return array(
'post_slug' => $slug_post,
'form_post' => $form_post->createView()
);
}

zendframework 2 Doctrine 2 my post form is not returning the values

i am a little baffled by this;
my post forms is not populating the values received from the returned post values; i suspect the problem is arising from my getJobId() in my jobsort class values;
below is my form:
public function jobSortAction()
{
$form = new CreateJobSortForm($this->getEntityManager());
$jobSort = new JobSort();
$form->setInputFilter($jobSort->getInputFilter());
$id= 11;
$jobSort->setId($id);
$form->bind($jobSort);
if ($this->request->isPost()) {
//$post = $this->request->getPost();
$form->setData($this->request->getPost());
//var_dump($post);
//var_dump($jobSort);
if ($form->isValid()) {
$this->getEntityManager()->persist($jobSort);
$this->getEntityManager()->flush();
}
}
return array('form' => $form);
}
below is the var_dumped values of the 'return post values' and the Jobsort() object. You will note that the returned post values has values for both the Id and the JobId
object(Zend\Stdlib\Parameters)[168]
public 'JobSort' =>
array (size=2)
'jobId' => string '5' (length=1)
'id' => string '11' (length=2)
public 'submit' => string 'Submit' (length=6)
object(Workers\Entity\JobSort)[394]
protected 'inputFilter' => null
protected 'id' => int 11
protected 'jobId' => null
protected 'workerservicelist' => null
yet, when i populate the values, it does not seem to record the values for the jobId
below is my jobsort entity class:
class JobSort
{
protected $inputFilter;
/**
* #ORM\Id
*
* #ORM\Column(name="user_id", type="integer")
*/
protected $id;
/**
* #ORM\Column(name="jobId", type="integer")
*/
protected $jobId;
public function setId($id)
{
return $this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setJobId($jobId)
{
return $this->jobId = $jobId;
}
public function getJobId( )
{
return $this->jobId;
}
is there any advice or suggestions on what i need to do to find out why the values are not been populated
warm regards
Andreea
by the way; the form actually works when i had the Id of CLASS jobsort set to
#ORM\GeneratedValue(strategy="AUTO")
the problem started when i took it out and set it to manual
Hello again
here is my form:
this is the error message i received;
An exception occurred while executing 'INSERT INTO worker_main_jobsort (user_id, jobId) VALUES (?, ?)' with params [11, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'jobId' cannot be null
here is my form:
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
use Workers\Form\Fieldset\JobSortFieldset;
class CreateJobSortForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('create-Job-post-form');
// The form will hydrate an object of type "BlogPost"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'));
// Add the user fieldset, and set it as the base fieldset
$JobSortFieldset = new JobSortFieldset($objectManager);
$JobSortFieldset->setUseAsBaseFieldset(true);
$this->add($JobSortFieldset);
// Optionally set your validation group here
// … add CSRF and submit elements …
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
'id' => 'submitbutton',
),
));
// Optionally set your validation group here
}
}
and here is the fieldset class:
class JobSortFieldset extends Fieldset
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('JobSort');
$id= 10;
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'))
->setObject(new JobSort());
}
}
this addition is in response to rafaame solution;
i amended my form as recommended; however it still not working. i think the issue now is that Rafaame solution is in regarding to zendDB save method, but i am using doctrine persis**t and **flush method . i accordingly get the following error message;
Call to undefined method Workers\Entity\JobSort::save()
below is my amended form:
public function jobSortAction()
{
$form = new CreateJobSortForm($this->getEntityManager() );
$jobSort = new JobSort();
if($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid())
{
$entity = $form->getData();
$model = new JobSort();
$model->save($entity);
// $this->getEntityManager()->persist( $model);
// $this->getEntityManager()->flush();
}
}
return array('form' => $form);
}
in response to Rafaame question about what problems i had,the message that i am now receiving is this:
**
EntityManager#persist() expects parameter 1 to be an entity object,
array given.
**
below is my function:
public function jobSortAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getEntityManager();
$form = new CreateJobSortForm($this->getEntityManager());
if ($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid()) {
$entity = $form->getData();
$model = new JobSort($objectManager, $serviceLocator);
$model->getEntityManager()->persist($entity);
$model->getEntityManager()->flush();
}
}
return array('form' => $form);
}
my form; i.e where the hydrator should be set
namespace Workers\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
use Workers\Form\Fieldset\JobSortFieldset;
class CreateJobSortForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('JobSort');
// The form will hydrate an object of type "BlogPost"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\JobSort'));
// Add the user fieldset, and set it as the base fieldset
$JobSortFieldset = new JobSortFieldset($objectManager);
$JobSortFieldset->setUseAsBaseFieldset(true);
$this->add($JobSortFieldset);
If you check your code, you are creating a JobSort entity, setting only its id and binding it to the form:
$jobSort = new JobSort();
$jobSort->setId($id);
$form->bind($jobSort);
After that, you are dumping $jobSort and $this->request->getPost(). So, obviously, you are getting jobId in the POST data but not in the entity (you didn't set the entity's jobId before binding it to the form). There's nothing wrong with your entity's code.
The solution for this: don't bind anything to the form. You should only bind an entity to the form in the case of an edit action, that you fetch the entity from the database and want to populate the form with its values.
Example of add action:
public function addAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getObjectManager();
$form = new Form\EmailCampaign\Add($serviceLocator, $objectManager);
if($this->request instanceof HttpRequest && $this->request->isPost())
{
$form->setData($this->request->getPost());
if($form->isValid())
{
$entity = $form->getData();
//If you want to modify a property of the entity (but remember that it's not recommended to do it here, do it in the model instead).
//$entity->setJobId(11);
$model = new Model\EmailCampaign($serviceLocator, $objectManager);
$model->save($entity);
if($entity->getId())
{
$this->flashMessenger()->addSuccessMessage('Email campaign successfully added to the database.');
return $this->redirect()->toRoute('admin/wildcard', ['controller' => 'email-campaign', 'action' => 'edit', 'id' => $entity->getId()]);
}
else
{
$this->flashMessenger()->addErrorMessage('There was an error adding the email campaign to the database. Contact the administrator.');
}
}
}
return new ViewModel
([
'form' => $form,
]);
}
Example of edit action:
public function editAction()
{
$serviceLocator = $this->getServiceLocator();
$objectManager = $this->getObjectManager();
$form = new Form\EmailCampaign\Edit($serviceLocator, $objectManager);
$id = $this->getEvent()->getRouteMatch()->getParam('id');
$entity = $objectManager
->getRepository('Application\Entity\EmailCampaign')
->findOneBy(['id' => $id]);
if($entity)
{
$form->bind($entity);
if($this->request instanceof HttpRequest && $this->request->isPost())
{
$form->setData($this->request->getPost());
if($form->isValid())
{
//If you want to modify a property of the entity (but remember that it's not recommended to do it here, do it in the model instead).
//$entity->setJobId(11);
$model = new Model\EmailCampaign($serviceLocator, $objectManager);
$model->save($entity);
$this->flashMessenger()->addSuccessMessage('Email campaign successfully saved to the database.');
}
}
}
else
{
$this->flashMessenger()->addErrorMessage('A email campaign with this ID was not found in the database.');
return $this->redirect()->toRoute('admin', ['controller' => 'email-campaign']);
}
return new ViewModel
([
'form' => $form,
'entity' => $entity,
]);
}
Hope this helps.
EDIT:
What I provided was an example of how to handle the form and the entities with Doctrine 2 + ZF2.
What you have to keep in mind is that Doctrine doesn't work with the concept of models, it just understands entities. The model I'm using in my application is a concept of the MVC (Model-View-Controller) design pattern (that ZF2 uses) and I have decided to wrap the entity manager calls (persist and flush) inside my model's method, that I named save() (in the case the entity needs some special treatment before being save to the database and also because it is not a good practice to use the entity manager directly in the controller - see this slide of Marcos Pivetta presentation http://ocramius.github.io/presentations/doctrine2-zf2-introduction/#/66).
Another thing that you may be misunderstanding is that when you do $form->getData() to a form that has the DoctrineObject hydrator, it will return you the entity object, and not an array with the data (this last happens if it has no hydrator). So you don't need to create the entity after doing $form->getData(), and if you do so, this created entity won't have any information provided by the form.
Your code should work now:
public function jobSortAction()
{
$serviceLocator = $this->getServiceLocator();
$entityManager = $this->getEntityManager();
$form = new CreateJobSortForm($entityManager);
if ($this->request->isPost())
{
$form->setData($this->request->getPost());
if ($form->isValid()) {
//I'm considering you are setting the DoctrineObject hydrator to your form,
//so here we will get the entity object already filled with the form data that came through POST.
$entity = $form->getData();
//Again, if you need special treatment to any data of your entity,
//you should do it here (well, I do it inside my model's save() method).
//$entity->setJobId(11);
$entityManager->persist($entity);
$entityManager->flush();
}
}
return array('form' => $form);
}

Testing symfony 2 forms

I develop new type, but I don't know how I can test it.
Assert annotation is not load and validations is not called.
Could any one please help me?
class BarcodeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('price');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bundles\MyBundle\Form\Model\Barcode',
'intention' => 'enable_barcode',
));
}
public function getName()
{
return 'enable_barcode';
}
}
A have following model for storing form data.
namepspace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "...",
* maxMessage = "..."
* )
*/
public $price;
}
I develop some test like this, the form didn't get valid data but it is valid! (Because annotation is not applied)
I try adding ValidatorExtension but I dont know how can I set constructor paramaters
function test...()
{
$field = $this->factory->createNamed('name', 'barcode');
$field->bind(
array(
'price' => 'hello',
));
$data = $field->getData();
$this->assertTrue($field->isValid()); // Must not be valid
}
Not sure why you need to unit-test the form. Cant You unit test validation of Your entity and cover controller with your expected output?
While testing validation of entity You could use something like this:
public function testIncorrectValuesOfUsernameWhileCallingValidation()
{
$v = \Symfony\Component\Validator\ValidatorFactory::buildDefault();
$validator = $v->getValidator();
$not_valid = array(
'as', '1234567890_234567890_234567890_234567890_dadadwadwad231',
"tab\t", "newline\n",
"Iñtërnâtiônàlizætiøn hasn't happened to ", 'trśżź',
'semicolon;', 'quote"', 'tick\'', 'backtick`', 'percent%', 'plus+', 'space ', 'mich #l'
);
foreach ($not_valid as $key) {
$violations = $validator->validatePropertyValue("\Brillante\SampleBundle\Entity\User", "username", $key);
$this->assertGreaterThan(0, count($violations) ,"dissalow username to be ($key)");
}
}
Functional test. Given that you generate a CRUD with app/console doctrine:generate:crud with routing=/ss/barcode, and given that maxMessage="Too high" you can:
class BarcodeControllerTest extends WebTestCase
{
public function testValidator()
{
$client = static::createClient();
$crawler = $client->request('GET', '/ss/barcode/new');
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '12',
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertTrue($crawler->filter('td:contains("12")')->count() > 0);
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
/* force validator response: */
$form = $crawler->selectButton('Edit')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '1002',
));
$crawler = $client->submit($form);
// Check the element contains the maxMessage:
$this->assertTrue($crawler->filter('ul li:contains("Too high")')->count() > 0);
}
}
Include this line must be in Model and try it after include look like your model.
/* Include the required validators */
use Symfony\Component\Validator\Constraints as Assert;
namespace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "min message here",
* maxMessage = "max message here"
* )
*/
public $price;
}

Zend_Validate_Db_RecordExists with Doctrine 2?

I'm using Doctrine 2 in a Zend Framework application and require functionality similar to Zend_Validate_Db_RecordExists and Zend_Validate_Db_NoRecordExists.
For example, when a user enters a new item, I need to validate that a duplicate entry doesn't already exist. This is easy to accomplish with Zend_Db by adding the Db_NoRecordExists validator on my forms.
I tried implementing the custom-validator solution proposed here, but I can't figure out how they are communicating with Doctrine to retrieve entities (I suspect this approach may no longer work post-Doctrine 1.x).
The FAQ section of the Doctrine manual suggests calling contains() from the client code, but this only covers collections, and if possible I'd like to handle all of my form validation consistently from within my form models.
Can anyone suggest a way to use these Zend validators with Doctrine 2 DBAL configured as the database connection/resource?
It's quite straightforward, really.
I have a few Zend_Validate-type validators that talk to Doctrine ORM, so I have an abstract class that they descend from.
Here's the abstract class:
<?php
namespace TimDev\Validate\Doctrine;
abstract class AbstractValidator extends \Zend_Validate_Abstract{
/**
* #var Doctrine\ORM\EntityManager
*/
private $_em;
public function __construct(\Doctrine\ORM\EntityManager $em){
$this->_em = $em;
}
public function em(){
return $this->_em;
}
}
Here's my NoEntityExists validator:
<?php
namespace TimDev\Validate\Doctrine;
class NoEntityExists extends AbstractValidator{
private $_ec = null;
private $_property = null;
private $_exclude = null;
const ERROR_ENTITY_EXISTS = 1;
protected $_messageTemplates = array(
self::ERROR_ENTITY_EXISTS => 'Another record already contains %value%'
);
public function __construct($opts){
$this->_ec = $opts['class'];
$this->_property = $opts['property'];
$this->_exclude = $opts['exclude'];
parent::__construct($opts['entityManager']);
}
public function getQuery(){
$qb = $this->em()->createQueryBuilder();
$qb->select('o')
->from($this->_ec,'o')
->where('o.' . $this->_property .'=:value');
if ($this->_exclude !== null){
if (is_array($this->_exclude)){
foreach($this->_exclude as $k=>$ex){
$qb->andWhere('o.' . $ex['property'] .' != :value'.$k);
$qb->setParameter('value'.$k,$ex['value'] ? $ex['value'] : '');
}
}
}
$query = $qb->getQuery();
return $query;
}
public function isValid($value){
$valid = true;
$this->_setValue($value);
$query = $this->getQuery();
$query->setParameter("value", $value);
$result = $query->execute();
if (count($result)){
$valid = false;
$this->_error(self::ERROR_ENTITY_EXISTS);
}
return $valid;
}
}
Used in the context of a Zend_Form (which has an em() method like the abstract class above):
/**
* Overrides superclass method to add just-in-time validation for NoEntityExists-type validators that
* rely on knowing the id of the entity in question.
* #param type $data
* #return type
*/
public function isValid($data) {
$unameUnique = new NoEntityExists(
array('entityManager' => $this->em(),
'class' => 'PMS\Entity\User',
'property' => 'username',
'exclude' => array(
array('property' => 'id', 'value' => $this->getValue('id'))
)
)
);
$unameUnique->setMessage('Another user already has username "%value%"', NoEntityExists::ERROR_ENTITY_EXISTS);
$this->getElement('username')->addValidator($unameUnique);
return parent::isValid($data);
}
Check out the RecordExists.php and NoRecordExists.php classes in my project:-
https://github.com/andyfenna/AJF-IT/tree/master/library/AJFIT/Validate
I hope these are some use to you.
Thanks
Andrew