I have a big form around 70 input fields and in the form there are buttons that can create additional input fields. My problem is how do I retain these input fields and their values if something on the server goes wrong upon submitting the form, like database insert failure? I have dynamic validation but what if something goes wrong upon submitting?
You have a lot of options.
You could use ajax to post your form, and then redirect to a new page if successful, or alert the user if not (easiest!)
You could recreate the from on the server side from the post variables (harder with a complex form)
The server could send back the form values on failure, and you can write JS to recreate the form (might be easy if you already have JS to manage the form).
You could pass the actual HTML for the form along with the form values, and have the server use it to recreate the form (you may lose JS events depending on how you hook them up, not the best option in general).
There are many more options that are less useful. I would recommend #1.
For a really simple solution:
You can store the values in an object and only once you've got a confirmed DB insert clear it. If insert fails, and you can retry a few times and it and if fails repeatedly, you can write the values to a CSV log file to handle manually.
More complex solution
Queue. Have all insert requests get queue into a job queue using Zend Jobserver, RabbitMQ or Gearman. Then create a worker process to run the inserts on the other end of the queue. This will allow you to re-run /retry failed jobs.
Why so many fields on a single page? I would reconsider doing this. At any rate, if you have server side validation and it fails you can set up the page to have each field use a default value based on the post value that was sent to the server... So just send them back to the same page and have the input fields value set to the post value.
Here is an example.
$username = array(
'name' => 'username',
'id' => 'username',
'maxlength' => '255',
'size' => '28',
'title' => 'username is required.',
'class' => 'form-text required',
'value' => $this->input->post('username'),
);
<?php echo form_input($username); ?>
here are a couple of function that you can use to store the form values in a session:
//save form inputs
function Hold_Form_Input()
{
$FormPost = array();
foreach ($_POST as $key => $entry)
{
$FormPost[''.$key.'']= $entry;
}
$_SESSION['post_form']= $FormPost;
}
//Clear form values upon success
function Clear_Form_Input()
{
if (isset($_SESSION['post_form']))
{
unset($_SESSION['post_form']);
return true;
}
return false;
}
//Reprint form values as needed
function Keep_on_error($fieldname)
{
if(isset($_SESSION['post_form']) && strlen($_SESSION['post_form'][$fieldname]) > 0)
{
$fill = $_SESSION['post_form'][$fieldname];
return $fill;
}
return false;
}
Related
My question is I have a form that I want to submit its values to two locations, 1 would be to my networks database and another to a vicidials server that accepts this api, what would be my approach on doing this, I was thinking of php redirect using header() after inserting to database, isn't it messy that way? Is there any other way of doing this?
Form -> Database -> This apilink
http://serverip/vicidial/non_agent_api.php?phone_number=123456&first_name=John&last_name=Smith&address1=7153+Marbella+Unit+401&city=Cape+Canaveral&state=FL&postal_code=329200000&custom_fields=Y&birth_date=12-25-2017&duplicate_check=DUPCAMP
You can make two functions, one to save the submitted data to the database and one to submit it to the API.
function handleForm()
{
$data = [
'phone_number' => $_POST['phone_number'],
'first_name' => $_POST['first_name'],
// etc ...
];
$inserted = insertToDb($data);
if ($inserted) {
$api_response = submitToApi($data);
}
}
function insertToDb($data)
{
$inserted = false;
// do stuff to insert data to database
if (/**stuff went good**/) {
$inserted = true;
}
return $inserted;
}
function submitToApi($data)
{
$query_string = http_build_query($data);
$url = 'http://serverip/vicidial/non_agent_api.php?' . $query_string;
return file_get_contents($url);
}
You could use file_get_contents:
<?php
file_get_contents($url_with_encoded_params);
Otherwise you are reliant upon the client's browser doing a redirect after receiving the header instruction, you'll also be exposing the end point, and are prone to possible data manipulation.
There are two options:
1. Client side processing
Use ajax call to submit form data to your app server at first then submit same data to third-party API endpoint.
2. Server-side processing
You can simply submit the form to your app server, and then from your app server (after storing data in your database) send a request (may use curl) to third-party API.
I would personally prefer the 2nd one. As it does not exposes the vendor API to the user.
stack: symfony2/doctrine2/php/mysql
a multipage form constist of two steps. each step is realized in a controller action.
in step1, the form is displayed. form-input is validated in the same action. if the form is valid the user should be redirected to the second step/action. in the second step the user has to confirm his input. after confirmation the data should be stored in the db.
thus form-entities/form-data are/is needed in the second step/action. however i do not want to store it in the db before confirmation.
do i really need to serialize all objects? to the session?
is there a better approach?
any suggestions?
First of all, I would recommend validating the input via JavaScript before posting and not in the controller action on the server.
If you don't want to serialize the data to the session you can simply pass it on to the next page when you receive it in the first action and then post it to the second action, I'm imagining something like this:
firstAction() {
$exampleData = $_POST['exampleData'];
// Do whatever you need, then pass the data on to the next page
return $this->render('SomeBundle:Views:secondPage.html.php',
array('exampleData' => $exampleData));
On the second page you then just have to access $exampleData with JavaScript and best put it in some hidden input field inside the form.
<!-- secondPage.html.php -->
<script type="text/javascript">
var exampleData = <?php echo $exampleData ?>;
$('#hiddenInput').val(exampleData);
</script>
The second controller action will then receive $exampleData as well without having it serialized in the session.
Sorry if there are any syntax errors, haven't used symfony2 in a while :)
tried to use serialization, but entities are quite complex with "many" associations. thus serialization is too slow. even after detaching.
first solution (simplified):
store the POST variables to the session inside the first step/action.
$postParams = $this->getRequest()->request;
$session = $this->getRequest()->getSession();
if (!$session) {
$session = new Session();
}
$session->set($sessionKey, $postParams);
in the second step/action i used the form to repopulate my entity.
$cancellation = $manager->initCancellationSomehow();
$session = $this->getRequest()->getSession();
if (!$session) {
$session = new Session();
}
$parameterBag = $session->get($sessionKey);
$cancellation = $this->getCancellation($customerId);
$form = $this->createForm(
new CancellationType(),
$cancellation,
array(
'em' => $this->getDoctrine()->getManager())
);
$form->bind($parameterBag->get('form'));
[..]
second solution:
well my first thought was to store cancellation in the db. therefore i added a state attribute (active/temp/..). unconfirmed cancellations get marked as temp. if the user confirms the state gets changed form temp to active. temp collections get deleted after on hour by a garbarge collector which runs at a low priority.
i like the second solution because the user has to confirm the final cancellation, which is already stored in the db. if the frontend does not work as expected the user will likely notice corrupted cancellations (e.g. wrong entries selected). if he confirms, only the state is changed. feels safe. in the first solution the user confirms what should be stored in the db, but isn't till now. feels unsecure.
I have a form that has 3 steps. I use SESSIONS to keep values from step to step. When I open the same form in another tab and complete the first step, this immediately replaces the Session values from the other form.
So how can I create sessions with field values that are attached to a specific form? I need to avoid Session conflicts.
If the forms are indeed different, then put it in a multi-dimensional array:
$_SESSION['register']['field1']=$_POST['field1'];
$_SESSION['contact']['field1']=$_POST['field1'];
If it is the same form and it is just a new tab, you can either check if the value was set before and ignore it, set a flag to say there is a form submission in progress, or entirely delete the old session values so the new form in the new tab has no values attached.
I use this function to handle form input:
function Hold_Form_Input($formname)
{
$FormPost = array();
foreach ($_POST as $key => $entry)
{
$FormPost[$key]= $entry;
}
$_SESSION[$formname]= $FormPost;
}
And I pass the form name in with a hidden input.
On my website, I have user accounts that are configurable with forms that allow users to update everything from first and last names to privacy settings. I use the following function to update the database with that input. (Note that the following code uses WordPress-specific features.)
function update_account() {
global $current_user; get_currentuserinfo();
require_once( ABSPATH . WPINC . '/registration.php' );
$uid = $current_user->ID;
// First Name
if(isset($_POST['first_name']) && $_POST['first_name'] <> $current_user->first_name) {
wp_update_user( array(
'ID' => $uid, 'first_name' => esc_attr($_POST['first_name'])
));
}
// ...and so on 43 more times...
}
This feels like the wrong way to process forms. This also looks like it will negatively impact server performance when there are multiple users and frequent updates, given that the if-then-else conditions for every field, even fields not on a particular page, force checking each field for input.
Moreover, since form data can be expected to remain relatively constant, I added the <> operator to prevent the function from updating fields where there has not been any change, but I suspect this also means that every field is still evaluated for change. To make matters worse, adding new fields -- there are already 44 fields in total -- is an unwieldy process.
What's a better way to process form data?
Keep an array of the fields you will be processing with this code, and iterate over it. This works if all your attributes are strings, for example. If you have different data types such as boolean flags to handle differently from the strings, you may wish to group them into their own array.
// All the fields you wish to process are in this array
$fields = array('first_name', 'last_name', 'others',...'others99');
// Loop over the array and process each field with the same block
foreach ($fields as $field) {
if(isset($_POST[$field]) && $_POST[$field] != $current_user->{$field}) {
wp_update_user( array(
'ID' => $uid, $field => esc_attr($_POST[$field])
));
}
}
There's a lot of things missing with your implementation. I don't know what kinds of data you're allowing the user to manipulate but most usually have some kind of requirements to be acceptable. Like not having certain characters, not being blank, etc. I don't see any validation occurring, so how do you handle values that might be undesirable? And what happens when you receive bad data? How do you inform the user of the bad data and prompt them to correct it?
If we abstract the situation a bit we can come up with generalizations and implement an appropriate solution.
Basically form fields [can] have a default value, a user specified value [on form review], validation requirements and validation errors [with messages]. A form is a collection of fields that upon form submit needs to be validated and if invalid, re-displayed to the user with instructive corrective prompts.
If we create a form class that encapsulates the above logic we can instantiate and use it to pass around our controller/views. Oops, I was just assuming you were using an Model/View/Controller type framework, and I'm not really familiar with wordPress so I don't know if that is exactly applicable. But the principle still applies. On the page where you both display or process the form, here's some pseudo logic how how it might look.
function update_account()
{
// initialize a new form class
$form = new UserAccountInfoForm();
// give the form to your view for rendering
$this->view->form = $form;
// check if form was posted [however your framework provides this check]
if(!Is_Post())
return $this->render('accountform.phtml');
// check if posted form data validates
if(!$form->isValid($_POST))
{
// if the form didn't validate re-display the form
// the view takes care of displaying errors, with the help of its
// copy of the $form object
return $this->render('accountform.phtml');
}
// form validated, so we can use the supplied values and update the db
$values = $form->getValues(); // returns an array of ['fieldname'=>'value']
// escape the values of the array
EscapeArrayValues($values);
// update db
wp_update_user($values);
// inform the user of successful update via flash message
$this->flashMessage('Successfully updated profile');
// go back to main profile page
$this->redirect('/profile');
That makes your controller relatively clean an easy to work with. The view gets some love and care to, utilizing the $form value to display the form correctly. Technically, you can implement a method in the form class to give you the form html, but for simplicity I'm just going to assume your form html is manually coded in accountform.phtml and it just uses $form to get field info
<form action='post'>
<label>first name</label> <input class='<?=$this->form->getElement('first_name')->hasError() ? "invalid":""?>' type='text' name='first_name' value="<?=$this->form->getElement('first_name')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('first_name')->getError()?></span><br/>
<label>last name</label> <input class='<?=$this->form->getElement('last_name')->hasError() ? "invalid":""?>' type='text' name='last_name' value="<?=$this->form->getElement('last_name')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('last_name')->getError()?></span><br/>
<label>other</label> <input class='<?=$this->form->getElement('other')->hasError() ? "invalid":""?>' type='text' name='other' value="<?=$this->form->getElement('other')->getValue()"/> <span class='errmsg'><?=$this->form->getElement('other')->getError()?></span><br/>
<input type='submit' value='submit'/>
</form>
Here the pseudo code relies on the form class method "getElement" which returns the field class instance for the specified field name (which would be created an initialized in the constructor of your form class). Then on the field class methods "hasError" and "getError" to check if the field validated correctly. If the form has not be submitted yet, then these return false and blank, but if the form was posted and invalid, then they will have been set appropriately in the validate method when it was called. Also "getValue" would return either the value supplied by the user when the form was submitted, or if the form has not been submitted, the default value as specified when the field class was instantiated and initialized.
Obviously this pseudo code is relying on a lot of magic that you'd have to implement if you roll your own solution--and it's certainly doable. However, at this point I'll direct you to the Zend Framework Zend_Form components. You can use zend framework components by themselves without having to utilize the entire framework and application structure too. You might also find similar form component solutions from other frameworks but I wouldn't know about those (we are a Zend Framework shop at my work place).
Hopefully this hasn't been too complicated, and you know where to go from here. Of course just ask if you need any clarification.
I have a simple form with a select menu on the node display page. Is there an easy way to validate the form in my callback function? By validation I don't mean anything advanced, just to check that the values actually existed in the form array. For example, without ajax, if my select menu has 3 items and I add a 4th item and try to submit the form, drupal will give an error saying something similar to "an illegal choice was made, please contact the admin."
With ajax this 4th item you created would get saved into the database. So do I have to write validation like
if ($select_item > 0 && $select_item <= 3) {
//insert into db
}
Or is there an easier way that will check that the item actually existed in the form array? I'm hoping there is since without ajax, drupal will not submit the form if it was manipulated. Thanks.
EDIT:
So I basically need this in my callback function?
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);
$args = $form['#parameters'];
$form_id = array_shift($args);
$form_state['post'] = $form['#post'] = $_POST;
$form['#programmed'] = $form['#redirect'] = FALSE;
drupal_process_form($form_id, $form, $form_state);
To get $_POST['form_build_id'], I sent it as a data param, is that right? Where I use form_get_cache, looks like there is no data. Kind of lost now.
Since you're already using AJAX, why not just write a bit of jQuery to only allow form submission if the choice is within the list of legal choices? This can be done within the custom module it already looks like you're working on (using drupal_add_js()).
It is not especially 'easy', but the standard way to do it would be to use Drupals Forms API for the callback submission as well - that way, you'll get the same validation that would happen on a non js submit.
Take a look at Adding dynamic form elements using AHAH. While it does not match your scenario exactly (they rebuild the form on the callback to add new elements, not to save data), the explanation of the processing workflow is pretty helpful.
Then there are several modules that try to offer AJAX form submission in a generic way - you could check their code on how to do it (or maybe just use them ;)
Ajax submit (only has a dev version)
Ajax (has an 'official' release)
Finally, there are efforts to put better support this functionality into core in Drupal 7 - the related discussions might also help.