Wordpress - $_POST form request with admin-post

Maybe a little late but I stumbled on this when I was having issues figuring it out so thought I would supply what I found out for future people.

I've found the basic principles are to have a hidden input named action and it's value being a custom set identifier. For example

<input name='action' type="hidden" value='custom_form_submit'>

With this input, and your forms action pointing to admin-post.php an action can be set. We set this action using admin_post_custom_form_submit.

To make things more complicated we can use a wp_nonce_field which I think is a basic security thingy. Basically it adds a random $_POST value. Fair enough.

Next we want to set our action like so:

add_action('admin_post_custom_form_submit','our_custom_form_function');

So when there is a form submitted to admin-post.php and there is an action value of custom_form_submit the function our_custom_form_function will be called! :D

function our_custom_form_function(){
    //print_r($_POST);
    //you can access $_POST, $GET and $_REQUEST values here. 
    wp_redirect(admin_url('admin.php?page=your_custom_page_where_form_is'));
   //apparently when finished, die(); is required. 
}

Now you say you get a white page. This is because we need to redirect back to our form. I've used a simple wp_redirect()

Hope this has helped :) I'm going to try and figure out how I can do some validation and give errors back to our redirect form. I think the simplest idea would be to make a $_GET value and find that on our page but it's not great is it?

I've also found that once submitted $_POST is cleared!! DX This is probably to do with the redirect. I'll have a google and see what I can find :d

Hope this has helped :)

UPDATE

I done some more work and realised the only real way to return values is to use the $_GET variable. This way you can re-enter any post values. Just don't forget to use urlencode()to ensure that special characters such as '@' and so on are included.

I had more than one page with what I was working on so I done 2 different redirects to the different pages and included the errors and so on. The checked for them above my form.

Another handy function. http_build_request() can turn arrays into 'url safe' arrays so that you can send them over $_GET requests etc.


Before redirecting you can save any validation errors and post values into a transient. These values will persist after the url redirection.

This is exactly how WP core does it for the Settings API when returning errors.

So:

-validate form data

-store post data in transient with set_transient()

-store any errors in transient

-redirect to form output

-check for errors in transient

-load post values into form fields from transient

You could use a session or the Options API instead of a transient.

Now, I've been experimenting with various ways of handling form submissions both on the frontend and on the admin. I'm not sure admin_post_{action} is all that convenient because of the need to persist data and errors across the redirect. Using the init or admin_init hook to check for a form submission might be easier. You could also simply post your form back to the same page and process the form submission before the form is displayed.


The short answer is that you have to do all the work: save the user input in the database, check for errors or success, and redirect wherever you want when finished.

If you want to redirect to the form again you can use a field generated by wp_nonce() included in the $_POST array sent by your form: $_POST['_wp_http_referer']

Finally you can redirect and send a success or error message using wp_safe_redirect(), esc_url_raw() and add_query_arg().

add_action( 'admin_post_my-action', 'my_save_form_function' );
add_action( 'admin_post_nopriv_my-action', 'my_save_form_function' );
function my_save_form_function() {
    if ( ! empty( $_POST['_wp_http_referer'] ) ) {
        $form_url = esc_url_raw( wp_unslash( $_POST['_wp_http_referer'] ) );
    } else {
        $form_url = home_url( '/' );
    }
    if ( isset( $_POST['name'] )
        && isset( $_POST['description'] )
        && isset( $_POST['my-nonce'] )
        && wp_verify_nonce(
            sanitize_text_field( wp_unslash( $_POST['my-nonce'] ) ),
            'my-action'
        )
        ) {

        // Save your form data...

        //All  works fine ?
        wp_safe_redirect(
            esc_url_raw(
                add_query_arg( 'my_status', 'success', $form_url )
            )
        );
        exit();
    } else {
        wp_safe_redirect(
            esc_url_raw(
                add_query_arg( 'my_status', 'error', $form_url )
            )
        );
        exit();
    }
}