ACF\CLI
JsonCommand::sync │ public │ ACF 6.8 Syncs local JSON changes to the database.
Imports pending JSON changes for ACF items (field groups, post types, taxonomies, and options pages). This command reads JSON files from your theme/plugin acf-json directory and creates or updates the corresponding database entries.
WARNING: This command modifies your database. Use --dry-run first to preview changes before running on production.
OPTIONS
[--type=<type>]
Limit sync to a specific item type. Defaults to all item types (field groups, post types, taxonomies, options pages).
--- options:
field-group
post-type
taxonomy
options-page
[--key=<key>]
Sync a specific item by its ACF key (e.g., group_abc123).
[--dry-run]
Preview what would be synced without making changes. Recommended for production deployments.
EXAMPLES
# Preview what will be synced (safe)
$ wp acf json sync --dry-run
3 item(s) pending sync:
+-------------------+------------------+---------------+--------+
| Key | Title | Type | Action |
+-------------------+------------------+---------------+--------+
| group_abc123 | Product Fields | field-group | Update |
+-------------------+------------------+---------------+--------+
# Sync all pending changes
$ wp acf json sync
Updated field-group: Product Fields (group_abc123)
Success: 1 item(s) synced.
# Sync only field groups (during deployment)
$ wp acf json sync --type=field-group
# Sync a specific field group after manual JSON edit
$ wp acf json sync --key=group_abc123
# CI/CD deployment workflow
$ wp acf json status --format=json | jq '.[] | select(.Pending > 0)'
$ wp acf json sync --dry-run
$ wp acf json sync
Method of the class: JsonCommand{}
No Hooks.
Returns
null. Nothing (null).
Usage
$JsonCommand = new JsonCommand();
$JsonCommand->sync( $args, $assoc_args );
$args(array) (required)
Positional arguments.
$assoc_args(array) (required)
Associative arguments.
Changelog
JsonCommand::sync() JsonCommand::sync code
ACF 6.8.8
public function sync( $args, $assoc_args ) {
$this->log_command( 'sync' );
$type_filter = get_flag_value( $assoc_args, 'type' );
$key_filter = get_flag_value( $assoc_args, 'key' );
$dry_run = get_flag_value( $assoc_args, 'dry-run', false );
$post_types = $this->get_post_types( $type_filter );
$all_syncable = array();
foreach ( $post_types as $post_type ) {
$syncable = $this->get_syncable_items( $post_type );
foreach ( $syncable as $key => $post ) {
$all_syncable[ $key ] = array(
'post' => $post,
'post_type' => $post_type,
);
}
}
if ( $key_filter ) {
if ( ! isset( $all_syncable[ $key_filter ] ) ) {
WP_CLI::error(
sprintf(
"No syncable item found with key '%s'.\n\n" .
"Possible reasons:\n" .
" - Key does not exist in JSON files\n" .
" - Item is already in sync with database\n" .
" - Item is marked as private\n\n" .
"To see all syncable items, run:\n" .
' wp acf json sync --dry-run',
$key_filter
)
);
}
$all_syncable = array( $key_filter => $all_syncable[ $key_filter ] );
}
if ( empty( $all_syncable ) ) {
WP_CLI::success( self::MESSAGE_ALREADY_IN_SYNC );
return;
}
if ( $dry_run ) {
$this->display_dry_run( $all_syncable );
return;
}
// Disable Local JSON controller to prevent .json files from being modified during import.
$json_enabled = acf_get_setting( 'json' );
acf_update_setting( 'json', false );
// Build file index per post type before the loop (matches admin UI pattern).
$files_by_type = array();
foreach ( $all_syncable as $item ) {
$pt = $item['post_type'];
if ( ! isset( $files_by_type[ $pt ] ) ) {
$files_by_type[ $pt ] = acf_get_local_json_files( $pt );
}
}
$synced_count = 0;
foreach ( $all_syncable as $key => $item ) {
$post = $item['post'];
$post_type = $item['post_type'];
$files = $files_by_type[ $post_type ];
if ( ! isset( $files[ $key ] ) ) {
WP_CLI::warning(
sprintf(
"JSON file not found for key '%s'. Skipping.\n" .
'The JSON file may have been deleted or moved.',
$key
)
);
continue;
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$local_post = json_decode( file_get_contents( $files[ $key ] ), true );
if ( ! is_array( $local_post ) ) {
WP_CLI::warning( sprintf( "Invalid JSON in file for key '%s'. Skipping.", $key ) );
continue;
}
$local_post['ID'] = $post['ID'];
$result = acf_import_internal_post_type( $local_post, $post_type );
if ( empty( $result ) || ! isset( $result['ID'] ) ) {
WP_CLI::warning( sprintf( "Failed to sync item with key '%s'.", $key ) );
continue;
}
$action = $post['ID'] ? 'Updated' : 'Created';
$type_label = $this->get_type_label( $post_type );
WP_CLI::log( sprintf( '%s %s: %s (%s)', $action, $type_label, $post['title'], $key ) );
++$synced_count;
}
// Restore Local JSON setting.
acf_update_setting( 'json', $json_enabled );
if ( 0 === $synced_count ) {
WP_CLI::warning( 'No items were synced.' );
return;
}
WP_CLI::success( sprintf( '%d item(s) synced.', $synced_count ) );
}