Symfony2 Famous Ozmerk’s Form Model Pattern

A problem arises when we try to create a form that does not conform to our entities.

The solution is said to be to create what we know as Form Model. The idea of this is basically that we don’t use the entity until we need to. This also demands things to be the way they are meant to be, that is that validation should occur on the entity or a wrapper for the entity but should not occur on the form. Therefore all form models created with this in mind end up being a wrapper for the entities.

So we start by defining a php class, the form model, with the validation logic embedded on the getters/setters of our new class. This results in enabling all of our validation rules to be relaxed at will within the form model. The goal is that by implementing a form model would let us partially persist information by relaxing the validation.
This is not all. The complete effort demands that when we are about to save (persist/flush) form information to entities, that we unwrap entities from the form model and persist it directly.

How does this looks in practice?

An example of this use can be found in FOSUB:
https://github.com/FriendsOfSymfony/FOSUserBundle/tree/master/Form/Model

So let’s take a look at this implementation of a model class:

namespace FOS\UserBundle\Form\Model;
 
class ChangePassword extends CheckPassword
{
    /**
     * @var string
     */
    public $new;
}

In this case CheckPassword is only:

namespace FOS\UserBundle\Form\Model;
 
use FOS\UserBundle\Model\UserInterface;
 
class CheckPassword
{
    /**
     * User whose password is changed
     *
     * @var UserInterface
     */
    public $user;
 
    /**
     * @var string
     */
    public $current;
 
    public function __construct(UserInterface $user)
    {
        $this->user = $user;
    }
}

So it is just basically adding a new public property to the main class which already has a constructor that accepts a user object. All the purpose of this class is to be able to change the password for an existing user object.

namespace FOS\UserBundle\Form\Handler;
 
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
 
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Form\Model\ChangePassword;
 
class ChangePasswordFormHandler
{
    protected $request;
    protected $userManager;
    protected $form;
 
    public function __construct(Form $form, Request $request, UserManagerInterface $userManager)
    {
        $this->form = $form;
        $this->request = $request;
        $this->userManager = $userManager;
    }
 
    public function getNewPassword()
    {
        return $this->form->getData()->new;
    }
 
    public function process(UserInterface $user)
    {
        $this->form->setData(new ChangePassword($user));
 
        if ('POST' == $this->request->getMethod()) {
            $this->form->bindRequest($this->request);
 
            if ($this->form->isValid()) {
                $user->setPlainPassword($this->getNewPassword());
                $this->userManager->updateUser($user);
 
                return true;
            }
        }
 
        return false;
    }
}

So as we see here:

$this->form->setData(new ChangePassword($user));

ChangePassword is acting as a wrapper (form model) for setting values from form into entity $user. We know $user has so many other properties. And also we know that the form’s current purpose is just to change the password. So the form is just concerned with the password field out of the entire entity. This makes it a special case for form models and a special handler as we see.

The advantage of using a handler is that we can enclose form validation logic into it. Here it does it within the function process which will be probably called by a controller. The logic thereby has been shifted out of the controller and given to the handler. This is a good practice as we clearly see: Housing form validation logic outside the controller and shift it into the model/form layer of the application.

More over so you can know the power of the DI and services and how they are used to be exposed and do the job at the controller level here are the most significative 3 lines that accomplish the work at hand for changing the password for a given user:

$form = $this->container->get('fos_user.change_password.form');
$formHandler = $this->container->get('fos_user.change_password.form.handler');
$process = $formHandler->process($user);

First line gets the service form with the aim at that field for password. Second line gets the handler that maps this field to the user entity via the form-model for changing password. And finally the last thing to do is to call process method of the handler which does the persistence.

I have been working on two repos.
FormModelBundle

https://github.com/cordoval/FormModelProjectBundle

https://github.com/cordoval/FormModelBundle

The FormModelBundle is the bundle that house these experiments and the other is the project that embeds the first bundle. So you can git clone it and start working right away provided you run bin/vendors update to get the other bundle. Check the deps for the entry of the FormModelbundle.

With that we are now ready to explore the Partial Persistance strategies or Variable Persistance Strategies that can be developed on a form:

So there is going to be more than one of these $this->form->setData(new ChangePassword1($user)); and $this->form->setData(new ChangePassword2($user));.

For our example we are going to use a simple approach but with different entities:

 

This here is a snippet to develop in the future for multistage forms:

Wrapper 
__construct(entity)
 
validation_group1
validation_group2
 
Formtype class is Wrapper (wrapper does not have required)
 
$this->serialize()->save(Session[])
or
$this->serialize()->save_to_db
 
$wrapper->getEntity()

9 thoughts on “Symfony2 Famous Ozmerk’s Form Model Pattern

  1. hi, thanks for your blog is quite interesting… Related to this topic I was wondering about how to use the validator with objects that are created on runtime, and has its properties added later “not defined in a file”

    I’ve been struggling with this for days without luck :( the problem comes because the Validator component uses Reflection based in the name of the class that you are validating so he makes a new instance of the object that doesn’t has its properties yet. So the validator component throws an exception.

    I’m not sure it here is a correct way to do that, may you or someone can help me with this or drive me to the right direction…

    Thanks in advance , and keep posting!

  2. hi Luis, please try to PR the repos, so far we have two scenarios, yours is valid for this approach and we will include it as a scenario. Thanks btw for your contribution. Even though it is small i feel like i am getting through the message of what i am doing and why i am doing it.

    So i will work on this tomorrow all morning. And will try to include your scenario. It is very interesting actually. Thanks!

  3. You’re welcome! don’t feel obligated for this! I actually think that share the knowledge it’s really important and everybody should reward to those people who foment these actions :)

    Currently I’m working with the last repo, the validation component has been marked with @api so its behavior shouldn’t be modified until symfony3

    If can help to get an a idea for a real sceneario , these kind of problems occurs when you try to work with EAV ( entity attribute value) Models, for example a Medical application which is my case.

  4. Get your point of why you sent me here! (from my blogpost at http://sf.khepin.com/2011/08/form-composition-in-symfony2/ )
    It’s true that this is also something I ran into that when making a form for the hole, I only need just one information from the user. Everything else I can already know from his previous request. So the form is only a limited part of the entity.
    In your case validation only need to happen on one field, in mine I still need to validate the whole entity as I’m creating it though so this part was still easy for me!

  5. Pingback: Validating a password in Symfony2 | PHP Developer Resource

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>