You should follow the Post/Redirect/Get pattern to prevent form submission duplicates.
You can integrate that pattern with error processing and successful message display this way:
if POST_REQUEST:
ERRORS = VALIDATE_FORM()
if ERRORS IS EMPTY:
PROCESS_REQUEST
REDIRECT TO <Successful URL>
DISPLAY_FORM(ERRORS)
These are the possible scenarios:
- Get request at form url:
POST_REQUEST is false and so you just display the form with no errors (so a normal empty form)
- Form submission with errors:
POST_REQUEST is true, you validate the form. In this case some errors will be returned in the ERRORS variable (probably an array). The ERRORS variable is not empty so you just display the form with the error messages displayed
- Correct form submission:
POST_REQUEST is true and you validate the form. Since this time the form is valid the ERRORS variable would be empty and you perform the redirect to the <Successful URL> which will display the successful message to the user.
Using this pattern you prevent that the user to send again form data when refreshing the page after a correct form submission.
Django Framework suggest to handle forms this way.
A good way to handle errors in form display is to have the VALIDATE_FORM() function returning an associative array of errors where each key is the name of the field and the corresponding item is an array of error messages.
General form errors (not associated with a specified field) should have an emtpy key.
This way you can, for example, display errors this way in the DISPLAY_FORM() function:
<ul class="errors"><?php foreach(ERRORS['field_name'] as $error) { echo "<li>$error</li>"; } ?></ul>
<input name="field_name" type="text" ... />