【问题标题】:Making HABTM relationships unique in CakePHP在 CakePHP 中使 HABTM 关系独一无二
【发布时间】:2010-03-07 11:24:16
【问题描述】:

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

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

反之亦然,但唯一标志对我没有帮助;我仍然可以为同一对夫妇节省两次。

如何在 CakePHP 中做到这一点?我应该直接在数据库中声明这对夫妇(书,标签)是唯一的,还是这会让 CakePHP 发疯?有没有办法处理这种情况?

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

ALTER TABLE books_tags ADD UNIQUE (book_id,tag_id);

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

【问题讨论】:

    标签: php cakephp model has-and-belongs-to-many


    【解决方案1】:

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

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

    【讨论】:

    • 你能解释一下array('validate' => 'first')saveAll($data, array('validate' => 'first'))中的用法
    【解决方案2】:

    在我看来,任何时候只要您有数据需求,就应该在数据(基础)级别指定这些需求。在尽可能低的级别执行您的规则。如果应用程序也可以执行这些规则,并且您选择这样做,那么我会说它不会受到伤害。不过,我建议不要将应用程序用作强制数据约束的唯一手段。

    【讨论】:

    • 我同意,但是当 CakePHP 第二次尝试保存一对(书、标签)时会发生什么?我希望它被优雅地处理,而不是导致错误。
    • 您肯定希望在应用程序端捕获任何数据库错误。我不是故意暗示其他的。
    【解决方案3】:

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

    根据您使用的 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-relations)

    【讨论】:

      【解决方案4】:

      黑客方式:

      如果您可以拥有第三个 DB 字段(如上面的那个),请执行一个 VARchar(32),它是唯一的,它是前两个的 MD5 哈希。

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

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

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

      【讨论】:

      • 捕捉错误的逻辑确实有点复杂。当用户修改一本书并添加一些标签时,这对夫妇被保存。因此,当 CakePHP 尝试保存关联的模型(标签)时,失败的是保存书。然后我应该了解重复项是什么,然后删除它们并再次尝试保存。这没有多大意义;在保存之前让程序检查重复项更为明智。不管怎样,我觉得这不是正确的方式......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多