Doctrine 2 – Day 2 – Model Validation using Symfony Validator Service in Zend Framework

It is just Day 2 of my experiences in the trenches, regular work, had kept me from this but I managed to get some time to keep digging. As a followup to my Doctrine 2 – Day 1 – Commentary from the Trenches. The models are up and configured, and the unit testing is setup following steps from Michelangelo van Dam’s presentation (http://www.slideshare.net/DragonBe/unit-testing-zend-framework-apps).

One of the major process that we are implementing with this migration is detailed unit testing which was tougher with the Doctrine1. With the unit test infrastructure setup, the next item on the agenda is model validation. From previous experience (before Doctrine 1) and with Doctrine1 it is critical to be able to specify validation rules using annotations without having to write PHP if statements. Being a ZF person, the first step to look was the ZF validator classes. While they seem to be well integrated with the forms, they would prove to be too verbose to use for validation of models, since I also need to be able to specify multiple validators per column, and this would not cut it for me.

Next stop was Symfony2 validator service (http://symfony.com/doc/current/book/validation.html) provides validators with annotations support. So that was the easy part, the hard part was yet to come integration. The integration followed the steps below:

a) Add Symfony Validator service to library folder, easy, just download a package from https://github.com/symfony/Validator

b) Register the Symfony Validator annotations – this is where I had problems (more later)

c) Add the Symfony Validators to the model properties

d) Add validation code which needed a validate() method in the base class from which all entities are derived, which requires @ORM\HasLifecycleCallbacks (so that the model can hook into the lifecyle call back models) @ORMPrePersist and @ORM\PreUpdate for the validate method to ensure its called before the models are saved (first time) or updated. More details on the Doctrine annotations can be found at http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

So what problems did I face.

1. From the Doctrine documentation, you use the annotations directly, for example @Id, however experience has shown that you need to namespace and alias them see note from Symfony integration http://symfony.com/doc/current/book/doctrine.html . So I had to change all the Doctrine annotations to use @ORM namespace

2. The default annotation driver only supports a single namespace so you will need to update as per the pastebin below

http://pastebin.com/embed_iframe.php?i=feiKsVxg

Now I am a happy camper, got my models working using Symfony validations, we only have to write code for custom validations which happens only about 20 – 30% of the time.

As a parting shot, the Symfony team and community have done a great job for PHP, why because they provide standalone components (similar to Zend Framework), but each of their components can be used without the rest of the framework. As I was investigating the validator usage and issues, I found a thread where Fabien Potencier and team were discussing annotation support in Symfony. However they also noted that Doctrine Commons had better support, so they stopped the work on Symfony annotation support and just used the services of the Doctrine team. This is how all software development should be done, and is a torch to the rest of us. I am a convert, and happy to be a proud member of the PHP community.

Update May 8, 2012

I had promised to provide some sample snippets of what I am using for the integration with Bisna integration and Symfony validation that I ended up using so here we go https://gist.github.com/2638526 The files are as follows:

a) application.ini – there is nothing special here from Bisna. Includes the cache configuration, prod/staging/dev/test environments all of which inherit from production

b) index.php – from the public folder or htdocs – this may not be perfect but it works and am looking for ways to simplify it

c) Bootstrap – this is the file we use, highlight:

– Storing the entity manager instance in the Zend_Registry, we have a utility method which loads it from the registry and another which also provides a connection from the entity manager so its fully encapsulated

– intialization of the Zend_DB adapter, we need this since we are using the Zend_Session to save the sessions in the database

– the last config is for other resources we use. We have a dependency on the Zend_Registry class as it hides a lot of complexity

d) Document.php – a sample model class

– it extends BaseEntity which provides automatic getters and setters through the __call method, some required fields like id, datecreated, last  update date and last updated by (not all models extend this class only those which need those auditing fields)

– Why do we have getCategoryID(), setCategoryID() for the category property instead of mapping the categoryid field from the database see the next post in the series https://ssmusoke.wordpress.com/2012/03/25/doctrine2-day-3-proxies-associations-relationships/

– Unlike the Doctrine2 defaults we do not use tablename_id but rather tablenameid for the foreign keys so we have define them in each relationship.

Please let me know what I can do to make this any clearer, thanks for reading

10 responses to this post.

  1. Posted by cordoval on March 9, 2012 at 07:54

    I have heard a bit about that torch approach from symfony2 community but now i verified it. Great that you saw that and pass it to others. Just one more thing, get technical and show code, how things work.

    Thanks.

    Like

    Reply

  2. Posted by Nate L on July 13, 2012 at 03:43

    Thanks for this post! I’ve been using Kohana, but discovered its ORM is pretty weak. I swapped out it out for the all-powerful Doctrine2 ORM, but discovered it doesn’t come with validations. Using techniques you discussed above, I dropped in the Symfony Validator files as a submodule in my vendor/doctrine folder and registered the Annotation driver in the Doctrine module init script. I created a base model all my entities extended with @ORM\HasLifecycleCallbacks set and a validate function with @PrePersist and @PreUpdate set on it.

    Getting the validator to actually run and instantiate was a bit trickier though, as most of the Symfony docs just say, “run $this->get(‘validator’)->validate($object)” from your controller. After a bit of source code snooping, I got it to work by putting this inside my validate function:

    // generate a new validator from the factory
    $validator = Validator\ValidatorFactory::buildDefault(
        array(),    // empty array signifies no mapping files (XML or YAML)
        true         // true signfies use of annotations for validation
      )
      ->getValidator();
    // perform the validation on our model object
    $errors = $validator->validate($this);
    // do something with $errors
    

    Now, I have a base class with an inherent validate function function triggered on every persist and update for every entity. All I have to do is add annotations like @Assert\NotBlank on my entity member variables. Perfect!

    Like

    Reply

  3. Hi,

    Thanks for such a great information. In these days its hard to find a honest blog about zend framework developer

    This is sooo exciting! I love your blog and check it every day.the blog has grown so much. Wish you & the blog more success!

    God bless 🙂

    Like

    Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.