A common pattern in web applications is to take data from a request and validate it in some way, before using it to perform actions on the model layer. A reasonable example using value objects might look something like this:
Should an invalid parameter be passed to a value object, a GuardException is thrown which is handled by the dispatcher to produce an appropriate validation error response.
The first issue is a fatal error when a parameter doesn’t exist. This can be solved with relative ease by creating a “Safe Array” implementing ArrayAccess, falling back to an empty string when no value is available, which will of course fail construction of any value object.
A more pressing issue is that this function has several outcomes and consequently takes quite a lot of effort to test:
What’s more, in order to test that an invalid last name throws an exception, all other fields need to be populated with valid values. When I find myself in this position it suggests that I’ve missed an opportunity to abstract away some logic which as it turns out, I have.
The problems is with the parameters object; it really only offers us a simple layer of indirection before the native parameters which is essential but could be so much better.
A more useful parameters service could perform the mapping from source data to a value object:
This is a great example of why I love php’s unique ability to switch between dynamic “magic” code at the boundaries, and strict, reliable code within the domain.
The beauty of this is that we now only need to test for success in our signupAction since error cases are tested by our ValueObjectService.
Testing the error case in this service can be done with a single example since the concept of mapping from a parameter to a value object has been appropriately abstracted. We’ve also improved the quality of the exceptions, distinguishing between invalid and missing values.
Our revised method now looks like this:
Unfortunately the requirements have changed; first name and last name are now optional fields. The most obvious solution would be to add a has() method to our parameter service, allowing us to check optional fields before requesting them. However this solution reintroduces all the complexity we’ve just recovered from.
A more graceful solution pulls inspiration from functional programming and I’m not sure a method prefix has been coined for this yet but I refer to it as “giving”.
The give parameter will forward the created value object for $key to the recipient which can either be an object with the appropriate setter, or any callable.
So now we have a mechanism to ask our parameter service to pass along its value to a recipient if its source has the appropriate data, otherwise nothing happens, abstracting away the duplicated optional logic.
And so our final method is both readable and incredibly easy to test, something that is often very difficult to achieve on “controller” actions.