[PP-1] Order's Adjustment can't be normalized and serialized

Created on 14 October 2017, almost 7 years ago
Updated 5 August 2024, about 2 months ago

Problem/Motivation

Order's Adjustment can't be normalized, So the field adjustments can't been serialized
there is a core bug cause this, #2915705: TypedData 'Any' can't be normalized to array if it stores an object
the core will not recursive normalzie object,

But the resolution require that object itself saved to a 'any' type data must been normalizable or traversable

Proposed resolution

Make Adjustment been traversable

Remaining tasks

make Adjustment implement IteratorAggregate

User interface changes

API changes

Data model changes

🐛 Bug report
Status

Needs review

Version

2.0

Component

Order

Created by

🇨🇳China lawxen

Live updates comments and jobs are added and updated live.
  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

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.

  • 🇮🇳India Akhil Babu Chengannur

    Thanks for the patch. Adjustment values are now getting displayed in API response after applying the latest patch from https://www.drupal.org/project/drupal/issues/2915705 and then 2916252-66.patch.
    But there was a waring after applying 2916252-66.patch.
    Deprecated function: Return type of Drupal\commerce_order\Adjustment::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in include()
    Updating 2916252-66.patch to remove the warning.
    Change

      public function getIterator() {
        return new \ArrayIterator($this->toArray());
      }

    to

      public function getIterator(): \ArrayIterator {
        return new \ArrayIterator($this->toArray());
      }
  • 🇨🇾Cyprus alex.bukach

    It should be \Traversable, not \ArrayIterator.

  • 🇫🇷France Agence Web CoherActio

    The last patch caused side-effects (e.g. price substract() method with adjustment).
    I ended up extending Drupal TypedDataNormalizer class to support Ajdustment normalizer

    namespace Drupal\my_module\Normalizer;
    
    use Drupal\commerce_order\Adjustment;
    use Drupal\Core\TypedData\TypedDataInterface;
    use Drupal\serialization\Normalizer\TypedDataNormalizer;
    
    class MyModuleAdjustmentNormalizer extends TypedDataNormalizer {
    
      /**
       * {@inheritdoc}
       */
      public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
        $this->addCacheableDependency($context, $object);
        $value = $object->getValue();
        // Support for stringable value objects: avoid numerous custom normalizers.
        if (is_object($value) && method_exists($value, '__toString')) {
          $value = (string) $value;
        }
        if ($value instanceof Adjustment) {
          $value = $value->toArray();
        }
        return $value;
      }
    }
    
  • 🇮🇱Israel jsacksick

    The attached patch works... However I don't think we can commit it as this means Commerce just provides a normalizer for the "Any" data type, which is used by other field types... which is a problem... But attaching the patch anyway..

  • 🇮🇱Israel jsacksick

    Perhaps we could provide our normalizer, only if the core one introduced in #2915705: TypedData 'Any' can't be normalized to array if it stores an object doesn't exist?

  • Status changed to Needs review 4 months ago
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    794 pass
  • 🇮🇱Israel jsacksick

    Actually, let's go with this patch... Which provides the "Any" normalizer conditionally in case it is not defined by the serialization module.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    794 pass
  • Pipeline finished with Success
    4 months ago
    Total: 505s
    #184882
  • First commit to issue fork.
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    CI error
  • Pipeline finished with Failed
    4 months ago
    Total: 491s
    #187226
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    Unable to generate test groups
  • Pipeline finished with Success
    4 months ago
    Total: 521s
    #187258
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    Unable to generate test groups
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    Unable to generate test groups
  • Pipeline finished with Canceled
    4 months ago
    Total: 46s
    #187267
  • Pipeline finished with Success
    4 months ago
    Total: 490s
    #187268
  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update 4 months ago
    794 pass, 2 fail
  • Pipeline finished with Failed
    4 months ago
    Total: 478s
    #190748
  • 🇮🇱Israel jsacksick

    We were missing the test group (added that). But also suddenly not sure about the fix, whenever I don't skip the test methods defined by the base class, there seems to be an issue with the denormalization of the adjustments field... Also... now extending getSupportedTypes() from the normalizer as the previous approach :

      /**
       * {@inheritdoc}
       */
      protected $supportedInterfaceOrClass = Any::class;
    

    is deprecated in D10.2. I'd need to double check when this change was introduced to see if we need ot raise the required core version... Since I'm not sure the issue is actually fixed... Putting this issue on hold for now.

  • 🇺🇸United States mrweiner

    @jsacksick re: #80, I can confirm that the patch in #77 fixes the issue for me on D10.3.0.

  • Pipeline finished with Skipped
    3 months ago
    #213298
  • 🇮🇹Italy trickfun

    Patch #77 works fine with Drupal 10.3
    Thank you

  • 🇮🇱Israel jsacksick

    Last time I worked on the tests, they were failing, and can't remember why, so looks like the patch doesn't address all the problems...

  • 🇮🇹Italy trickfun

    @jsacksick i have this patch too https://www.drupal.org/project/drupal/issues/3300404 📌 Unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in Drupal\Core\Entity\Sql\SqlContentEntityStorage Needs work

  • Pipeline finished with Failed
    about 2 months ago
    #240684
  • Pipeline finished with Success
    about 2 months ago
    #243638
  • Pipeline finished with Success
    about 2 months ago
    Total: 450s
    #243650
  • Pipeline finished with Success
    about 2 months ago
    Total: 453s
    #243658
  • 🇮🇱Israel jsacksick

    @tBKoT: Interesting approach, just wondering if this isn't a breaking change?
    Also, we might as well copy what was done in Commerce API. The Commerce API module defines an Adjustment data type as well as an AdjustmentDataDefinition definition class.

    The data definition class defines all the adjustment properties. This should help getting rid of the "value".

  • 🇺🇦Ukraine tBKoT

    @jsacksick I've tried to get rid of the "value" key but it seems that it is not possible without rewriting a lot of staff or removing the denormalization process in the test (it would fail in this case)

    The main thing is that JSON:API uses the field property definition as a foundation to generate the structure if we change it as a result all other things related to this field would be broken such as getAdjustments() etc. as those methods use "value" and I guess many other contrib/custom solutions may use this field like that.

    Another thing that can help is to define more properties with their own data types to set values for Label, Type, Amount, etc. I do not think it is the best way to resolve this.

    One more solution is to disable access to the main "adjustment" field for JSON:API as it is done in the "commerce_api" module and define a new computed field for it to show adjustment without the "value" key.

  • Pipeline finished with Success
    about 2 months ago
    Total: 546s
    #248022
  • Pipeline finished with Success
    23 days ago
    Total: 445s
    #275317
  • Pipeline finished with Skipped
    22 days ago
    #275682
Production build 0.71.5 2024