I have a Symfony rest api build with fos restbundle and I'm deserializing a json PUT request in order to update a doctrine entity with a to-many relation.
However, the to-many child objects which are configured with orphanremoval=true
does not get removed from the database when they are not present in the json data.
The PUT request payload:
{
"id": 1,
"name":"Some name",
"export_destinations": [
{
"id": 1,
"type": "USER_STORAGE",
"user": {"id": 5}
}
{
"id": 2,
"type": "SYSTEM_STORAGE"
}
]
}
The controller action:
/**
* @Rest\Put("{id}")
* @ParamConverter(
* "exportJob",
* converter="fos_rest.request_body",
* options={"deserializationContext"={"groups"={"put"}}}
* )
* @Rest\View(serializerGroups={"details"})
* @param ExportJob $exportJob
* @return ExportJob
*/
public function putAction(ExportJob $exportJob)
{
$this->getManager()->persist($exportJob);
$this->getManager()->flush();
return $exportJob;
}
ExportJob entity
/**
* @ORM\Entity()
*/
class ExportJob
{
/**
* @var ArrayCollection|ExportDestination[]
*
* @ORM\OneToMany(targetEntity="ExportDestination", mappedBy="exportJob", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
*/
protected $exportDestinations;
/**
* @param ExportDestination $exportDestination
* @return $this
*/
public function addExportDestination(ExportDestination $exportDestination)
{
$exportDestination->setExportJob($this);
$this->exportDestinations->add($exportDestination);
return $this;
}
/**
* @param ExportDestination $exportDestination
* @return $this
*/
public function removeExportDestination(ExportDestination $exportDestination)
{
$this->exportDestinations->removeElement($exportDestination);
$exportDestination->setExportJob(null);
return $this;
}
}
JMS meta data
MyProject\ExportBundle\Entity\ExportJob:
exclusion_policy: ALL
properties:
id:
groups: ['list', 'details', 'put']
expose: true
name:
groups: ['list', 'details', 'put', 'patch', 'post']
expose: true
exportDestinations:
groups: ['details', 'put', 'patch', 'post']
expose: true
type: 'ArrayCollection<MyProject\ExportBundle\Entity\ExportDestination>'
I am using the DoctrineObjectConstructor
jms_serializer.object_constructor:
alias: jms_serializer.doctrine_object_constructor
public: false
Now when I leave out the second object from the export_destinations array in the json payload my exportJob in the controller action has only one exportDestination object in the array collection after deserialization.
But when I persist, I expect doctrine to remove the exportDestination from the database since I have orphanremoval=true
.
What I think the problem is, is that the removeExportDestination()
method never gets called during deserialization what should set the relation to null on the inversed side. If that doesn't happen it will not delete the entity since it's not become an orphan yet.
Is there a way that JMS will use the add/remove methods for ArrayCollections during deserialization?
I've also tried to use merge()
instead of persist()
but did not make any difference
You are right about that "the removeExportDestination() method never gets called during deserialization".
You could define the accessor property to do what you want:
exportDestinations:
groups: ['details', 'put', 'patch', 'post']
expose: true
type: 'ArrayCollection<AppBundle\Entity\ExportDestination>'
accessor:
getter: "getExportDestinations"
setter: "setExportDestinations"
and in ExportJob entity:
public function getExportDestinations()
{
return $this->exportDestinations;
}
public function setExportDestinations($exportDestinations)
{
// first detach existing related entities.
foreach ($this->exportDestinations as $exportDestination) {
$exportDestination->setExportJob(null);
}
$this->exportDestinations = $exportDestinations;
foreach ($exportDestinations as $exportDestination) {
$exportDestination->setExportJob($this);
}
}
so that during deserialization "setExportDestinations" is called and it takes care of the relation removal.
Having said that, I am not sure if you should use orphanremoval=true
on the relation, as
orphanRemoval=true, even if you will remove given ExportDestination from one ExportJob, and then attach to another ExportJob, this ExportDestination will be deleted during persist, because the reference has been deleted.
I would suggest to remove it and find another way to delete "orphan" ExportDestination entities.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With