MySQL Duplicate entry / Which column?

thread: 10 messages  |  last: about 2 years ago  |  started: sunday, september 23, 2007, 2:29 pm pdt


#1  |  GiBSwiss Switzerland
Sunday, September 23, 2007, 2:29 PM PDT

Hi Everyone! ;o)

I'm dealing with MySQL Duplicate entry exception...

I know that with a try/catch such as

try {
    $objMyTable->Save();
}
catch (QMySqliDatabaseException $objExc) {
    if ($objExc->ErrorNumber == 1062) {
        // somes actions...
    }
}

I could “manage” it...

But, as my table has more than one UNIQUE column, how I can I know which is the one causing the exception? Only in that way I could indicate the user which field is the fault...

Of course I could look into my rows to find it out, but maybe there isa simpler way?

Thanks!

#2  |  GiBSwiss Switzerland
Sunday, September 23, 2007, 2:41 PM PDT

For I'm not sure I made myself clear ;o), here is an example

try {
            $objNewArtiste->Save();
            QApplication::Redirect('./');
        }
        catch (QMySqliDatabaseException $objExc) {
            if ($objExc->ErrorNumber == 1062) {
                // MySqli Error: Duplicate entry
                // Which column causes the duplicate entry Exception?
                $objTestArtiste = Artistes::LoadByEmail($this->txtEmail->Text);
                if (is_object($objTestArtiste)) { $this->txtEmail->Warning = 'uplicate entry'; }
                $objTestArtiste = Artistes::LoadByPseudo($this->txtPseudo->Text);
                if (is_object($objTestArtiste)) { $this->txtPseudo->Warning = 'duplicate entry'; }
            }
        }

Is there a better way?

#3  |  Mike Ho (Sunnyvale, CA) United States of America Qcodo Administrator
Monday, September 24, 2007, 10:16 AM PDT

You could try and do a LoadBy() beforehand to see if there already exists a SEPARATE record with that unique column value, before attempting to do the Save().

#4  |  scarax (Rome, Italy) Italy
Wednesday, February 3, 2010, 1:35 PM PST

Hi guys,
with your suggestions, I solved the problem in the following way, using loadBy instead of try/catch:

    protected function btnSave_Click($strFormId, $strControlId, $strParameter) {
        //$withDuplicates stores the result of validation on duplicate entries
        $withDuplicates= false;

        //first validation for the form field "AssignedCardNumber"
        $obj = People::LoadByAssignedCardNumber($this->txtAssignedCardNumber->Text);
        if (is_object($obj) && $obj->Id != $this->lblId->Text){
            $this->txtAssignedCardNumber->Warning = 'value already used';
            $withDuplicates = TRUE;
        }

        //second validation for the form field "Email"
        $obj = People::LoadByEmail($this->txtEmail->Text);
        if (is_object($obj) && $obj->Id != $this->lblId->Text) {
            $this->txtEmail->Warning = 'value already used';
            $withDuplicates = TRUE;
        }

        //if duplicates were found, saving function is aborted
        if ($withDuplicates)
            return;
    
        //no duplicates, go on saving...
        // Delegate "Save" processing to the PeopleMetaControl
        $this->mctPeople->SavePeople();
        $this->RedirectToListPage();
    }

the following condition

(is_object($obj) && $obj->Id != $this->lblId->Text) 

checks if the validation on the field with unique constraint fails: the condition is true if simultaneously:
- there is still a person in the table People with the 'mail' or 'assignedCardNumber' entered
- and this person is not the person that is currently under editing.

It could be very useful making Qcodo automatically generate this validation-code for any UNIQUE CONSTRAINT in the fields of a DB table
Thank you!

#5  |  Mike Ho (Sunnyvale, CA) United States of America Qcodo Administrator
Wednesday, February 3, 2010, 1:57 PM PST

It's a good idea -- unfortunately, where would you suggest that this code generation take place?  E.g., are we talking about code generation at the Model, View or the Controller?

I don't think it makes sense at the Model - a Save() should be stripped of business-specific logic and should focus instead on the ORM-level of attempt the save() -- i believe the caller should be responsible for ensuring that the values validate, including ensuring uniques aren't already in use.  At worst, the caller can always catch any database exception to see if a unique constraint was violated.

This might make more sense at the controller level (e.g. in the metacontrol) -- but again, I wonder if there's a nice clean way of approaching it where (a) it doesn't get in the way for users that are dealing with unique checking at a different place and (b) that you can easily tie in hooks to specify what interaction should take place if a unique constraint fails...

I'd be curious to hear more.

#6  |  scarax (Rome, Italy) Italy
Wednesday, February 3, 2010, 3:20 PM PST

Hi Mike and all,
few minutes ago I moved the validation-code to the FormValidate() function, that is surely a better place than the previous one (About your questions about MVC...I'm going to think about) and the result is surely better:

        protected function Form_Validate() {
            // By default, we report that Custom Validations passed
            $blnToReturn = true;
                        //... other eventual validation...



            // Custom Validation Rules
            $obj = People::LoadByAssignedCardNumber($this->txtAssignedCardNumber->Text);
            if (is_object($obj) && $obj->Id != $this->lblId->Text){            
                $this->txtAssignedCardNumber->Warning = 'value already used';
                $blnToReturn = false;
            }
            $obj = People::LoadByEmail($this->txtEmail->Text);
            if (is_object($obj) && $obj->Id != $this->lblId->Text) {
                $this->txtEmail->Warning = 'value already used';
                $blnToReturn = false;
            }
                


            //... blink code ...
            return $blnToReturn;
        }

but anyway I found a problem: the condition uses “$this->lblId->Text”, but it is not always sure that there is the item 'lblId' in the form (maybe it could be an hidden field).

#7  |  Mike Ho (Sunnyvale, CA) United States of America Qcodo Administrator
Wednesday, February 3, 2010, 3:34 PM PST

Why not compare the obj->id with the ->id of the object in the metacontrol on the page?

That's a much more reliable comparison than comparing the text value of the label. =)

#8  |  scarax (Rome, Italy) Italy
Wednesday, February 3, 2010, 3:55 PM PST

Yes, I was thinking about.
I changed the condition in:

is_object($obj) && $obj->Id != $this->mctPeople->getObjPeople()->Id

but I needed to create the function 'getObjPeople' in the 'PeopleMetaControlGen' class

        public function getObjPeople() {
            return $this->objPeople;
        }

maybe there is still implemented the possibility to get the 'Id' value, but I didn't find it.
Thank you.

#9  |  Mike Ho (Sunnyvale, CA) United States of America Qcodo Administrator
Wednesday, February 3, 2010, 4:06 PM PST

Yeah... you can just do

$this->mctPeople->People->Id

People should be a code-generated public getter for the People object in your metacontrol.

#10  |  scarax (Rome, Italy) Italy
Wednesday, February 3, 2010, 4:11 PM PST

Ok! I found it. Thank you.



Copyright © 2005 - 2012, Quasidea Development, LLC
This open-source framework for PHP is released under the terms of The MIT License.