Value Object instead of Form

Written by cn007b | Published 2018/08/16
Tech Story Tags: php | design-patterns | value-objects | symfony

TLDRvia the TL;DR App

You think handling forms it’s the only way? But, is there any options?

Practical benefits of using Value Object instead of Form

Let’s consider how Value Object (VO) “design pattern” may help you in handling web forms and simplify your life.

Form

Suppose we need handle HTTP request in PHP (both cases: “application/x-www-form-urlencoded” or “application/json” it’s super simple to convert parameters from both requests and use them to fill form in PHP).Usually we do something like this (here I’m going to use symfony/form because it’s pretty straightforward, simple enough, popular and you can find it on github):

  1. create form class:

  1. Create form instance: $task = new Form();

  2. Fill form with parameters from HTTP request:

    $form->handleRequest($request);

And now we can deal with this form. Nothing difficult so far…But hold on… let’s answer next questions:

  1. Is this form valid?
  2. Was all parameters populated with values from request?
  3. Can I modify any parameter value?
  4. Can I loosely re-use this form in downstream services, layers, etc?

Answers:

  1. Have no idea, have to run: $form->isValid()
  2. Not sure. If someone missed (which is rare but technically possible): $form->handleRequest($request); it means form contains blank initialized values, in our case $form->getName() will return null.

  3. Yes. Just call public setter.
  4. Definitely no. Because form can contain not only valid values but also invalid values and it means that we have to flood services with code like:

if ($form->isSubmitted() && $form->isValid()) {// ...}

Value Object

Let’s handle same HTTP request:

  1. create VO class:

  1. Create VO instance: $vo = new ValueObject($request);

And that’s it, no additional steps required. Let’s answer previous questions:

  1. Is this VO valid?
  2. Was all parameters populated with values from request?
  3. Can I modify any parameter value?
  4. Can I loosely re-use this VO in downstream services, layers, etc?

Answers:

  1. Yes! Since VO created — it is valid, because VO contains self validation in __costruct method. VO won’t be created if provided invalid parameters, exception will be thrown.
  2. Yes! Otherwise exception will be thrown and VO won’t be created.
  3. No! VO is immutable, you can’t change anything.
  4. Yes! VO always valid and you can rely on it on any application layer, you can use it as type hint in methods, like:public function doSomething(ValueObject $vo) and you don’t have to flood your code with redundant if blocks (more declarative style).

Criticism

You may consider that write all validation stuff in constructor might be overwhelming — I’ve used here just small simple example, with purpose to provide the main idea in a simplest way. But you may consider option to use something like kint/vo more information you can find here.

Also you may consider exception during __construct as flood your code with try-catch blocks, but in real life you have to have only 1 such block for whole application (no matter how it’s big) which will catch your custom exceptions and will provide all errors into response, like in case with kint/vo you have just catch ValueObject\Exception\ValidationException in 1 place.

In case you don’t like to have all this stuff in Value Object’s constructor and don’t want to use new keyword, you may consider next example:

the main idea stay the same, the only difference — is the way how you create ($vo = ValueObject::fromArray([‘namex’ => ‘bond’]);) and use your Value Object.

Conclusion

Now you know how Value Object can help you to write more strict, robust, immutable code in declarative style.

Hope this article was helpful and you will use VO not only to handle HTTP requests but also to build whole interaction between your components, services, etc. in your application.


Written by cn007b | @cn007b
Published by HackerNoon on 2018/08/16