odan
April 13, 2021, 9:01pm
1
Hello!
I try to transform a PDO or QueryBuilder result set into an DTO (object with typed properties).
final class FooData
{
public ?int $id = null;
// other properties...
}
Example usage:
// Result set from the database
$rows = [
[
'id' => '1',
// ...
],
[
'id' => '2',
// ...
],
];
$strategy = new \Laminas\Hydrator\Strategy\CollectionStrategy(
new \Laminas\Hydrator\ObjectPropertyHydrator(),
FooData::class
);
$result = $strategy->hydrate($items);
Error message:
Typed property FooData::$id must be int or null, string used
in src\Strategy\CollectionStrategy.php Line 103
The code works if I remove the type from the properties. But that’s not what I want.
So how could this be accomplished without this error?
Hello and welcome to your forums!
You can use the Closure strategy to create a custom conversion or you can check the suggestion in the repository:
laminas:4.2.x
← R4c00n:type-cast-strategy
opened 09:05AM - 04 Mar 21 UTC
| Q | A
|-------------- | ------
| Documentation | no
| Bugfix … | no
| BC Break | no
| New Feature | yes
| RFC | no
| QA | no
### Description
We quite often have the case that we need to do a type cast before we can hydrate a value.
The `TypeCastStrategy` provides named constructors to cast a value to `int`, `float` or `string`.
Do you think such a feature could be useful directly in this project? And if so, would you prefer a single strategy (like the current state of this PR) or multiple ones, like `IntStrategy`, `FloatStrategy` and `StringStrategy`?
Any comment or feedback is welcome.
odan
April 14, 2021, 7:53am
3
Hi @froschdesign . Thank you for the tip
Looks like the TypeCastStrategy #54 feature is not merged yet.
I’ve tried the ClosureStrategy. For very complex use cases, this would be a good way to go.
I have found a very simple solution for collections (arrays) with scalar types:
$hydrator = new ReflectionHydrator();
$hydrator->setNamingStrategy(new UnderscoreNamingStrategy());
$strategy = new CollectionStrategy($hydrator, $class);
$result = $strategy->hydrate($items);
This works, because the ReflectionProperty::setValue is able to cast scalar types automatically. Here is a demo: Online PHP editor | output for fou3O
Edit: The downside is that I now get this error message when I try to read undefined properties that were not in the source array (result set).
Typed property FooData::$firstName
must not be accessed before initialization
And you can help here: test it in your application and add your feedback to the pull request. This helps to speed up the process.
Thanks in advance!
1 Like
You must ensure that your object is always created or ends in a valid state.
odan
April 14, 2021, 10:45am
6
Indeed. The easiest way seems to be to define a default value for each property.
Example:
final class FooData
{
public ?int $id = null;
public ?string $username = null;
public string $firstName = '';
public string $lastName = '';
public bool $enabled = false;
}