CakePHP 2调用saveAssociated时关联模型无法保存问题排查
问题现象
维护遗留CakePHP 2项目时,已完成所有模型关联关系定义。因待保存请求数据包含其他模型名标识,在主模型beforeSave回调中实现了数据映射逻辑。
执行保存时主模型ApplicationVersionFive可正常写入数据,但ApplicationPaydayVersionFive等关联模型始终无法写入:关联表无对应数据,也未自动填充application_version_five_id外键字段。
已确认数据库配置正常:database.php中已配置统一表前缀,模型与表映射关系如下:
- 模型:
ApplicationVersionFive,对应数据表:tlp_application_version_fives - 模型:
ApplicationPaydayVersionFive,对应数据表:tlp_application_payday_version_fives
现有代码
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'); /* ** 保存前回调 ** https://book.cakephp.org/2/en/models/callback-methods.html#beforesave */ public function beforeSave($options = array()) { $application = $this->data; $this->data = [ 'ApplicationVersionFive' => $application['ApplicationVersionFive']['Application'], 'ApplicationPaydayVersionFive' => $application['ApplicationVersionFive']['ApplicationPayday'], 'ApplicationResponseVersionFive' => $application['ApplicationVersionFive']['ApplicationResponse'], 'ApplicationPaydayIcicleHashVersionFive' => $application['ApplicationVersionFive']['ApplicationPaydayIcicleHash'] ]; // 继续执行保存 return true; } }
注:原代码存在两处笔误:
$åoptions变量名带特殊字符、最后一个数组元素误用对象操作符->代替数组赋值符=>,已在上述代码块修正。
ApplicationPaydayVersionFive模型代码
<?php class ApplicationPaydayVersionFive extends AppModel { public $name = 'ApplicationPaydayVersionFive'; public $belongsTo = 'ApplicationVersionFive'; }
保存调用代码
$this->ApplicationVersionFive->saveAssociated($this->request->data, array('deep' => true));
问题原因
核心问题是数据映射逻辑放错了执行位置:
CakePHP 2的saveAssociated执行流程中,会在触发主模型beforeSave回调之前,先调用内部_prepareData()方法解析传入的原始数据集,拆分出主模型数据、所有关联模型数据存入内部待保存队列,后续才会执行beforeSave回调。
在beforeSave里修改$this->data新增关联模型数据时,框架已经完成了关联数据的提取流程,不会重新读取新加到$this->data里的关联内容,自然不会触发关联模型的保存、也不会自动填充外键。
解决方案
把beforeSave里的数据映射逻辑移到saveAssociated调用之前执行:先把请求数据处理成CakePHP要求的「主模型键+各关联模型键」的标准结构,再把处理完成的完整数据集传给saveAssociated即可。
如果需要把映射逻辑封装在模型层,不要用beforeSave实现,可以自定义一个公有保存方法,在方法内部先完成数据结构转换,再调用原生的saveAssociated执行保存。
内容的提问来源于stack exchange,提问作者Ryan H

