如何阻止映射数据刷入数据库,同时保留表单映射字段?
问题
是否可以在保持表单字段'mapped' => true的前提下,排除部分表单字段对应的实体被持久化到数据库?
需求场景:用户添加带规格的Device数据时,输入多个序列号,提交后需要拆分这些序列号,每个序列号对应一个独立的Device实体持久化。
当前遇到的问题:表单数据映射到实体后,调用$em->flush()会自动将所有映射的原Device实体(包含所有序列号的那条数据)也持久化,导致最终数据库里既有拆分后的单个序列号设备,又有包含所有序列号的原设备。
当前代码:
$data = $deviceAddForm->getData(); foreach ($data['devices'] as $key => $device) { $device->setAgreement($a); $snList = array_filter(preg_split('/[\s,~]+/', $device->getSerialNumber()), 'strlen'); if($device->isAccessory() != true){ foreach ($snList as $key => $sn) { $d = new Device(); $d->setAgreement($a); $d->setSerialNumber($sn); $d->setQuantity(1); $d->setAccessory(false); $d->setName($device->getName()); $d->setPartNumber($device->getPartnumber()); $d->setModyfications($device->getModyfications()); $d->setWarrantyEnds($device->getWarrantyEnds()); $d->setSla($device->getSla()); $d->setDeviceCategory($device->getDeviceCategory()); $em->persist($d); } } else { $em->persist($device); } } $em->flush();
核心问题:$em->flush()会额外将$data['devices']中的原设备实体刷入数据库,希望阻止这一行为,但保留表单的devices字段映射;同时不想因单独刷入每个拆分后的设备导致多次数据库调用,影响性能。
解决方案
1. 分离原设备实体,避免被持久化
在处理完拆分逻辑后,对不需要持久化的原Device实体调用EntityManager的detach()方法,将其从持久化上下文移除,这样flush时就不会处理它:
$data = $deviceAddForm->getData(); foreach ($data['devices'] as $key => $device) { $device->setAgreement($a); $snList = array_filter(preg_split('/[\s,~]+/', $device->getSerialNumber()), 'strlen'); if($device->isAccessory() != true){ foreach ($snList as $key => $sn) { $d = new Device(); $d->setAgreement($a); $d->setSerialNumber($sn); $d->setQuantity(1); $d->setAccessory(false); $d->setName($device->getName()); $d->setPartNumber($device->getPartnumber()); $d->setModyfications($device->getModyfications()); $d->setWarrantyEnds($device->getWarrantyEnds()); $d->setSla($device->getSla()); $d->setDeviceCategory($device->getDeviceCategory()); $em->persist($d); } // 分离原设备,阻止其被持久化 $em->detach($device); } else { $em->persist($device); } } $em->flush();
2. 批量持久化优化性能
Doctrine默认会在flush时批量执行SQL插入,只要你是在同一个flush调用前完成所有persist操作,就不会产生多次数据库调用。上述代码中所有拆分后的设备都先persist,最后一次flush,本身就是批量处理,不会有性能问题,无需单独刷入每个设备。
3. 替代方案:表单提交后提取字段值创建新实体
如果不想用detach,也可以在表单获取数据后,提取原实体的字段值来创建新实体,不直接依赖映射的原实体进行持久化,这样原实体不会被EntityManager管理,自然不会被持久化:
$data = $deviceAddForm->getData(); foreach ($data['devices'] as $key => $deviceData) { $agreement = $a; $serialNumber = $deviceData->getSerialNumber(); $snList = array_filter(preg_split('/[\s,~]+/', $serialNumber), 'strlen'); if(!$deviceData->isAccessory()){ foreach ($snList as $sn) { $d = new Device(); $d->setAgreement($agreement); $d->setSerialNumber($sn); $d->setQuantity(1); $d->setAccessory(false); $d->setName($deviceData->getName()); $d->setPartNumber($deviceData->getPartnumber()); $d->setModyfications($deviceData->getModyfications()); $d->setWarrantyEnds($deviceData->getWarrantyEnds()); $d->setSla($deviceData->getSla()); $d->setDeviceCategory($deviceData->getDeviceCategory()); $em->persist($d); } } else { $em->persist($deviceData); } } $em->flush();
内容的提问来源于stack exchange,提问作者Pyrol
相关产品推荐
相关产品推荐

