CakePHP 保存 HABTM 数据

Posted

技术标签:

【中文标题】CakePHP 保存 HABTM 数据【英文标题】:CakePHP saving HABTM data 【发布时间】:2013-09-24 11:05:07 【问题描述】:

我有 2 个模型,ClientSecurity。它们与 HATBTM 关系相关联。 我制作了一个名为clients_securities 的连接表。所以Client 可以有很多证券,Security 可以属于很多Clients。

这是我的关系定义:

 //Client Model:
public $hasAndBelongsToMany = array(
    'Security' =>
        array(
            'className' => 'Security',
            'joinTable' => 'clients_securities',
            'foreignKey' => 'client_id',
            'associationForeignKey' => 'security_id',
            'unique' => true,
        )
);

 //Security Model:
public $hasAndBelongsToMany = array(
    'Client' =>
        array(
            'className' => 'Client',
            'joinTable' => 'clients_securities',
            'foreignKey' => 'security_id',
            'associationForeignKey' => 'client_id',
            'unique' => true,
        )
);

然后我在客户端控制器中进行了编辑操作并执行了此操作:

public function edit($id = null) 
        if (!$id) 
            throw new NotFoundException(__('Invalid client'));
        

        $client = $this->Client->findById($id);
        if (!$client) 
            throw new NotFoundException(__('Invalid client'));
        

        if ($this->request->is('post') || $this->request->is('put')) 
            $this->Client->id = $id;
            if ($this->Client->saveAll($this->request->data)) 
                $this->Session->setFlash(__('Client has been updated.'));
                return $this->redirect(array('action' => 'edit', $id));
            
            $this->Session->setFlash(__('Unable to update client.'));
        

        if (!$this->request->data) 
            $this->request->data = $client;
            $this->set('securities', $this->Client->Security->find('list'));
        
    

在我的编辑视图中,我使用 htmlHelper 构建表单,添加客户端表字段并生成多选:

echo $this->Form->create('Client'); //start the form for the client model
echo $this->Form->input('Security'); //generates a multi-select popuplated with securities

此外,我还以相同的形式为Client 提供了正常的直接形式输入。例如

echo $this->Form->input('name'); //Input for the client name
echo $this->Form->input('currency'); //Input for the client currency
...

在呈现表单时,所有这些输入都会生成并填充正确的值,但只保存直接客户端数据,而不是来自多选的 HABTM 数据。

当我提交表单时,clients_securities 表没有填充连接 ID。

我需要做些什么才能正确保存它,然后在我重新加载“编辑”视图时预先选择保存的证券。


编辑:为了澄清事情,这里是$this->request->datapr()。 (值(“ALLCMCT LX Equity”)是securities 表的正确外键):

Array
(
    [Client] => Array
        (
            [id] => 1212
            [name] => Example Client
            [currency] => GBP
            [partner] => 
            [risk_category_id] => 4
            [client_code_id] => 1
            [min_cash_balance] => 0
            [active] => 1
            [model] => 0
            [institutional] => 0
            [non_uk_situs] => 0
            [reporting_status] => 0 
        )

    [Security] => Array
        (
            [Security] => Array
                (
                    [0] => .1muslib3 index
                    [1] => .eurib3 index
                    [2] => .ukcpi2 index
                )

        )

)

所以基本上,假设我的客户 ID 是 12345,Cake 应该像这样在 clients_securities 表中插入 2 条记录:

id | client_id | security_id
----------------------------
1  | 12345     | ALLCMCT LX Equity
2  | 12345     | APMKNTD LX Equity

如果我手动将一些连接记录添加到clients_securities,当我去编辑客户时,多选中的证券会正确地预先选择,表明正在从连接表中读取数据。当我保存表单时,它实际上会删除连接记录,但不会保存新记录。

附加说明:我的安全 ID 存储为 CHAR(36)(如果有任何影响)。这不是一个自动递增字段,它包含证券的代码并且每个都是唯一的。

【问题讨论】:

看到这个***.com/a/18891807/1239506 我已经阅读了所有我能找到但没有成功的相关问题。 它应该按照您的方式工作。无论如何,您是否尝试过'unique' => 'keepExisting'?另外,我认为 ID 格式不是问题,但只是为了确定您是否尝试过使用 INT 假 ID?最后:能否请您发布完整的pr($this->request->data)(当然没有敏感数据) 修改问题以包含整个$this-request->data。我想我想让unique 保持真实,因为我希望在插入新链接之前删除旧链接。 我的一个项目中也有类似的情况,但我有数字 ID。我尝试将 id 更改为 char(30) 并停止工作。如果我混合使用数字和字符值,它只会保存数字! 【参考方案1】:

问题在于security_idSecurity.id的格式:不支持char。

我找到了这个ticket(但我在文档中没有找到任何参考资料)

您必须创建另一个带有数字 id 的列才能在 HABTM 关联中使用

【讨论】:

如果我将列类型更改为 CHAR(36) 会解决问题吗?编辑:不,它没有。 如果我将我的关系改造成hasManybelongsTo 关系,它会起作用吗?我该怎么做? 在我看来,最好的方法是使用数字 id 并将当前 id 重命名为“name”或“alfanumeric_identifier”。如果你不能这样做,那么你可以手动保存clients_securities 表中的项目,将$this->request->data['Security']['Security'] 数组放在foreach cicle 中。即$this->Client->ClientsSecurity->save(array('client_id' => $client_id, 'security_id' => $security_id));。请记住先删除旧关系 已解决!最后。你是绝对正确的。向我的证券表添加一个整数主键列现在可以正常工作。非常感谢,有赏金【参考方案2】:

像这样修改表单输入:

echo $this->Form->input('Security.Security');

【讨论】:

抱歉,这不会改变任何事情【参考方案3】:
<?php 
Model::saveAssociated(array $data = null, array $options = array());

or

Model::saveAll(array $data = null, array $options = array());
?>

欲了解更多信息,请访问:http://book.cakephp.org/2.0/en/models/saving-your-data.html

【讨论】:

如果上面的代码有任何问题,请告诉我。很高兴帮助你:) 我尝试将save 更改为saveAll,但没有成功。多选中的数据看起来不像是与其他数据一起发布的。 如果我手动将连接记录添加到数据库,它会正确填充和预选证券。它只是不会保存。 事实上,当我保存它时,实际上删除了当前的连接记录而不保存新的。【参考方案4】:

我试试看……

你忘记了 $this->Client->read();在 $this->Client->id = $id 之后;在 edit() 函数中($this->Client 的内部未初始化,因此保存完成时不存在 client_id)。调用“findById”将只返回一个数组,但不会将数据读取到对象 $this->Client。

只是一个有根据的猜测..

【讨论】:

对不起,这个没有改变。【参考方案5】:

也许我已经老了,但我猜你忘记在你的视图中创建一个带有客户端 ID 的隐藏输入

echo $this->Form->hidden('Client.id');

【讨论】:

该表单已经与Client 关联,并且其中有一个客户端ID 的输入。完整的请求数据现在有问题【参考方案6】:

它没有填充中介表,因为在填充了两个模型之后,它没有填充中介表的 ID。因此,用户 beforeSave 和 afterSave 函数将填充您的中介表。在 Client 模型中添加 beforesave 功能

public function beforeSave($options = array()) 
        parent::beforeSave($options = array());
            foreach (array_keys($this->hasAndBelongsToMany) as $model):
                if(isset($this->data[$this->alias][$model])):
                    $this->data[$model][$model] = $this->data[$this->alias][$model];
                    unset($this->data[$this->alias][$model]);
                endif;
            endforeach;
        return true;
    

在安全用户AfterSave功能中,

public $securityIdArray = array();

public function afterSave($created) 
        if($created) 
            $this->securityIdArray[] = $this->getInsertID();
        
        return true;
    

首先保存安全并使用 $securityIdArray 获取 $this->Model->保存在控制器中的安全 ID。

$this->Security->securityIdArray[] = $void['Security']['id'];

嗯,正如@Arilia 所说,修改你的论坛输入字段

echo $this->Form->input('Security.Security');

并且不要为两个模型制作单独的表格

【讨论】:

最后一点,Security.Security 字段属于哪个表单模型?目的是为客户分配一种或多种证券,这类似于为帖子分配标签。客户表格肯定是正确的。 另外,Securities 模型永远不会,也不需要填充,只有连接表。表中已经有证券。目的是选择一些并让它们“属于”客户。

以上是关于CakePHP 保存 HABTM 数据的主要内容,如果未能解决你的问题,请参考以下文章

无需表单即可保存 cakePHP HABTM 数据

cakephp 在不使用视图的情况下保存 HABTM 关系的数据

CakePHP 使用 saveAll:如何使用 HABTM 链接记录保存额外数据?

CakePHP 在 HABTM 字段中保存数据

在 HABTM 的情况下,CakePHP 不会保存正确的数据

在 CakePHP 中使用 HABTM 保存 hasOne