Symfony2 - how to recover a unique restriction error after submitting a form?

I have an object called Contact with a unique email field. In addition, I have a form type used for the admin interface, call him ContactType . All of the following happens using a form built using ContactType :

Suppose I want to add an email mr.validated@example.com , of course it works. Then I try again, and bam, the check passed, the error message says what happened. Fine!

Now I want to add another contact, this time with the email mr.race.condition@example.com , but, unfortunately, I accidentally submitted the form twice! Both requests are processed as follows:

  | Request 1 | Request 2 -+-----------------+----------------- 1| $form->bind() | $form->bind() 2| Validation | Validation 3| $em->flush() | $em->flush() 

In both cases, a check has passed since the Contact object with this email address was not yet in the database. This results in two Insert requests with the same email address. MySQL will prevent the second, so Doctrine will throw an exception, the user will see a 500 error instead of "Email has already been done."

My question is: How do I get Symfony to handle this for me? I just want to tell the user that he must enter a different email address.

I could, of course, do something like this:

 try { $this->getDoctrine()->getManager()->flush(); } catch (DBALException $e) { $pdoException = $e->getPrevious(); if ($pdoException && $pdoException instanceof PDOException && $pdoException->getCode() === '23000' ) { // let the form know about the error } else throw $e; } 

But this is wrong, it requires copying the code paste every time I have to deal with unique constraints, and this is a problem if there is more than one unique index.

+1
php mysql forms symfony doctrine2
source share
1 answer

This is probably not a real SO style answer, as far as my personal opinion is, but I'm just trying to help you. I will be a little critical and get a lot of votes here, but this is normal if you feel more confident about this problem.


EDIT: added sample code

If you really need a truly decisive look at PHP mutex and its derivatives. Just protect (wrap) your critical race status code in the blocked code section, and you will see that two threads can be executed at the same time (as long as you have only one front car).

Usage example here:

 $file = fopen("code_section_001.lock", "w+"); if (flock($file,LOCK_EX)) { // $form->bind() // Validation // $em->flush() flock($file,LOCK_UN); } else { echo "Error locking file!"; } fclose($file); 

If you can, use try {} finally {} , it depends on the version of PHP you are using.

However, I would discourage this practice because it has some performance implications.


I have the impression that you are overfulfilling. How much time elapses between validation and insertion? Microseconds? And how many subscribers will enter their email in a day? How often will they use the same email? More than ever? And if all these coincidences lead to this race condition, then the HTTP 500 error code is not so bad.

Because if it is a legitimate user:

  • he will be registered once;
  • he will use his email, which is by definition unique;
  • he may retry in the remote event of any error page.

On the contrary, an unlawful user (bot?) He will:

  • register several times;
  • use random or known or stolen email addresses;
  • try HTTP requests again.

In this second case, I would advise you to answer 500! This is what a website should do.

You have already taken steps to gracefully recover errors (custom error message), so the real user user will most likely not see the HTTP 500 error.

By the way, PHP and Symfony often present problems like these, and solving it in a perfect way means messing up the simple and clean code. Is it really worth cluttering up your code?

And think that emails are not necessarily unique. I can, for example, use the not-so-well-known suffix function. It is supported by gmail and others and allows you to have the suffix + , for example:

 someone@somewhere.com someone+one@somewhere.com someone+two@somewhere.com 

All three addresses will go to the same mailbox ( someone@somewhere.com ), but are they unique?

Perhaps you should invest more in these details than in a microsecond crash window. The important thing is that the database does not insert a multiplicity that you have already done with a unique constraint. This error message, not very clean, is really secondary.

And have you ever released it? I mean with the test setup and actually got 500 instead of the error page?

+2
source share

All Articles