cakephp isUnique for 2 fields?
Asked Answered
W

6

7

I have a registration form in which users can fill in two email address (email1 & email2). Marketing's requirement is that they need to be unique (unique as in if we had 10 users, then there would be 10*2=20 unique email address).

The system is already built on cakephp, so what I'd like to know is, is there something similar to the isUnique feature (unique in one field) that can do this right out of the box? Or am I doomed to code this myself? Thanks in advance.

EDIT: built on Richard's example, this worked for me:

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);
}
Whacky answered 17/3, 2010 at 9:59 Comment(1)
Note that newer versions of CakePHP2.x support an array for book.cakephp.org/2.0/en/models/… to allow multiple fields by default now.Lacylad
N
13

I posted a solution to this on the CakePHP Google Group:

http://groups.google.com/group/cake-php/browse_frm/thread/b3a1e4ae3eeb6091/e168f54bac27c163?lnk=gst&q=checkUnique#e168f54bac27c163

Add the following to your 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); 
        } 
} 

and is used in your model validate:

        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" 
                        ) 
                ) 
       ); 
Nonmaterial answered 17/3, 2010 at 16:55 Comment(2)
Thanks for the starter! I had to modify it a bit to get it working for my situation though. Will edit original question with final code.Whacky
The if (isset($this->data[$this->name][$this->primaryKey])) { $tmp[$this->primaryKey] = "<>".$this->data[$this->name][$this- >primaryKey]; should be removed, since Cake 2.x makes this check by itself in the isUnique method. Worse, it makes the isUnique return incorrect for me.Tzong
V
13

checkUnique could just be written as a wrapper for isUnique.

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

and is used in your model validate:

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'
        )
    )
);
Visibility answered 31/3, 2013 at 16:57 Comment(3)
This is much simpler answer than the originally accepted one.Hemstitch
@KimStacks This functionality wasn't available in CakePHP when I wrote my original answerNonmaterial
what is on edit case.?? when we put this condition to bypass that row.Geraldina
K
4

From cakePHP 2.0 documentation:

You can validate that a set of fields are unique by providing multiple fields and set $or to false:

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

Make sure to include the original field in the list of fields when making a unique rule across multiple fields.

If a listed field isn’t included in the model data, then it’s treated as a null value. You may consider marking the listed fields as required.

Kellen answered 14/9, 2015 at 15:41 Comment(0)
B
-1

Yes and no.

Yes, you will have to code it yourself, but within the CakePHP validation component.

The validation component has a mechanism to allow custom validation rules. Essentially, you put a function name inside $validate (like you normally would). You do have to define the function; in this case, it's pretty simple (just enforce your double isUnique requirement).

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

Bone answered 17/3, 2010 at 15:7 Comment(0)
R
-1

At the risk of being beaten about the head and shoulders for offering a non-CakePHP solution, let me present the following.

Create a unique index in your database over however many columns you need.

Standard SQL syntax for this is:

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

Place your "$this->Model->save()" command inside of a "try/catch" block. In the "catch" block, test the exception for the error code. In MySQL, a unique key violation is error code 23000, but you should be prepared for other possible errors as well.

It's quick and simple and doesn't involve counting array parentheses.

You should always place database access code inside a "try/catch" block anyway. Part of your exception handling should include logging any unexpected error messages. You can't expect CakePHP to do everything for you.

Residentiary answered 17/4, 2015 at 15:8 Comment(1)
I see my answer was voted down. No matter. I contend my solution is simpler, probably faster, and certainly involves a lot less coding than the designated "correct" answer.Residentiary
O
-2

As far as I remember, you have to this kind of enforcement using the beforeSave method in the model. I had a requirement that an object have at least one of N fks set, and I could only do it this way.

Edit: try this thread to see if anything there solves your problem.

Odiliaodille answered 17/3, 2010 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.