Avoid wrong duplications on entity save

Created on 11 September 2023, about 1 year ago
Updated 29 September 2023, about 1 year ago

Problem/Motivation

We are using lingotek_copy_target to download the translations of the language "de", "de-at" and (gsw-berne or de-ch), being the "de" translation the original one where the others are copies of this one.
When the editor downloads the "de" translation from Lingotek and then, alters the paragraph related to the "de" translation and removes one or both of the other translations, the paragraphs related to the "de" are also removed.

Steps to reproduce

The modules installed that can be related to this problem are lingotek, lingotek_copy_target, paragraphs and paragraphs_asymmetric_translation_widgets.
Configure lingotek_copy_target to map a lingotek language to a multiple translation on Drupal, in this case, "de" (German) to "de-at" and "de-ch/gsw-berne".

  • Create a node with lingotek enabled, the content should have field paragraphs, with the translation enabled on the field.
  • Upload the translation to lingotek of the original language from node/[NID]/manage
  • Download the translation for "de"
  • Edit the "de" translation paragraphs
  • Remove the de-at or de-ch translation
  • The paragraphs will be also removed for the "de" translation

Demo video: https://www.drupal.org/files/issues/2023-09-11/3386477_demo-2023-09-11.mp4 β†’

The database will be something like this (before altering or removing any translations and after downloading from lingotek):


MySQL [drupal]> select id, revision_id, langcode, default_langcode, revision_translation_affected, content_translation_source from paragraphs_item_revision_field_data where parent_id = 655747 order by id desc;
+--------+-------------+-----------+------------------+-------------------------------+----------------------------+
| id     | revision_id | langcode  | default_langcode | revision_translation_affected | content_translation_source |
+--------+-------------+-----------+------------------+-------------------------------+----------------------------+
| 660279 |     9229067 | gsw-berne |                1 |                             1 | gsw-berne                  |
| 660279 |     9229068 | gsw-berne |                1 |                          NULL | gsw-berne                  |
| 660278 |     9229064 | de-at     |                1 |                             1 | de-at                      |
| 660278 |     9229065 | de-at     |                1 |                          NULL | de-at                      |
| 660277 |     9229063 | de        |                1 |                             1 | de                         |
| 660277 |     9229063 | de-at     |                0 |                             1 | de                         |
| 660277 |     9229063 | gsw-berne |                0 |                             1 | de                         |
| 660277 |     9229066 | de        |                1 |                          NULL | de                         |
| 660277 |     9229066 | de-at     |                0 |                          NULL | de                         |
| 660277 |     9229066 | gsw-berne |                0 |                          NULL | de                         |
| 660276 |     9229062 | en        |                1 |                             1 | und                        |
+--------+-------------+-----------+------------------+-------------------------------+----------------------------+
11 rows in set (4.093 sec)
 
MySQL [drupal]> select * from paragraphs_item where id >= 660276;
+--------+-------------+--------------+--------------------------------------+-----------+
| id     | revision_id | type         | uuid                                 | langcode  |
+--------+-------------+--------------+--------------------------------------+-----------+
| 660276 |     9229062 | article_text | 37eeb9b4-a92f-462f-b9de-f799aa821266 | en        |
| 660277 |     9229066 | article_text | d54a1837-41d0-4193-9d76-682941032817 | de        |
| 660278 |     9229065 | article_text | c96d883d-2839-4d36-b219-689caded5971 | de-at     |
| 660279 |     9229068 | article_text | bf44998b-f9a4-4b7d-ac89-77ea8aa23fc5 | gsw-berne |
+--------+-------------+--------------+--------------------------------------+-----------+
4 rows in set (0.000 sec) 

Proposed resolution

Because the module has a hook presave, it is possible that this hoos is also saving the node and the paragraphs on each translation save (paragraphs and node) https://git.drupalcode.org/project/lingotek_copy_target/-/blob/1.0.x/lin...

function lingotek_copy_target_lingotek_content_entity_translation_presave(ContentEntityInterface &$translation, $langcode, $data) {
  /** @var \Drupal\lingotek\LanguageLocaleMapperInterface $languageMapper */
  $languageMapper = \Drupal::service('lingotek.language_locale_mapper');
  $lingotek_copy_target_mappings = \Drupal::config('lingotek_copy_target.mappings')->get('map');
  foreach ($lingotek_copy_target_mappings as $key => $languageMap) {
    $original_language = $languageMap['original_language'];
    $copy_language = $languageMap['copy_language'];
    $language = $languageMapper->getConfigurableLanguageForLocale($original_language);
    if ($langcode === $language->id()) {
      $targetLanguage = $languageMapper->getConfigurableLanguageForLocale($copy_language) ?? NULL;
      if (is_null($targetLanguage) || $langcode === $targetLanguage->id()) {
        \Drupal::logger('lingotek_copy_target')->warning('%target_language has not been configured yet', ['%target_language' => $targetLanguage]);
        continue;
      }
      $translation_service = \Drupal::service('lingotek.content_translation');
      $translation_service->saveTargetData($translation, $targetLanguage->id(), $data);
    }
  }
}

It can lead to saving the entities more times than needed.

As a solution, we propose to only call saveTargetData if $translation is the original_language and is a Node, do not execute it on paragraphs.

πŸ› Bug report
Status

Fixed

Version

1.0

Component

Code

Created by

πŸ‡ͺπŸ‡ΈSpain eduardo morales alberti Spain, πŸ‡ͺπŸ‡Ί

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • πŸ‡ͺπŸ‡ΈSpain eduardo morales alberti Spain, πŸ‡ͺπŸ‡Ί
  • πŸ‡ͺπŸ‡ΈSpain eduardo morales alberti Spain, πŸ‡ͺπŸ‡Ί
  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update about 1 year ago
    run-tests.sh fatal error
  • @eduardo-morales-alberti opened merge request.
  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update about 1 year ago
    run-tests.sh fatal error
  • πŸ‡ͺπŸ‡ΈSpain eduardo morales alberti Spain, πŸ‡ͺπŸ‡Ί

    Before applying the patch and downloading the translation the paragraph tables are like this:

    
    MySQL [drupal]> select id, revision_id, langcode, default_langcode, revision_translation_affected, content_translation_source from paragraphs_item_revision_field_data where parent_id = 655747 order by id desc;
    +--------+-------------+-----------+------------------+-------------------------------+----------------------------+
    | id     | revision_id | langcode  | default_langcode | revision_translation_affected | content_translation_source |
    +--------+-------------+-----------+------------------+-------------------------------+----------------------------+
    | 660279 |     9229067 | gsw-berne |                1 |                             1 | gsw-berne                  |
    | 660279 |     9229068 | gsw-berne |                1 |                          NULL | gsw-berne                  |
    | 660278 |     9229064 | de-at     |                1 |                             1 | de-at                      |
    | 660278 |     9229065 | de-at     |                1 |                          NULL | de-at                      |
    | 660277 |     9229063 | de        |                1 |                             1 | de                         |
    | 660277 |     9229063 | de-at     |                0 |                             1 | de                         |
    | 660277 |     9229063 | gsw-berne |                0 |                             1 | de                         |
    | 660277 |     9229066 | de        |                1 |                          NULL | de                         |
    | 660277 |     9229066 | de-at     |                0 |                          NULL | de                         |
    | 660277 |     9229066 | gsw-berne |                0 |                          NULL | de                         |
    | 660276 |     9229062 | en        |                1 |                             1 | und                        |
    +--------+-------------+-----------+------------------+-------------------------------+----------------------------+
    11 rows in set (4.093 sec)
     
    MySQL [drupal]> select * from paragraphs_item where id >= 660276;
    +--------+-------------+--------------+--------------------------------------+-----------+
    | id     | revision_id | type         | uuid                                 | langcode  |
    +--------+-------------+--------------+--------------------------------------+-----------+
    | 660276 |     9229062 | article_text | 37eeb9b4-a92f-462f-b9de-f799aa821266 | en        |
    | 660277 |     9229066 | article_text | d54a1837-41d0-4193-9d76-682941032817 | de        |
    | 660278 |     9229065 | article_text | c96d883d-2839-4d36-b219-689caded5971 | de-at     |
    | 660279 |     9229068 | article_text | bf44998b-f9a4-4b7d-ac89-77ea8aa23fc5 | gsw-berne |
    +--------+-------------+--------------+--------------------------------------+-----------+
    4 rows in set (0.000 sec) 
    

    After applying the patch:

    MySQL [drupal_clean]> select * from paragraphs_item where id >= 199;
    +-----+-------------+--------------+--------------------------------------+-----------+
    | id  | revision_id | type         | uuid                                 | langcode  |
    +-----+-------------+--------------+--------------------------------------+-----------+
    | 199 |         243 | article_text | 69dcd04e-ca63-49bc-88b9-c3c0d4699c0c | en        |
    | 231 |         309 | article_text | fdb896c7-b54f-402e-9852-a182089f45ad | de        |
    | 232 |         308 | article_text | ef71e777-1b05-4351-b289-e8bd4bfaec94 | de-at     |
    | 233 |         311 | article_text | f8a4247b-ec0b-40e9-9586-a95f565fe3c1 | gsw-berne |
    +-----+-------------+--------------+--------------------------------------+-----------+
    4 rows in set (0.001 sec)
    
    MySQL [drupal_clean]> select id, revision_id, langcode, default_langcode, revision_translation_affected, content_translation_source from paragraphs_item_revision_field_data where parent_id = 48;
    +-----+-------------+-----------+------------------+-------------------------------+----------------------------+
    | id  | revision_id | langcode  | default_langcode | revision_translation_affected | content_translation_source |
    +-----+-------------+-----------+------------------+-------------------------------+----------------------------+
    | 199 |         243 | en        |                1 |                             1 | und                        |
    | 231 |         306 | de        |                1 |                             1 | de                         |
    | 232 |         307 | de-at     |                1 |                             1 | de-at                      |
    | 232 |         308 | de-at     |                1 |                          NULL | de-at                      |
    | 231 |         309 | de        |                1 |                          NULL | de                         |
    | 233 |         310 | gsw-berne |                1 |                             1 | gsw-berne                  |
    | 233 |         311 | gsw-berne |                1 |                          NULL | gsw-berne                  |
    +-----+-------------+-----------+------------------+-------------------------------+----------------------------+
    7 rows in set (0.001 sec)
    

    Demo video after applying the patch https://www.drupal.org/files/issues/2023-09-11/3386477_demo_after_patch-... β†’

    There are two revisions for each patch because the module Lingotek duplicates the paragraph if the module paragraph asymmetric is enabled https://git.drupalcode.org/project/lingotek/-/blame/4.0.1/src/Plugin/Lin...

    // If there is asymmetrical paragraphs enabled, we need a new one duplicated and stored.
    if ($paragraphTranslatable && $this->moduleHandler->moduleExists('paragraphs_asymmetric_translation_widgets')) {
      /** @var \Drupal\paragraphs\ParagraphInterface $duplicate */
      $duplicate = $embedded_entity->createDuplicate(); 
    
  • Status changed to Needs review about 1 year ago
  • πŸ‡ͺπŸ‡ΈSpain eduardo morales alberti Spain, πŸ‡ͺπŸ‡Ί

    Ready to review

  • πŸ‡ΊπŸ‡ΈUnited States npaudyal001

    @Eduardo, thanks for the fix. Looks good to me!

  • Status changed to RTBC about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States npaudyal001
  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update about 1 year ago
    run-tests.sh fatal error
  • Status changed to Fixed about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States npaudyal001
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024