CakePHP 2调用saveAssociated保存异名同构模型数据失败问题
CakePHP 2 异名同构模型关联保存失败问题
- 开发CakePHP 2项目时,需要将
Application模型的数据同步保存到表结构完全一致、仅命名不同的ApplicationVersionFive模型中 - 原有逻辑中
Application模型通过saveAssociated方法接收请求数据完成关联保存,尝试在Application保存完成后直接调用$this->ApplicationVersionFive->saveAssociated()执行保存,但数据始终未写入数据库
现有代码说明
Application模型定义
<?php App::uses('HttpSocket', 'Network/Http', 'HttpResponse'); class Application extends AppModel { public $name = 'Application'; public $hasMany = array('Redirect','ApplicationPromotion', 'ApplicationApiLink'); public $hasOne = array('ApplicationResponse','ApplicationPayday','ApplicationInstallment','ApplicationSecured','ApplicationDebtmgmt','ApplicationReclaim', 'ApplicationPaydayIcicleHash'); /* ** 模型保存后回调方法 */ public function afterSave($created, $options) { } }
ApplicationVersionFive模型定义
<?php App::uses('HttpSocket', 'Network/Http', 'HttpResponse'); class ApplicationVersionFive extends AppModel { public $name = 'ApplicationVersionFive'; public $hasMany = array('RedirectVersionFive','ApplicationPromotionVersionFive', 'ApplicationApiLinkVersionFive'); public $hasOne = array('ApplicationResponseVersionFive','ApplicationPaydayVersionFive','ApplicationInstallmentVersionFive','ApplicationSecuredVersionFive','ApplicationDebtmgmtVersionFive','ApplicationReclaimVersionFive', 'ApplicationPaydayIcicleHashVersionFive'); }
原有控制器保存逻辑
// finally, save to db // TODO: save to Version 5 model $r = $this->Application->saveAssociated($this->request->data, array('deep' => true, 'settings' => $this->_settings)); $this->ApplicationVersionFive->saveAssociated($this->request->data, array('deep' => true, 'settings' => $this->_settings));
受遗留业务属性限制,控制器代码长达数千行无法完整提供,需要调整逻辑实现异名同构模型的完整数据同步存储。
解决方案
失败核心原因
CakePHP 2的saveAssociated方法解析传入数据时,只会读取和当前模型别名、当前模型关联别名完全匹配的数组键。原始请求数据的顶层键为Application,关联数据键为Redirect、ApplicationPromotion等原模型名称,和ApplicationVersionFive及其关联模型的别名完全不匹配,模型读取不到待保存数据,因此不会执行数据库写入。
调整步骤
- 第一步:重置模型状态,避免前一次保存缓存的主键ID、验证错误影响第二次保存,关联模型也要一并清空,防止出现主键重复、执行更新而非新增的问题
- 第二步:重构传入数据集,把所有原模型的数组键替换为VersionFive系列模型的别名,同步第一次保存生成的最新主键ID,保证关联数据外键匹配
- 第三步:重复校验可直接关闭,第一次保存
Application时已经完成数据校验,不需要重复执行浪费性能,再调用saveAssociated完成写入
调整后可直接使用的代码
// 保存原Application模型 $r = $this->Application->saveAssociated($this->request->data, array('deep' => true, 'settings' => $this->_settings)); if ($r) { // 清空VersionFive模型及所有关联模型状态 $this->ApplicationVersionFive->create(); foreach ($this->ApplicationVersionFive->hasMany as $assoc => $config) { $this->ApplicationVersionFive->{$assoc}->create(); } foreach ($this->ApplicationVersionFive->hasOne as $assoc => $config) { $this->ApplicationVersionFive->{$assoc}->create(); } // 构造适配VersionFive模型的数据集 $versionFiveData = array( 'ApplicationVersionFive' => $this->request->data['Application'] ); // 映射替换hasOne关联键名 $hasOneMap = array( 'ApplicationResponse' => 'ApplicationResponseVersionFive', 'ApplicationPayday' => 'ApplicationPaydayVersionFive', 'ApplicationInstallment' => 'ApplicationInstallmentVersionFive', 'ApplicationSecured' => 'ApplicationSecuredVersionFive', 'ApplicationDebtmgmt' => 'ApplicationDebtmgmtVersionFive', 'ApplicationReclaim' => 'ApplicationReclaimVersionFive', 'ApplicationPaydayIcicleHash' => 'ApplicationPaydayIcicleHashVersionFive' ); foreach ($hasOneMap as $oldKey => $newKey) { if (isset($this->request->data[$oldKey])) { $versionFiveData[$newKey] = $this->request->data[$oldKey]; } } // 映射替换hasMany关联键名 $hasManyMap = array( 'Redirect' => 'RedirectVersionFive', 'ApplicationPromotion' => 'ApplicationPromotionVersionFive', 'ApplicationApiLink' => 'ApplicationApiLinkVersionFive' ); foreach ($hasManyMap as $oldKey => $newKey) { if (isset($this->request->data[$oldKey])) { $versionFiveData[$newKey] = $this->request->data[$oldKey]; } } // 执行同步保存 $this->ApplicationVersionFive->saveAssociated($versionFiveData, array( 'deep' => true, 'validate' => false, 'settings' => $this->_settings )); }
后续优化建议
如果后续同步逻辑调用场景多,可以把数据键名转换的逻辑封装到ApplicationVersionFive模型的自定义方法中,比如新增syncFromApplication($sourceData)方法处理数据转换,避免控制器中堆积大量数组映射代码,降低后续维护成本。
因为两个系列模型表结构完全一致,关联表外键字段默认都是application_id,不需要额外修改模型关联的外键配置。
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

