Strategies for hydrators: a practical use case
After using Zend Framework 2 for more than a year, I just found out about strategies for hydrators today. Yes, today and thanks to Michael Gallego who introduced me to this concept. But I think many people underestimate what they can do with hydrators and their strategies, so this post explains why strategies for hydrators are awesome.
Take an entity for example which contains a timestamp. You prefer to type hint on the setter for a DateTime object, for obvious reasons:
public function getDate()
{
return $this->timestamp;
}
public function setDate(DateTime $timestamp)
{
$this->timestamp = $timestamp;
return $this;
}
However, you run into troubles if you bind this entity to a form. By default the POST contains string values, so a date value can be send to the server as “2012-11-06”. The hydrator wants to set this string to your entity, but it fails due to the missing DateTime object. And then the strategy comes to the rescue!
A strategy manipulates (and probably converts) a value during extraction
and/or hydration. In this case, it’s perfectly valid to extract the timestamp
as a DateTime
object. However, it is important to hydrate the value
specifically according to the signature: the string value must be converted
into a DateTime
object. Now we can apply this conversion in a strategy:
namespace MyModule\Hydrator\Strategy;
use DateTime;
use Zend\Stdlib\Hydrator\Strategy\DefaultStrategy;
class DateTimeStrategy extends DefaultStrategy
{
/**
* {@inheritdoc}
*
* Convert a string value into a DateTime object
*/
public function hydrate($value)
{
if (is_string($value)) {
$value = new DateTime($value);
}
return $value;
}
}
In this strategy only the hydration part is implemented. If a specific value
is hydrated and this value is a string, it must be converted into a DateTime
object. The only thing left is to attach this strategy to your hydrator, for
the field you want this to be applied. In above case, the property is
extracted to/hydrated from a "date"
(see the naming getDate()
and
setDate()
). So the strategy must be applied to the "date"
field:
use Zend\Stdlib\Hydrator\ClassMethods;
use MyModule\Hydrator\Strategy\DateTimeStrategy;
$hydrator = new ClassMethods;
$hydrator->addStrategy('date', new DateTimeStrategy);
If you now hydrate the entity in the example with a "date"
key in the array
containing a string, is is perfectly transformed into a DateTime object.
One step further: unset properties via the strategy
You can extend this principle. If you want to clear the timestamp, you want to
set the date to null
. However, if you have an HTML form, you cannot make a
POST indicating null
. What happens is you send an empty string. So date is
now ""
.
You can modify the signature of your entity to use the setter to unset the date:
public function setDate(DateTime $timestamp = null)
{
$this->timestamp = $timestamp;
return $this;
}
And the hydrator must understand an empty string means converting it to
null
:
public function hydrate($value)
{
if (is_string($value) && "" === $value) {
$value = null;
} elseif (is_string($value)) {
$value = new DateTime($value);
}
return $value;
}
And voila! Your entity is very clean and the strategy solves all the mess for you.