Closed VitaliiIsaenko closed 5 years ago
Hey, are you in control of how your source data looks? The $source
data doesn't resemble the structure of the parent-child relation you are trying to map. This makes the mapping a bit difficult. I'd expect the input data to look like this:
$source = [
"id" => 1,
'child' => ["name" => "Super name"]
];
This way you could just define your mapping like this:
$config = new AutoMapperConfig();
$config->registerMapping(DataType::ARRAY, ChildClass::class);
$config->registerMapping(DataType::ARRAY, ParentClass::class)
->forMember('child', Operation::mapTo(ChildClass::class, true));
(You need the true
in your mapTo, see the comment just below here in the docs).
So if you can change the source structure, that would be the easiest solution. Otherwise you can try the following config. This is assuming your child class has a setter for the name property, or maybe a constructor:
$config->registerMapping(DataType::ARRAY, ParentClass::class)
->forMember('child', function ($source) {
// Just manually map the name string to an instance of the child class.
$child = new ChildClass();
$child->setName($source['name']);
return $child;
});
If your child class doesn't provide any way to set the private name property, you can abuse the fact that the mapper has no problems with setting private properties. The mapper itself is passed as the second parameter of the mapping function, so you could construct an array and map that, basically recreating the first solution on the fly. Something like this:
$config->registerMapping(DataType::ARRAY, ParentClass::class)
->forMember('child', function ($source, AutoMapperInterface $mapper) {
$child = ['name' => $source['name']];
return $mapper->map($child, ChildClass::class);
});
Let me know if this helped you, or if you have any other problems!
That's an amazing explanation. I was thinking about these ways and you are right that they are perfectly valid and my example is too unintuitive.
Thank you again for the explanation!
@mark-gerarts Sorry for bump this old issue. For the solution above it is great for mapping a new object for the destination class.
How about mapping an existing entity object to destination class?
$source = [
'child' => ["id" => "1"]
];
$config->registerMapping(DataType::ARRAY, ParentClass::class)
->forMember('child', function ($source) {
// fetch entity from doctrine by "id" and return the child?
return $child;
});
No problem! You should be able to just fetch your entity from the repository where you indicate it. Unless I'm missing something, then please explain the issue some more.
The code could look something like this:
$config->registerMapping(DataType::ARRAY, ParentClass::class)
->forMember('child', function ($source) use ($repository) {
return $repository->find($source['child']['id']);
});
Hi! I have a problem with nested object filling with array as a source. Is it possible with AutoMapper? Here is what I'd like to do, I attach a sample code. Could you have a look, please?
I know that the example looks strange and therefore give you some context. I have an
address
table in my database. It has fields likecountryId
andcityId
. I don't want to get the city and country with separate queries and write an sql with joins to get all info. My sql looks like this (you can also see the tables and fields there):My
Address
model is the following:As you can see, I would like to have
city
andcountry
fields that areCity
andCountry
types. I would like to map the fields likecityNameEn
to fieldnameEn
of childcity
. Is that possible?Thanks a lot for your effort, the library is awesomely useful!