This thread is resolved. Here is a description of the problem and solution.
Problem: If you're experiencing performance issues when copying Advanced Custom Fields (ACF) to translations with WPML, such as slow operations or timeouts, this might be due to inefficiencies in the code handling the copy process.
Solution: We recommend modifying the
SitePress::copy_custom_fields
and
WPML_Copy_Once_Custom_Field::copy
methods to improve performance. Here are the steps to follow:
These changes should optimize the process by reducing the number of instances where the entire post meta is fetched and looped through, thus improving the performance when copying ACF fields to translations.
I work for Pantheon (hosting company) and I have been investigating some performance issues related to WPML and custom fields. Specifically, we have a customer site who uses WPML and has hundreds of ACF fields on their site. Whenever, they add a translation to an existing post, and save it, copying all of the ACF fields to the translation is very slow and often times out.
I have narrowed down the issue to inefficiencies in WPML's code when performing this operation.
The process starts in
WPML_Copy_Once_Custom_Field->copy()
method which gets all of the post meta from the original post, and loops through it to copy each field one by one by calling
SitePress->sync_custom_field()
which then instantiates a new instance of
WPML_Sync_Custom_Fields
for each field and calls that class'
sync_custom_field()
method.
Even though `sync_custom_field()` is provided the `$meta_key` to copy, this function doesn't actually copy a single field as you'd expect. Instead, it retrieves all of the post meta from both the original and copied posts, performs a diff of the meta keys, deletes any that aren't present in the original post and then loops through all of the meta keys to copy them to the new copied post.
This entire loop of instantiating `WPML_Sync_Custom_Fields` and fetching + looping through every single post meta is then repeated for every meta field in the original post. In essence, each meta field creates its own loop of every meta field.
As you can imagine, when doing so for hundreds of fields this creates a significant performance bottleneck.
It should be entirely possible to refactor this code in a way that it would only perform this loop a single time. Would it be possible for the WPML team to review and improve this?
Languages: English (English )German (Deutsch )French (Français )
Timezone: Europe/Zagreb (GMT+01:00)
Hi,
Thank you for contacting WPML support!
We are familiar with the issue that more ACF fields can cause slowness because of the synchronization, especially if more languages coupled with more ACF fields, I brought this up to developers to see if this gives us some new ideas.
Also in \WPML_Copy_Once_Custom_Field::copy in \wp-content\plugins\sitepress-multilingual-cms\classes\custom-field-translation\class-wpml-copy-once-custom-field.php
change the method to:
public function copy( $post_id ) {
$custom_fields_to_copy = $this->sitepress->get_custom_fields_translation_settings( WPML_COPY_ONCE_CUSTOM_FIELD );
if ( empty( $custom_fields_to_copy ) ) {
return;
}
$source_element_id = $this->wpml_post_translation->get_original_element( $post_id );
$custom_fields = get_post_meta( $post_id );
foreach ( $custom_fields_to_copy as $meta_key ) {
if ( array_key_exists( $meta_key, $custom_fields ) ) {
$values = isset( $custom_fields[ $meta_key ] )
&& ! empty( $custom_fields[ $meta_key ] )
? [ $custom_fields[ $meta_key ] ]
: [];
/**
* Custom fields values for given post obtained directly from database
*
* @param array<mixed> $values Custom fields values as they are in the database
* @param array<int|string> $args {
*
* @type int $post_id ID of post associated with custom field
* @type string $meta_key custom fields meta key
* @type int $custom_fields_translation field translation option
*
* }
* @since 4.1
*
*/
$values = apply_filters(
'wpml_custom_field_values',
$values,
[
'post_id' => $post_id,
'meta_key' => $meta_key,
'custom_fields_translation' => WPML_COPY_ONCE_CUSTOM_FIELD,
]
);
if ( empty( $values ) && $source_element_id ) {
$this->sitepress->sync_custom_field( $source_element_id, $post_id, $meta_key );
}
}
}
}
Thanks so much for the quick turnaround and potential code fix here. After some initial testing, this does seem to significantly improve the performance of saving multilingual posts. I will work with our customer to do some additional testing and validating of the solution.
Can we expect to see this solution patched in an upcoming update of WPML?