-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Thanks for your interest in the alleyinteractive/wp-bulk-task
Composer package. This wiki contains detailed usage information and some tips on how to get the most out of the package.
Install the package via Composer:
composer require alleyinteractive/wp-bulk-task
Ensure that the Composer autoloader is being included in your project so that the class files can be loaded automatically:
require_once '/path/to/vendor/autoload.php';
Then declare a new bulk task with a unique key and run it with WP_Query
search/filter parameters and a callback for each post:
(new \Alley\WP_Bulk_Task\Bulk_Task( 'my-unique-key' ))->run(
[ 'post_type' => 'post' ],
function( WP_Post $post ) {
// Do something with the post object here.
}
);
You can also declare a new bulk task with a unique key and run it with WP_Term_Query
search/filter params and a callback for each term:
(new \Alley\WP_Bulk_Task\Bulk_Task( 'my-unique-key' ))->run(
[ 'taxonomy' => 'category' ],
function( WP_Term $term ) {
// Do something with the term object here.
},
'wp_term'
);
Bulk task functionality can be used in any context, but is recommended for systems that are intended to handle long-running processes, such as WP-CLI
or WP_Cron
.
You can pass an optional second parameter to the Bulk_Task
constructor to output progress of the run. WP Bulk Task ships with a helper class for this called PHP_CLI_Progress_Bar
, or you can roll your own using the Progress
interface (more on this below).
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task(
'my-unique-key',
new \Alley\WP_Bulk_Task\Progress\PHP_CLI_Progress_Bar(
__( 'Bulk Task: my-unique-key', 'my-textdomain' )
)
);
By passing in an instance of a class that implements the Progress
interface, like the PHP_CLI_Progress_Bar
class above, progress will automatically be updated and output during the run of the task.
By default, the cursor will be used and updated whenever the task is run using the same unique key. Resetting the cursor is the responsibility of the implementer.
How and when the cursor is reset can take a number of different forms. However, it is generally encouraged to provide an option that resets the cursor and immediately exits the task, so that resetting the cursor and running the task can be accomplished independently, to accommodate running CLI tasks that will automatically resume if interrupted.
A rewind flag is a boolean flag that can be provided when the WP CLI command is run. It is defined in the docblock for the command and can be used like this:
/**
* Command description.
*
* ## OPTIONS
*
* [--rewind]
* : Resets the cursor so the next time the command is run it will start from the beginning.
*/
public function example_command( $args, $assoc_args ) {
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task( 'example_command' );
// Handle rewind requests.
if ( ! empty( $assoc_args['rewind'] ) ) {
$bulk_task->cursor->reset();
WP_CLI::log( __( 'Rewound the cursor. Run again without the --rewind flag to process posts.', 'my-textdomain' ) );
return;
}
// Run the command.
$bulk_task->run( /* run command arguments here */ );
}
A from scratch flag is a boolean flag that can be provided when the WP CLI command is run. It differs from a rewind flag in that it will reset the cursor and then run the command, which is useful during local development, but is less ideal for production runs, because it doesn't handle automatic restarts due to connection failures. It is defined in the docblock for the command and can be used like this:
/**
* Command description.
*
* ## OPTIONS
*
* [--from-scratch]
* : Resets the cursor before starting the command.
*/
public function example_command( $args, $assoc_args ) {
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task( 'example_command' );
// Handle from scratch requests.
if ( ! empty( $assoc_args['from-scratch'] ) ) {
$bulk_task->cursor->reset();
}
// Run the command.
$bulk_task->run( /* run command arguments here */ );
}
Commands that implement
--from-scratch
should also implement--rewind
.--rewind
should be used on runs on remote systems so that the cursor can behave as intended when the command could be interrupted and would need to be restarted.
An alternate option to a --rewind
or --from-scratch
flag is to ask the user whether they want to start the run over from the beginning or not. This can be accomplished thus:
/**
* Command description.
*/
public function example_command( $args, $assoc_args ) {
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task( 'example_command' );
// Ask the user whether to start from scratch.
fwrite( STDOUT, 'Start the run from scratch? [y/n] ' );
$from_scratch = strtolower( trim( fgets( STDIN ) ) );
if ( 'y' === $from_scratch ) {
$bulk_task->cursor->reset();
}
// Run the command.
$bulk_task->run( /* run command arguments here */ );
}
WP-CLI doesn't provide a helper method for this, but this code was lifted from WP-CLI's
confirm
method.
A common pattern when writing WP-CLI commands is to provide support for a --dry-run
flag that doesn't make any changes to the database, but outputs detailed logging about what operations would be performed. Here is an example of how to set up a command with a dry run flag that is available to the callback, and that disables the progress bar in favor of detailed logging:
/**
* Command description.
*
* ## OPTIONS
*
* [--dry-run]
* : Resets the cursor before starting the command.
*/
public function example_command( $args, $assoc_args ) {
$dry_run = ! empty( $assoc_args['dry-run'] );
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task(
'example_command',
$dry_run ? null : new \Alley\WP_Bulk_Task\Progress\PHP_CLI_Progress_Bar(
__( 'Bulk Task: my-unique-key', 'my-textdomain' )
)
);
// Optionally handle `--rewind`, `--from-scratch`, or user input here.
$bulk_task->run(
[ /* WP_Query arguments */ ],
function( $post ) use ( $dry_run ) {
$new_value = str_replace( 'apple', 'banana', $post->post_content );
if ( $dry_run ) {
WP_CLI::log( 'Old post_content: ' . $post->post_content );
WP_CLI::log( 'New post_content: ' . $new_value );
} else {
$post->post_content = $new_value;
wp_update_post( $post );
}
}
);
}
If you are using this class outside of WP-CLI, or otherwise don't want to use the PHP CLI progress bar, you can implement your own progress handler by defining a class that implements \Alley\WP_Bulk_Task\Progress\Progress
. For example:
class MyCustomProgress implements \Alley\WP_Bulk_Task\Progress\Progress {
/**
* Keeps track of the current progress toward the total.
*
* @var int
*/
private int $current = 0;
/**
* Keeps track of the total number of items.
*
* @var int
*/
private int $total = 0;
/**
* Sets the current value of the progress tracker.
*
* @param int $current The new current value for the progress tracker.
*/
public function set_current( int $current ): void {
$this->current = $current;
printf( 'Processed %d of %d' . "\n", $this->current, $this->total );
}
/**
* Tells the progress tracker that it is finished.
*/
public function set_finished(): void {
echo 'Done!' . "\n";
}
/**
* Sets the maximum value of things being counted in the progress tracker.
*
* @param int $total The total that is being counted up to.
*/
public function set_total( int $total ): void {
$this->total = $total;
}
}
An instance of this class can be passed to the second parameter of the Bulk_Task
constructor and will be automatically used by the task as it runs:
$bulk_task = new \Alley\WP_Bulk_Task\Bulk_Task( 'example_command', new MyCustomProgress() );
If your site is using a plugin to offload queries to Elasticsearch, such as SearchPress, WordPress VIP Enterprise Search, or ElasticPress, you will need to disable the query integration before running a bulk task to ensure that the queries are run directly against the database. If you fail to do so and run a query that is offloaded to Elasticsearch, the ID-based pagination will fail, causing the task to run in an infinite loop.
Add the following before running your bulk task:
add_filter( 'ep_skip_query_integration', '__return_true' );
Since version 0.2.1, this is handled by default.