有没有办法让saveAll()删除无关的对象?

时间:2010-11-03 14:22:59

标签: php cakephp transactions

我的主机对象有许多与之关联的选项对象。在编辑表单中,用户可以(de)选择选项并保存新的关联集。这是使用发布数据上的saveAll()实现的。结果是

  • 主机(主)对象已更新,
  • 更新先前和新关联中包含的选项(关联)对象,
  • 创建了先前关联中未包含但包含在新关联中的选项对象。

但是没有发生的是

  • 删除先前关联中包含但未包含在新关联中的选项对象。

问题:也可以saveAll()这样做,数据结构如何实现这种效果呢?

相关信息:

我处理编辑表单的代码实际上更复杂(因此我在这里没有引用它)但它导致了书中描述的数据结构:

( [Host] => ( ... host object fields ... ),
  [Option] => ( [0] => ( ... first option object fields ... ),
                ...
                [n] => ( ... nth option object fields ... )
              )
)

现在,如果原始主机具有未包含在0..n数组中的关联选项,则saveAll()将不会检测到此选项,也不会删除该关联对象。

不确定这是否相关,但我使用的是CakePHP 1.3。

5 个答案:

答案 0 :(得分:2)

不是一个优雅的解决方案,但对我有用。

if ($this->Main->saveAll($this->data))
{
    $this->Main->query(sprintf(
        'DELETE '
        . 'FROM extraneous '
        . 'WHERE main_id = \'%s\' AND modified < (SELECT modified FROM main WHERE id = \'%1$s\')'
        , mysql_real_escape_string($this->Main->id)
    ));
}

请注意,您的表格需要有modified字段。

答案 1 :(得分:2)

如果手动将所有内容都包装到事务中,您可以确保以原子方式执行所有操作。

可以使用数据源的begin()rollback()commit()方法完成此操作:

$this->Main->begin();

if ( !$this->Main->save(...) ) {
  $this->Main->rollback();
  return false;
}

// Perform saves in related models...
if ( !$this->Main->MainRelatedModel->save(...) ) {
  $this->Main->rollback();
  return false;
}

// Perform deletes in extraneous records...
if ( !$this->Main->MainRelatedModel->delete(...) ) {
  $this->Main->rollback();
  return false;
}

// Everything went well, commit and close the transaction
$this->Main->commit();

这里的主要缺点是交易无法嵌套,因此您无法使用saveAll()。您必须逐步保存/删除所有内容,而不是一次性保存。

答案 2 :(得分:1)

saveAll()不会删除数据库中的任何内容。

我想最好的方法是在保存之前删除与当前主机相关的选项,然后添加它们。但是,如果你需要更新那些已经存在的(你呢?)因为某些原因(比如:选项与其他一些模型有关),我想你可以尝试编写一段代码,删除未选择的选项。 / p>

答案 3 :(得分:1)

寻找这个,我注意到还没有内置的CakePHP解决方案。为此,我将以下代码添加到我的模型中:

private $oldBarIds = array();
public function beforeSave($options = array() {
    parent::beforeSave($options);

    $this->oldBarIds = array();
    if ($this->id && $this->exists() && isset($this->data['Bar'])) {
        $oldBars = $this->Bar->find('all', array(
            'fields' => array('id'),
            'conditions' => array(
                'Bar.foo_id' => $this->id
            )
        ));
        $this->oldBarIds = Hash::extract($oldBars, '{n}.id');
    }
}

检查保存数据中是否存在Bar。如果是,它将获得当前id的当前id,将它们设置为$this->oldBarIds。然后当保存成功时,它应该删除旧的:

public function afterSave($created, $options = array()) {
    parent::afterSave($created, $options);

    if (!$created && $this->oldBarIds) {
        $this->Bar->deleteAll(array(
            'Bar' => $this->oldBarIds
        ));
    }
}

这种删除方式由模型处理,只有在保存成功时才会发生。应该能够将此添加到某个行为中,可能有一天会这样做。

答案 4 :(得分:0)

HABTM删除所有相关记录,然后重新创建所需内容。正如PawelMysior建议的那样,您可以通过在保存之前手动删除关联的记录来使用hasMany来实现此目的。然而,危险在于,保存失败,你失去了以前的状态。

我会使用GJ解决方案的变体并在成功保存后删除它们,而是循环遍历冗余ID数组并使用Cake的Model-&gt; del()方法。这样就可以保留所有内置的错误处理。