traits should be installed at the API level, not the form level

Created on 25 August 2017, over 7 years ago
Updated 2 October 2024, 3 months ago

Currently, trait fields are added in CommerceBundleEntityFormBase when a trait is selected. This means that creating and saving an entity through the API, as you would in a test, doesn't add trait fields:

    $this->createEntity('commerce_order_item_type', [
      'id' => 'license_order_item_type',
      'label' => $this->randomMachineName(),
      'purchasableEntityType' => 'commerce_product_variation',
      'orderType' => 'license_order_type',
      'traits' => ['commerce_license_order_item_type'],
    ]);

The above code ought to install the trait's fields.

This also will affect:

- migrations
- entity clone form

📌 Task
Status

Needs review

Version

2.0

Component

Developer experience

Created by

🇬🇧United Kingdom joachim

Live updates comments and jobs are added and updated live.
  • Novice

    It would make a good project for someone who is new to the Drupal contribution process. It's preferred over Newbie.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

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

  • 🇬🇧United Kingdom daniel.j

    Had a look at this issue as part of getting into contributing.

    Attached is a new patch, hopfully this was the intention of a cleanup. The `submitTraitForm` from `CommerceBundleEntityFormBase.php` has been removed, as well as the calls in the extending Forms.

  • 🇮🇱Israel jsacksick

    Let's change the target branch... Could you please open an MR to see if the tests are passing? I wonder if we have tests coverage for trait installation / uninstallation.

  • 🇬🇧United Kingdom daniel.j

    Merge request created

  • Pipeline finished with Failed
    2 months ago
    Total: 551s
    #305140
  • 🇮🇱Israel jsacksick

    Tests are failing, setting the status to "Needs work".

  • 🇬🇧United Kingdom daniel.j

    The test that is failing is the 'testDuplicateTraits' test of 'Drupal\Tests\commerce\Functional\EntityTraitTest'.

    When an entity type is cloned from an entity type with an EntityTrait that provides a field, the test checks that by removing the EntityTrait and saving the new cloned entity type the provided field is also removed. As the entity being saved returns TRUE when it's 'isNew()' method is called, the save logic assumes that there are no original traits.

        if ($this->isNew()) {
          $original_traits = [];
        } else {
          $original = $this->entityTypeManager()->getStorage($this->getEntityTypeId())->loadUnchanged($this->id());
          $original_traits = $original->getTraits();
        }
    

    When the trait handling logic was handled at the form level, it was called after the entity had been saved, so at that point the entity was no longer new and the original traits from the cloned entity were loaded.

    I feel like we should either:
    A) Not go ahead with this change now that we know why the traits are handled at the form level and not the API level. Perhaps adding a comment to explain why.
    B) Not assume that just because an entity is new it has no traits as this does not account for the situation where the new entity has been cloned from an entity that has traits.

    I hope this is helpful and not poorly explained. I'll be interested to hear your thoughts.

  • 🇮🇱Israel jsacksick

    hm... What about using postSave() then?

  • 🇬🇧United Kingdom daniel.j

    Moving the logic to the 'postSave' has its own issues due to the way that the 'selected_traits' and 'unselected_traits' are calculated.

        $selected_traits = array_diff($traits, $original_traits);
        $unselected_traits = array_diff($original_traits, $traits);
    

    With this inside of the 'postSave', there is no longer visability of the original traits on the entity (unless I'm missing something) in order to work out what traits have been added and removed as the new selection has already been saved to the entity.

  • 🇮🇱Israel jsacksick

    And do you know why we aren't calling the parent presave() method first? Rather than accessing the original traits prior to that?

    Considering the small interest expressed with this, wondering if we should really spend time "fixing" this.

  • 🇬🇧United Kingdom daniel.j

    Having a further look, the fields from the source entity are coppied accross to the cloned entity in the 'postSave' of 'EntityDuplicateFormTrait', which is called after the entity save.

    Even if in the entity save we handle the added / removed traits' fields, they are added again afterward in the 'SourceTypeForm'. I think that this is why the trait logic is handled at the form level, so that it can be the last word after both the entity saves and 'EntityDuplicateFormTrait'.

    I think that there would have been value in making it clearer as to why it's done in this order. Do you think that it would be good to add a comment to explain why it has been done this way?

    Considering the small interest expressed with this, wondering if we should really spend time "fixing" this.

    Personally, I've been looking at this issue as part of my efforts to start getting into making contributions, so I've been spending time on it exactly because it's not high priority :)

  • 🇮🇱Israel jsacksick

    I think that there would have been value in making it clearer as to why it's done in this order. Do you think that it would be good to add a comment to explain why it has been done this way?

    Definitely!

  • Pipeline finished with Success
    2 months ago
    Total: 716s
    #305494
  • 🇬🇧United Kingdom daniel.j

    This means that creating and saving an entity through the API, as you would in a test, doesn't add trait fields

    Looking back at the issue description, I think that the issue is still valid. However, the postSave method of EntityDuplicateFormTrait (which dispatches an event listened to by EntityDuplicateSubscriber) would need to not re-add fields that have been removed in the entity save or postSave as part of the removal of an EntityTrait.

  • Pipeline finished with Failed
    2 months ago
    Total: 486s
    #307453
  • Pipeline finished with Failed
    2 months ago
    #309268
  • Pipeline finished with Failed
    2 months ago
    Total: 455s
    #309324
  • Pipeline finished with Success
    2 months ago
    Total: 626s
    #309343
  • Pipeline finished with Success
    2 months ago
    Total: 449s
    #309362
  • Pipeline finished with Canceled
    2 months ago
    Total: 198s
    #309472
  • Pipeline finished with Success
    2 months ago
    Total: 453s
    #309476
  • Pipeline finished with Canceled
    2 months ago
    Total: 133s
    #311702
  • Pipeline finished with Success
    2 months ago
    Total: 477s
    #311703
  • Pipeline finished with Success
    2 months ago
    Total: 549s
    #311720
  • Pipeline finished with Success
    2 months ago
    Total: 482s
    #311726
  • Pipeline finished with Canceled
    4 days ago
    Total: 97s
    #372282
  • Pipeline finished with Failed
    4 days ago
    Total: 174s
    #372286
  • Pipeline finished with Failed
    4 days ago
    Total: 278s
    #372295
  • Pipeline finished with Failed
    4 days ago
    Total: 313s
    #372305
  • Pipeline finished with Success
    about 16 hours ago
    Total: 844s
    #376000
Production build 0.71.5 2024