Reputation: 524
I have a custom theme with a front-end form which is used to create a post using Ajax, and it works except for the file uploads.
The form looks like this...
<form class="job-form" id="job-form" method="post" enctype="multipart/form-data">
<input type="text" name="job_title" id="job_title" />
<input type="text" name="job_description" id="job_description" />
<input type="file" name="job_files[]" id="job_files" multiple="multiple"/>
<button>Submit</button>
</form>
...and this JS...
$('#job-form').on('submit', function(e) {
e.preventDefault();
$.post( inputs.ajaxurl, {
action : 'add_job',
nonce : inputs.nonce,
post : $(this).serialize()
},
function(response) {
console.log(response);
ResponseSuccess(response);
});
return false;
});
..and PHP to add everything to the database:
add_action( 'wp_ajax_add_job', 'wp_ajax_add_job' );
add_action( 'wp_ajax_nopriv_add_job', 'add_job' );
function add_job() {
$params = array();
parse_str($_POST["post"], $params);
if(isset($_POST["job-form"])) {
$job_title = sanitize_text_field($params['job_title']);
$job_description = sanitize_text_field($params['job_description']);
$args = array(
'post_title' => $job_title,
'post_status' => 'publish',
'post_type' => 'jobs',
'post_author' => 1
);
$result = wp_insert_post( $args );
$post_id = $result;
add_post_meta( $post_id, "job_title" , $job_title);
add_post_meta( $post_id, "job_description" , $job_description);
/* If attachments were attached, upload them */
if($_FILES) {
require_once(ABSPATH . "wp-admin" . '/includes/image.php');
require_once(ABSPATH . "wp-admin" . '/includes/file.php');
require_once(ABSPATH . "wp-admin" . '/includes/media.php');
$files = $_FILES["job_files"];
foreach ($files['name'] as $key => $value) {
if ($files['name'][$key]) {
$file = array(
'name' => $files['name'][$key],
'type' => $files['type'][$key],
'tmp_name' => $files['tmp_name'][$key],
'error' => $files['error'][$key],
'size' => $files['size'][$key]
);
$_FILES = array ("job_files" => $file);
foreach ($_FILES as $file => $array) {
$new_upload = media_handle_upload($file,$post_id);
}
}
}
}
ajaxStatus('job-created', __('Job created.', 'jobs-theme'), $post_id);
}
}
When submitting the form, the post gets created along with the meta keys and meta values without any dramas, but the file attachments are not working. What am I doing wrong?
Upvotes: 2
Views: 993
Reputation: 27214
As jQuery documentation for serialize
explains,
Data from file select elements is not serialized.
The reason is that serialize
encodes form elements into the application/x-www-form-urlencoded
format, which can only accommodate plaintext string values, and not file uploads. To be able to upload files, you will have to submit the whole form in the multipart/form-data
format. One way to accomplish it is to use the FormData
object; jQuery doesn’t directly support it, but it can accomodate it easily.
Here’s a minimal modification of your code to make this work:
$('#job-form').on('submit', function(e) {
e.preventDefault();
// take the data from the form
const formData = new FormData(this);
// append further fields
formData.append('action', 'add_job');
formData.append('nonce', inputs.nonce);
$.ajax( inputs.ajaxurl, {
type: 'POST',
data: formData,
contentType: false, // prevent jQuery from using an incorrect Content-type header
processData: false, // prevent jQuery from mangling the form data
success: function(response) {
console.log(response);
ResponseSuccess(response);
}
});
return false;
});
The contentType
and processData
properties need to be set to prevent jQuery from mangling the data and adding incorrect headers.
Because I had to modify the actually-submitted form structure to be able to handle file uploads, the form code and the receiver script will also have to be modified, for example like this:
<form class="job-form" id="job-form" method="post" enctype="multipart/form-data">
<input type="text" name="post[job_title]" id="job_title" />
<input type="text" name="post[job_description]" id="job_description" />
<input type="file" name="job_files[]" id="job_files" multiple="multiple"/>
<button>Submit</button>
</form>
And in the receiving script:
# ...
function add_job() {
$params = $_POST['post'];
if(isset($_POST["job-form"])) {
# ...
Here I am taking advantage of PHP’s built-in structural form parsing, where fields with names of the form a[b]
generate associative arrays that can be accessed as $_POST['a']['b']
on the PHP side.
Upvotes: 3
Reputation: 90
You need to rewrite your ajax request code just like below.
var $form = $("#yourFormSelector");
$.ajax({
type: 'post',
url: 'url',
data: new FormData($form[0]),
contentType: false,
processData: false,
success: function(response) {
//Your response handler code...
}
});
Upvotes: -1