You should use init
action hook. From the codex: init is useful for intercepting $_GET or $_POST triggers.
The following code is just a sample and is untested:
function wpse283607_handle_submit()
{
// check if form is POSTed and nonce is valid
if ( 'POST' === $_SERVER['REQUEST_METHOD']
&& wp_verify_nonce( $_REQUEST['_wpnonce'], 'my-action' ) ) {
if ( !empty( $_REQUEST['rank'] ) ) {
$ranks = (array) $_REQUEST['rank'];
foreach ( $ranks as $post_id => $rank ) {
add_post_meta( intval( $post_id ), 'rank', intval( $rank ) );
}
}
// redirect to the same page (to avoid re-submit form data on browser reload/refresh)
wp_safe_redirect(
remove_query_arg( [ '_wp_http_referer', '_wpnonce' ], wp_unslash( $_SERVER['REQUEST_URI'] ) )
);
exit;
}
}
add_action( 'init', 'wpse283607_handle_submit' );
Then your <form>
would look something like:
<form action="" method="post"><?
wp_nonce_field( 'my-action' );
/**
* @var \WP_Query $the_query
*/
if ( $the_query->have_posts() ) :
$i = 1; // not sure how you generate your "rank" but just an example
while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
<input type="text" name="rank[<?php echo $post->ID ?>]" value="<?php echo $i ?>" /><?
$i++;
endwhile;
wp_reset_postdata();
endif; ?>
<input type="submit" name="submitBtn" value="Update" />
</form>