使HABTM关系在CakePHP中独一无二

使HABTM关系在CakePHP中独一无二,php,cakephp,model,has-and-belongs-to-many,Php,Cakephp,Model,Has And Belongs To Many,我有两个模型,叫做Book和Tag,它们处于HABTM关系中。我想要一对(书、标签)只保存一次。在我的模型中,我有 var $hasAndBelongsToMany = array( 'Tag' => array( 'className' => 'Tag', 'joinTable' => 'books_tags', 'foreignKey' => 'book_id', 'associationFore

我有两个模型,叫做Book和Tag,它们处于HABTM关系中。我想要一对(书、标签)只保存一次。在我的模型中,我有

var $hasAndBelongsToMany = array(
    'Tag' => array(
        'className' => 'Tag',
        'joinTable' => 'books_tags',
        'foreignKey' => 'book_id',
        'associationForeignKey' => 'tag_id',
        'unique' => true
    )
);
还有维切维萨,但这面独特的旗帜对我没有帮助;我仍然可以为同一对夫妇节省两倍的钱

如何在CakePHP中实现这一点?我应该直接在数据库中声明这对夫妇(书籍、标签)是唯一的,还是这会让CakePHP发疯?有没有一个关键的方法来处理这种情况

编辑:我尝试通过查询使这对夫妇唯一(我使用的是MySQL)


但这并不奏效。当我一次保存多个标签时,如果所有夫妇都是新人,一切都会顺利进行。如果至少有一对重复,CakePHP无法完成整个操作,因此它不会保存任何新的对(即使是好的对)。

在我看来,任何时候有数据要求,都应该在数据(基础)级别指定这些要求。在尽可能低的级别执行规则。如果应用程序也可以执行这些规则,而您选择这样做,那么我认为这不会有什么坏处。不过,我建议不要使用应用程序作为强制执行数据约束的唯一手段。

我建议您将约束放在数据库模式中。如果可以将组合列声明为主键,这将很容易,尽管我认为这会导致一些问题

根据您使用的RDBMS,您可能能够创建一个组合行索引,并在该索引上声明一个唯一的约束

为了保持这种逻辑,另一种选择是更改Book模型中的beforeSave()函数,以便它首先对传递的数据运行find(),并且只有在find返回false时才会保存(这意味着没有前一对,这意味着您已经满足了唯一的约束)

一个我不承诺会奏效的选项是CakePHP计数器缓存行为,但看起来可能会。不确定它在默认情况下是否适用于HABTM,但下面是指向蛋糕书()的链接和使其适用于HABTM()的插件的链接。

Hackish way:

如果您可以使用第三个DB字段(如上面的一个),那么可以使用唯一的varchar(32),即前两个字段的MD5哈希

您必须将所有保存修改为
$md5\u unique\u field=md5($field\u one.$field\u two)

CakePHP可能允许自定义验证或自定义模型扩展来自动执行此操作


不过,我同意这也应该在DB级别强制执行,CakePHP应该捕获错误。

Cake通常保存HABTM数据的方法是从HABTM表中删除模型的所有现有数据,并从馈送到
save()
的数据数组中重新插入所有关系。如果您专门使用这种方式来处理HABTM关系,您应该不会有问题

当然,这并不总是理想的方式。要在HABTM表上实施验证,需要为其创建模型并添加一些自定义验证,如下所示:

class BooksTag extends AppModel {

    var $validate = array(
        'book_id' => array('rule' => 'uniqueCombi'),
        'tag_id'  => array('rule' => 'uniqueCombi')
    );

    function uniqueCombi() {
        $combi = array(
            "{$this->alias}.book_id" => $this->data[$this->alias]['book_id'],
            "{$this->alias}.tag_id"  => $this->data[$this->alias]['tag_id']
        );
        return $this->isUnique($combi, false);
    }

}
只需确保使用
saveAll($data,array('validate'=>'first'))保存数据即可
触发验证

您可能必须在关系中明确指定BooksTag模型:

var $hasAndBelongsToMany = array(
    'Tag' => array(
        ...
        'with' => 'BooksTag'
    )
);

如果除此之外,您还在DB级别上强制执行唯一性,那就更好了。

我同意,但是如果CakePHP第二次尝试保存一对(书籍、标签)时会发生什么呢?我希望它被优雅地处理,并且不会导致错误。您肯定希望在应用程序端捕获任何数据库错误。我并不是有意要暗示,捕捉错误的逻辑确实有点复杂。当用户修改一本书,添加一些标记时,这对夫妇将被保存。因此,当CakePHP试图保存相关模型(标记)时,失败的是保存这本书。然后,我应该了解重复的内容,然后删除这些内容并再次尝试保存。这没有多大意义;更明智的做法是让程序在保存之前检查重复项。无论如何,我觉得这不是正确的方法…你能解释一下
array('validate'=>'first')
saveAll($data,array('validate'=>'first'))中的用法吗
var $hasAndBelongsToMany = array(
    'Tag' => array(
        ...
        'with' => 'BooksTag'
    )
);