Subentities referenced by BaseFieldDefinition field are not duplicated

Created on 29 October 2020, almost 5 years ago
Updated 30 August 2023, about 2 years ago

Problem/Motivation

I created a custom entity and I want to be able to clone it. This entity has a reference to another custom entity through a field created by the BaseFieldDefinition::create('entity_reference') function.

When I clone a custom content, the subentities cannot be duplicated because they are not shown in the clone form confirmation (when you select if you want to duplicate subentities). Once the clone is done, the subentities referenced by the clone are the same as the original content.

I think this module is not made to handle field created by BaseFieldDefinition. In the file /src/EntityClone/Content/ContentEntityCloneFormBase.php at line 93, the code detect if the field definition is an instance of FieldConfigInterface, and it is not the case for the field created from the BaseFieldDefinition:

foreach ($entity->getFieldDefinitions() as $field_id => $field_definition) {
    if ($field_definition instanceof FieldConfigInterface && in_array($field_definition->getType(), ['entity_reference', 'entity_reference_revisions'], TRUE)) {
        // ...
    }
    // ...
}

I tagged this issue as a bug because if base fields are correctly duplicated, it is not possible to duplicate them. If it is the attended behaviour then we should update the documentation accordingly.

Steps to reproduce

  • Create a custom entity
  • Add a base field definition in the entity (in the baseFieldDefinitions function of the entity class
  • Clone the entity and see that the option to select if subentities should be be duplicated is the present

Proposed resolution

Adapt the functions formElement and getRecursiveFormElement from the ContentEntityCloneFormBase class to make it work with base field definitions. I think this is not trivial because it currently call $field_definition->id() and base field definition don't have id() function.

Remaining tasks

User interface changes

API changes

Data model changes

🐛 Bug report
Status

Active

Version

2.0

Component

Code

Created by

🇨🇭Switzerland faeby

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

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • First commit to issue fork.
  • Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.1 & MySQL 5.7
    last update about 2 years ago
    Not currently mergeable.
  • @dieterholvoet opened merge request.
  • Status changed to Needs review about 2 years ago
  • Open in Jenkins → Open on Drupal.org →
    Core: 10.1.x + Environment: PHP 8.2 & MySQL 8
    last update about 2 years ago
    27 pass, 6 fail
  • 🇺🇦Ukraine vselivanov Kyiv, Ukraine

    I have this issue with cloning custom entity and fixed it with MR 53 and custom code.

    With entity_clone 2.0.0-beta6 we have isClonable() method in src/EntityCloneClonableField.php which also checks
    $field_definition instanceof FieldConfigInterface
    But all custom reference fields are instance of BaseFieldDefinition class.
    So we need additional changes to this method.

    I tried to remove this FieldConfigInterface check, but it adds a lot of not needed fields and caused recursions and "Circular reference detected" errors.

    Finally I managed to clone custom entities with:
    1. Apply patch from the MR 53 (attached it)
    2. Extend EntityCloneClonableField class in the custom module and add hardcoded custom entity types check.

    Example with 2 files:

    1. File modules/custom/my_module/src/MyModuleServiceProvider.php

    <?php
    
    namespace Drupal\my_module;
    
    use Drupal\Core\DependencyInjection\ContainerBuilder;
    use Drupal\Core\DependencyInjection\ServiceProviderBase;
    
    /**
     * Overrides EntityCloneClonableField class.
     */
    class MyModuleServiceProvider extends ServiceProviderBase {
    
      /**
       * {@inheritdoc}
       */
      public function alter(ContainerBuilder $container) {
        if ($container->hasDefinition('entity_clone.clonable_field')) {
          /** @var \Symfony\Component\DependencyInjection\Definition $definition */
          $definition = $container->getDefinition('entity_clone.clonable_field');
          $definition->setClass('Drupal\my_module\EntityCloneClonableField');
        }
      }
    
    }
    

    2. File modules/custom/my_module/src/EntityCloneClonableField.php

    <?php
    
    namespace Drupal\my_module;
    
    use Drupal\Core\Field\BaseFieldDefinition;
    use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
    use Drupal\Core\Field\FieldDefinitionInterface;
    use Drupal\Core\Field\FieldItemListInterface;
    use Drupal\entity_clone\EntityCloneClonableField as EntityCloneClonableFieldSource;
    
    /**
     * Manage entity clone clonable field.
     */
    class EntityCloneClonableField extends EntityCloneClonableFieldSource {
    
      /**
       * {@inheritDoc}
       */
      public function isClonable(FieldDefinitionInterface $field_definition, FieldItemListInterface $field): bool {
        $isClonable = parent::isClonable($field_definition, $field);
    
        // Add custom entities fields as clonable.
        $customClonableEntityTypes = [
          'custom_entity_1',
          'custom_entity_2',
        ];
        if (!$isClonable && in_array($field_definition->getTargetEntityTypeId(), $customClonableEntityTypes)
          && $field_definition instanceof BaseFieldDefinition
          && $field instanceof EntityReferenceFieldItemListInterface && $field->count() > 0) {
          return TRUE;
        }
    
        return $isClonable;
      }
    
    }
    
  • There is one more place, where the field key variable needs to be changed: /src/EntityClone/Content/ContentEntityCloneFormBase.php line 205
    Or in human terms: The part of the content clone form, which displays referenced entities for which cloning is enforced.

  • First commit to issue fork.
  • @tgauges opened merge request.
  • 🇩🇪Germany tgauges

    I created a new branch and merge request https://git.drupalcode.org/project/entity_clone/-/merge_requests/102 base on the existing branch and merge request https://git.drupalcode.org/project/entity_clone/-/merge_requests/53.

    I adjusted the existing changes and tests and implemented a new test checking this issue's requirement.

    The main problem with the fix (1310a610) is that the clone form now also lists fields like uid, revision_uid, and similar. One could explicitly exclude those fields or define it as a feature.

  • 🇸🇮Slovenia deaom

    The tests are failing because the action is no longer part of core and the module can't be installed. Maintainers need to decide if they still want to support actions by adding a dependency to contributed action module or not and remove test.
    Tested manually with a custom entity and can confirm that without the changes in MR the user only sees the clone and cancel buttons. With the MR applied the child entities are shown. If the checkbox is checked, the fields of the referenced entity (created with Base field definition) are cloned.
    When cloning the "standard" content types, nothing changes, the cloning works as before, but I did not test with overly complicated entities.
    Will leave status to needs review in case tests do get fixed in the meantime and for somebody else to also test and confirm it's working as expected.

  • 🇸🇮Slovenia deaom

    The failing test is for previous major version (should be D10.4.5), D11 is passing. Because of that setting status to needs work.

Production build 0.71.5 2024