让 HABTM 关系在 CakePHP 中独一无二

发布于 2024-08-24 22:08:58 字数 700 浏览 4 评论 0原文

我有两个模型,称为 Book 和 Tag,它们处于 HABTM 关系中。我希望一对(书籍、标签)仅保存一次。在我的模型中,我有

var $hasAndBelongsToMany = array(
    'Tag' => array(
        'className' => 'Tag',
        'joinTable' => 'books_tags',
        'foreignKey' => 'book_id',
        'associationForeignKey' => 'tag_id',
        'unique' => true
    )
);

,反之亦然,但 Unique 标志对我没有帮助;我仍然可以保存同一对夫妇两次。

我如何在 CakePHP 中执行此操作?我应该直接声明这对(书籍、标签)在数据库中是唯一的,还是会让 CakePHP 发疯?有没有一种简单的方法来处理这种情况?

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

ALTER TABLE books_tags ADD UNIQUE (book_id,tag_id);

但这效果不佳。当我一次保存多个标签时,如果所有情侣都是新的,一切都会顺利。如果至少其中一对重复,CakePHP 将无法执行整个操作,因此它不会保存任何新的对(甚至是好的一对)。

I have two models, called Book and Tag, which are in a HABTM relationship. I want a couple (book, tag) to be saved only once. In my models I have

var $hasAndBelongsToMany = array(
    'Tag' => array(
        'className' => 'Tag',
        'joinTable' => 'books_tags',
        'foreignKey' => 'book_id',
        'associationForeignKey' => 'tag_id',
        'unique' => true
    )
);

and viceversa, but the Unique flag does not help me; I can still save two times the same couple.

How do I do this in CakePHP? Should I declare the couple (book, tag) unique in the database directly, or will this make CakePHP go nuts? Is there a Cakey way to handle this situation?

EDIT: I tried making the couple unique with the query (I'm using MySQL)

ALTER TABLE books_tags ADD UNIQUE (book_id,tag_id);

but this does not work well. When I save more than one tag at a time, everything goes well if all the couples are new. If at least one of the couples is repeated, CakePHP fails to do the whole operation, so it does not save ANY new couple (not even the good ones).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

淑女气质 2024-08-31 22:08:58

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'
    )
);

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

The way Cake usually saves HABTM data is by erasing all existing data for the model from the HABTM table and inserting all the relationships anew from the data array you feed into save(). If you're exclusively using this way to deal with HABTM relationship, you shouldn't have a problem.

Of course, that's not always the ideal way. To enforce validation on the HABTM table, you need to create the model for it and add some custom validation, like this:

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);
    }

}

Just make sure to save data using saveAll($data, array('validate' => 'first')) to trigger the validation.

You may have to specify the BooksTag model explicitly in the relationship:

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

If on top of that you're also enforcing the uniqueness on the DB level, all the better.

活泼老夫 2024-08-31 22:08:58

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

In my opinion, any time you have data requirements, those requirements should be specified at the data(base) level. Enforce your rules at the lowest possible level. If the application can enforce those rules as well, and you choose to do so, then I'd say it can't hurt. I'd advise against using an application as the sole means of enforcing data constraints, though.

街角迷惘 2024-08-31 22:08:58

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

根据您使用的 RDBMS,您也许能够创建组合行索引并对其声明唯一约束。

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

我不保证会起作用的一个选项是 CakePHP 计数器缓存行为,但看起来可能会起作用。不确定它默认是否适用于 HABTM,但这里是蛋糕书的链接 (http://book.cakephp.org/view/816/counterCache-Cache-your-count)以及使其与 habtm 一起使用的插件的链接(http://bakery.cakephp.org/articles/view/counter-cache-behavior -for-habtm-关系

I would recommend that you put the constraint in the database schema. It would be easy if you could declare the combined columns as the primary key although I think this would cause cake some problems.

Depending on which RDBMS you use, you may be able to create a combined row index and declare a unique constraint on that.

You other option, for keeping this logic in Cake, would be to alter the beforeSave() function in the Book model, so that it first runs a find() on the passed data, and will only save if the find returns false (meaning there is no previous pair, meaning you've fulfilled your unique constraint).

One option that I don't promise will work, but looks like it might, would be the CakePHP Counter Cache behavior. Not sure it works by default with HABTM, but here is the link to the cake book (http://book.cakephp.org/view/816/counterCache-Cache-your-count) and the link to the addon that will make it work with habtm (http://bakery.cakephp.org/articles/view/counter-cache-behavior-for-habtm-relations)

何其悲哀 2024-08-31 22:08:58

Hackish 方式:

如果您可以有第三个数据库字段(如上面的字段),请执行一个唯一的 varchar(32),它是前两个字段的 MD5 哈希值。

您必须将所有保存修改为 $md5_unique_field = md5( $field_one.$field_two );

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

我同意这也应该在数据库级别强制执行,并且 CakePHP 应该捕获错误。

Hackish way:

If you can have a third DB field (like the one above) do a varchar(32) that is UNIQUE that is an MD5 hash of the first two.

You'd have to modify all saves to be $md5_unique_field = md5( $field_one.$field_two );

CakePHP might allow for a custom validation or a custom model extension that could do this automatically.

I agree that this should be enforced at the DB level also though, and CakePHP should catch the errors.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文