CakePHP如何让两个字段都唯一?

7
我有一个注册表格,用户可以填写两个电子邮件地址(email1和email2)。市场的要求是它们必须是唯一的(唯一的意思是如果我们有10个用户,那么就会有10*2=20个唯一的电子邮件地址)。
系统已经建立在cakephp上,所以我想知道是否有类似于isUnique功能(一个字段中的唯一性)可以直接完成这项任务?还是我注定要自己编写代码?谢谢。
编辑:根据Richard的示例构建,这对我起作用了:
function checkUnique($data, $fields) {
    if (!is_array($fields)) {
        $fields = array($fields);
    }
    foreach($data as $key) {
        $checks = $key;
    }
    if (empty($checks)) {
      return true;  //allow null
    }
    foreach($fields as $key) {
        $tmp[$key] = $checks;
    }
    if (isset($this->data[$this->name][$this->primaryKey])) {
        $tmp[$this->primaryKey] = "<>".$this->data[$this->name][$this->primaryKey];
    }
    return $this->isUnique($tmp);
}

1
请注意,CakePHP2.x的较新版本现在默认支持http://book.cakephp.org/2.0/en/models/data-validation.html#Model::Validation::isUnique的数组,以允许多个字段。 - mark
6个回答

13
我在 CakePHP 的 Google Group 上发布了解决方案: http://groups.google.com/group/cake-php/browse_frm/thread/b3a1e4ae3eeb6091/e168f54bac27c163?lnk=gst&q=checkUnique#e168f54bac27c163 将以下内容添加到您的 AppModel 中:
        /** 
         * checks is the field value is unqiue in the table 
         * note: we are overriding the default cakephp isUnique test as the 
original appears to be broken 
         * 
         * @param string $data Unused ($this->data is used instead) 
         * @param mnixed $fields field name (or array of field names) to 
validate 
         * @return boolean true if combination of fields is unique 
         */ 
        function checkUnique($data, $fields) { 
                if (!is_array($fields)) { 
                        $fields = array($fields); 
                } 
                foreach($fields as $key) { 
                        $tmp[$key] = $this->data[$this->name][$key]; 
                } 
                if (isset($this->data[$this->name][$this->primaryKey])) { 
                        $tmp[$this->primaryKey] = "<>".$this->data[$this->name][$this- 
>primaryKey]; 

                } 
                return $this->isUnique($tmp, false); 
        } 
} 

并在您的模型验证中使用:

        var $validate = array( 
                "name"=>array( 
                        "unique"=>array( 
                                "rule"=>array("checkUnique", array("name", "institution_id")), 
                                "message"=>"A contact with that name already exists for that 
institution" 
                        ) 
                ) 
       ); 

谢谢提供这个起始代码!不过我还需要稍微修改一下才能让它适用于我的情况。我会编辑原问题并附上最终代码。 - jodeci
应该删除 `if (isset($this->data[$this->name][$this->primaryKey])) { $tmp[$this->primaryKey] = "<>".$this->data[$this->name][$this-
primaryKey]; 这段代码,因为在 Cake 2.x 中,isUnique方法会自行进行检查。更糟糕的是,这会导致isUnique` 返回错误的结果。
- OlivierH

13

checkUnique可以只作为isUnique的包装器来编写。

class AppModel extends Model {
    public function checkUnique($ignoredData, $fields, $or = true) {
        return $this->isUnique($fields, $or);
    }
}

并用于您的模型验证:

public $validate = array(
    'name' => array(
        'unique' => array(
            'rule' => array('checkUnique', array('name', 'institution_id'), false), 
            'message' => 'A contact with that name already exists for that 
institution'
        )
    )
);

这个答案比最初被接受的那个简单得多。 - Kim Stacks
1
@KimStacks 当我写下原始答案时,CakePHP中还没有这个功能。 - RichardAtHome
明白了。谢谢你告诉我,@RichardAtHome。 - Kim Stacks
什么是“on edit case”?当我们将此条件放置以绕过该行时。 - Manish Prajapati

4

来自cakePHP 2.0文档:

您可以通过提供多个字段并将$or设置为false来验证一组字段是否唯一:

public $validate = array(
    'email' => array(
        'rule' => array('isUnique', array('email', 'username'), false),
        'message' => 'This username & email combination has already been used.'
    )
);

在跨多个字段创建唯一规则时,确保将原始字段包含在字段列表中。

如果列表中的某个字段未包含在模型数据中,则视为 null 值。您可以考虑将列出的字段标记为必填。


-1

冒着被打的风险,因为提供了一个非CakePHP的解决方案,让我来介绍一下以下内容。

在数据库中创建一个唯一索引,覆盖您需要的任意列。

标准SQL语法如下:

create unique index {IndexName} on {Table} ({Column}, {Column}, ...)

将"$this->Model->save()"命令放置在"try/catch"块中。在"catch"块中,测试异常的错误代码。在MySQL中,唯一键冲突是错误代码23000,但您应该准备处理其他可能的错误。
这很快速简单,不需要计算数组括号。
无论如何,您应该始终将数据库访问代码放置在"try/catch"块中。您的异常处理的一部分应包括记录任何意外的错误消息。您不能指望CakePHP为您完成所有事情。

我看到我的答案被投票否决了。没关系。我认为我的解决方案更简单,可能更快,并且肯定比指定的“正确”答案涉及更少的编码。 - UncaAlby

-1

是和不是。

是的,你必须自己编写代码,但是可以在CakePHP验证组件内完成。

验证组件有一种机制,允许自定义验证规则。基本上,你只需要在$validate中放置一个函数名(就像通常做的那样)。你确实需要定义这个函数;在这种情况下,它非常简单(只需强制执行双重isUnique要求)。

http://book.cakephp.org/2.0/en/models/data-validation.html#custom-validation-rules


-2
就我所记得的,您必须在模型中使用 beforeSave 方法来实施这种强制。我有一个要求,即对象必须至少设置 N 个 fk 中的一个,并且我只能通过这种方式完成。
编辑:尝试 this thread,看看那里是否有任何方法可以解决您的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接