New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix total count #96
Fix total count #96
Changes from 11 commits
f8f9437
496fc1a
04db45f
76ec1c4
612a5d1
cfe3bbe
53a1f7b
46981dc
502daad
e4975a4
d1e7a78
66da599
f7ccf71
621d8a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,6 +87,13 @@ abstract class Batch { | |
*/ | ||
public $total_num_results; | ||
|
||
/** | ||
* Holds difference between total from client and total from query, if one exists. | ||
* | ||
* @var int | ||
*/ | ||
public $difference_in_result_totals = 0; | ||
|
||
/** | ||
* Errors from results | ||
* | ||
|
@@ -136,8 +143,47 @@ abstract public function update_result_item_status( $result, $status ); | |
*/ | ||
public function get_results() { | ||
$this->args = wp_parse_args( $this->args, $this->default_args ); | ||
$results = $this->batch_get_results(); | ||
$this->calculate_offset(); | ||
return $this->batch_get_results(); | ||
return $results; | ||
} | ||
|
||
/** | ||
* Set the total number of results | ||
* | ||
* Uses a number passed from the client to the server and compares it to the total objects | ||
* pulled by the latest query. If the dataset is larger, we increase the total_num_results number. | ||
* Otherwise, keep it at the original (to acount for deletion / changes). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spelling (account) |
||
* | ||
* @param int $total_from_query Total number of results from latest query. | ||
*/ | ||
public function set_total_num_results( $total_from_query ) { | ||
// If this is past step 1, the client is passing back the total number of results. | ||
// This accounts for deletion / descructive actions to the data. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spelling (destructive) |
||
$total_from_request = isset( $_POST['total_num_results'] ) ? absint( $_POST['total_num_results'] ) : 0; // Input var okay. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think your tests may be able to be improved by passing this in as a parameter and moving the $_POST access up the chain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that so we can test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Partially yes, this is more me identifying a potential refactoring to decouple this method from the $_POST global a bit. While reviewing the test I noticed how you have to set the $_POST value and it got me thinking of what a better way would be. This is not merge blocking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, that makes sense. I just don't know where to move it. We could put the global in it's own function, but that will need to be called somewhere either way, then passed down through to the function. Open to suggestions, I'm pretty deep in this code so an outside eye is always welcome 😃 |
||
|
||
// We need to check to see if there is any new data that has been added. | ||
if ( $total_from_query > $total_from_request ) { | ||
$this->total_num_results = (int) $total_from_query; | ||
} else { | ||
$this->total_num_results = (int) $total_from_request; | ||
} | ||
|
||
$this->record_change_if_totals_differ( $total_from_request, $total_from_query ); | ||
} | ||
|
||
/** | ||
* If the amount of total records has changed, the amount is recorded so that it can | ||
* be applied to the offeset when it is calculated. This ensures that the offset takes into | ||
* account if new objects have been added or removed from the query. | ||
* | ||
* @param int $total_from_request Total number of results passed up from client. | ||
* @param int $total_from_query Total number of results retreived from query. | ||
*/ | ||
public function record_change_if_totals_differ( $total_from_request, $total_from_query ) { | ||
if ( $total_from_query !== $total_from_request && $total_from_request > 0 ) { | ||
$this->difference_in_result_totals = $total_from_request - $total_from_query; | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -146,7 +192,8 @@ public function get_results() { | |
public function calculate_offset() { | ||
if ( 1 !== $this->current_step ) { | ||
// Example: step 2: 1 * 10 = offset of 10, step 3: 2 * 10 = offset of 20. | ||
$this->args['offset'] = ( ( $this->current_step - 1 ) * $this->args[ $this->per_batch_param ] ); | ||
// Also subtracting by results changed to account for deleted and added objects. | ||
$this->args['offset'] = ( ( $this->current_step - 1 ) * $this->args[ $this->per_batch_param ] ) - $this->difference_in_result_totals; | ||
} | ||
} | ||
|
||
|
@@ -271,8 +318,20 @@ public function run( $current_step ) { | |
$per_page = apply_filters( 'loco_batch_' . $this->slug . '_per_page', $per_page ); | ||
|
||
$total_steps = ceil( $this->total_num_results / $per_page ); | ||
|
||
if ( (int) $this->current_step === (int) $total_steps ) { | ||
$this->update_status( 'finished' ); | ||
|
||
// Need to really check to make sure there were no results added while processing. | ||
// In the case of destructive actions (i.e. deletion) there will be a gap equal to the per_page param. | ||
// In all other cases, the difference in totals should equal total number of results. | ||
// If neither of these are true, we need to run the last step over again. | ||
$difference = $this->total_num_results - $this->difference_in_result_totals; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your comment mentions "If neither of these are true, we need to run the last step over again.". How do we ensure that we aren't processing records again by rerunning the same step in this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That difference accounts for the gap between the original total and the most recent query. In the case of a destructive action, the final step will have a number that’s exactly equal to the posts_per_page number. So if there’s 20 total in 4 steps, the last step would have 5 results left. In all other cases, that difference would just be the same as the total_num_results so we know we are done. Now why the greater than or equal to? Well, let’s say we have a destructive action, but we also added a post. In the example above then that would increase 20 to 21 and the final step would actually only have 1 example. But it will never have above 5. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the thorough explanation |
||
if ( $difference <= $per_page || $difference === $this->total_num_results ) { | ||
$this->update_status( 'finished' ); | ||
} else { | ||
$this->current_step = $this->current_step - 1; | ||
$this->update_status( 'running' ); | ||
} | ||
} else { | ||
$this->update_status( 'running' ); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,6 +122,37 @@ public function test_offset_run() { | |
$this->assertEquals( 'finished', $batch_status['status'] ); | ||
} | ||
|
||
/** | ||
* Test that batch gets run. | ||
*/ | ||
public function test_run_with_destructive_callback() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add additional tests to ensure that these work for other batches? Terms and users and sites? |
||
|
||
$post_batch = new Posts(); | ||
|
||
$post_batch->register( array( | ||
'name' => 'Hey there', | ||
'type' => 'post', | ||
'callback' => __NAMESPACE__ . '\\my_callback_delete_post', | ||
'args' => array( | ||
'posts_per_page' => 5, | ||
'post_type' => 'post', | ||
), | ||
) ); | ||
|
||
// Simulate running twice with pass back of results number from client. | ||
$post_batch->run( 1 ); | ||
$_POST['total_num_results'] = 10; | ||
$post_batch->run( 2 ); | ||
|
||
// Check that all posts have been deleted. | ||
$all_posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => -1 ) ); | ||
$this->assertCount( 0, $all_posts ); | ||
|
||
// Ensure that we are still getting a finished message. | ||
$batch_status = get_option( 'loco_batch_' . $post_batch->slug ); | ||
$this->assertEquals( 'finished', $batch_status['status'] ); | ||
} | ||
|
||
} | ||
|
||
/** | ||
|
@@ -132,3 +163,12 @@ public function test_offset_run() { | |
function my_post_callback_function_test( $result ) { | ||
update_post_meta( $result->ID, 'custom-key', 'my-value' ); | ||
} | ||
|
||
/** | ||
* Callback function with destructive action (deletion). | ||
* | ||
* @param WP_Post $result Result item. | ||
*/ | ||
function my_callback_delete_post( $result ) { | ||
wp_delete_post( $result->ID, true ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the
currentStep
out of date? Should we drop it entirely and rely onresponse.current_step
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m all for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we need that currentStep variable to be able to pass it back in the AJAX request. The issue is that the server may deincrement the step as a way of running a process again if more have been added. Need to keep both unless there’s a different solution.