vendor/shopware/core/Framework/DataAbstractionLayer/FieldSerializer/JsonFieldSerializer.php line 37

  1. <?php
  2. declare(strict_types=1);
  3. namespace Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer;
  4. use Shopware\Core\Defaults;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InvalidSerializerFieldException;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Field\DateField;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Field\Field;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Field\JsonField;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Write\DataStack\DataStack;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Write\DataStack\KeyValuePair;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Write\EntityExistence;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Write\FieldException\UnexpectedFieldException;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Write\FieldException\WriteFieldException;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Write\WriteParameterBag;
  16. use Shopware\Core\Framework\Feature;
  17. use Shopware\Core\Framework\Log\Package;
  18. use Shopware\Core\Framework\Util\Json;
  19. use Symfony\Component\Validator\Constraints\NotNull;
  20. use Symfony\Component\Validator\Constraints\Type;
  21. /**
  22.  * @internal
  23.  */
  24. #[Package('core')]
  25. class JsonFieldSerializer extends AbstractFieldSerializer
  26. {
  27.     /**
  28.      * @deprecated tag:v6.6.0 - Will be removed, use \Shopware\Core\Framework\Util\Json::encode instead
  29.      */
  30.     public static function encodeJson(mixed $valueint $options \JSON_UNESCAPED_UNICODE \JSON_PRESERVE_ZERO_FRACTION \JSON_THROW_ON_ERROR \JSON_INVALID_UTF8_IGNORE): string
  31.     {
  32.         Feature::triggerDeprecationOrThrow(
  33.             'v6.6.0.0',
  34.             Feature::deprecatedMethodMessage(self::class, __METHOD__'v6.6.0.0'Json::class . '::encode')
  35.         );
  36.         return (string) json_encode($value$options);
  37.     }
  38.     public function encode(
  39.         Field $field,
  40.         EntityExistence $existence,
  41.         KeyValuePair $data,
  42.         WriteParameterBag $parameters
  43.     ): \Generator {
  44.         if (!$field instanceof JsonField) {
  45.             throw new InvalidSerializerFieldException(JsonField::class, $field);
  46.         }
  47.         $this->validateIfNeeded($field$existence$data$parameters);
  48.         $value $data->getValue() ?? $field->getDefault();
  49.         if ($value !== null && !empty($field->getPropertyMapping())) {
  50.             $value $this->validateMapping($field$value$parameters);
  51.         }
  52.         if ($value !== null) {
  53.             $value Json::encode($value);
  54.         }
  55.         yield $field->getStorageName() => $value;
  56.     }
  57.     public function decode(Field $fieldmixed $value): mixed
  58.     {
  59.         if (!$field instanceof JsonField) {
  60.             throw new InvalidSerializerFieldException(JsonField::class, $field);
  61.         }
  62.         if ($value === null) {
  63.             return $field->getDefault();
  64.         }
  65.         $raw json_decode((string) $valuetrue);
  66.         $decoded $raw;
  67.         if (empty($field->getPropertyMapping())) {
  68.             return $raw;
  69.         }
  70.         foreach ($field->getPropertyMapping() as $embedded) {
  71.             $key $embedded->getPropertyName();
  72.             if (!isset($raw[$key])) {
  73.                 continue;
  74.             }
  75.             $value $embedded instanceof JsonField
  76.                 Json::encode($raw[$key])
  77.                 : $raw[$key];
  78.             $embedded->compile($this->definitionRegistry);
  79.             $decodedValue $embedded->getSerializer()->decode($embedded$value);
  80.             if ($decodedValue instanceof \DateTimeInterface) {
  81.                 $format $embedded instanceof DateField Defaults::STORAGE_DATE_FORMAT \DATE_ATOM;
  82.                 $decodedValue $decodedValue->format($format);
  83.             }
  84.             $decoded[$key] = $decodedValue;
  85.         }
  86.         return $decoded;
  87.     }
  88.     protected function getConstraints(Field $field): array
  89.     {
  90.         return [
  91.             new Type('array'),
  92.             new NotNull(),
  93.         ];
  94.     }
  95.     protected function validateMapping(
  96.         JsonField $field,
  97.         array $data,
  98.         WriteParameterBag $parameters
  99.     ): array {
  100.         if (\array_key_exists('_class'$data)) {
  101.             unset($data['_class']);
  102.         }
  103.         $stack = new DataStack($data);
  104.         $existence = new EntityExistence(null, [], falsefalsefalse, []);
  105.         $fieldPath $parameters->getPath() . '/' $field->getPropertyName();
  106.         $propertyKeys array_map(fn (Field $field) => $field->getPropertyName(), $field->getPropertyMapping());
  107.         // If a mapping is defined, you should not send properties that are undefined.
  108.         // Sending undefined fields will throw an UnexpectedFieldException
  109.         $keyDiff array_diff(array_keys($data), $propertyKeys);
  110.         if (\count($keyDiff)) {
  111.             foreach ($keyDiff as $fieldName) {
  112.                 $parameters->getContext()->getExceptions()->add(
  113.                     new UnexpectedFieldException($fieldPath '/' $fieldName, (string) $fieldName)
  114.                 );
  115.             }
  116.         }
  117.         foreach ($field->getPropertyMapping() as $nestedField) {
  118.             $kvPair $stack->pop($nestedField->getPropertyName());
  119.             if ($kvPair === null) {
  120.                 // The writer updates the whole field, so there is no possibility to update
  121.                 // "some" fields. To enable a merge, we have to respect the $existence state
  122.                 // for correct constraint validation. In addition the writer has to be rewritten
  123.                 // in order to handle merges.
  124.                 if (!$nestedField->is(Required::class)) {
  125.                     continue;
  126.                 }
  127.                 $kvPair = new KeyValuePair($nestedField->getPropertyName(), nulltrue);
  128.             }
  129.             $nestedParams = new WriteParameterBag(
  130.                 $parameters->getDefinition(),
  131.                 $parameters->getContext(),
  132.                 $parameters->getPath() . '/' $field->getPropertyName(),
  133.                 $parameters->getCommandQueue()
  134.             );
  135.             /*
  136.              * Don't call `encode()` or `validateIfNeeded()` on nested JsonFields if they are not typed.
  137.              * This also allows directly storing non-array values like strings.
  138.              */
  139.             if ($nestedField instanceof JsonField && empty($nestedField->getPropertyMapping())) {
  140.                 // Validate required flag manually
  141.                 if ($nestedField->is(Required::class)) {
  142.                     $this->validate([new NotNull()], $kvPair$nestedParams->getPath());
  143.                 }
  144.                 $stack->update($kvPair->getKey(), $kvPair->getValue());
  145.                 continue;
  146.             }
  147.             try {
  148.                 $nestedField->compile($this->definitionRegistry);
  149.                 $encoded $nestedField->getSerializer()->encode($nestedField$existence$kvPair$nestedParams);
  150.                 foreach ($encoded as $fieldKey => $fieldValue) {
  151.                     if ($nestedField instanceof JsonField && $fieldValue !== null) {
  152.                         $fieldValue json_decode((string) $fieldValuetrue512\JSON_THROW_ON_ERROR);
  153.                     }
  154.                     $stack->update($fieldKey$fieldValue);
  155.                 }
  156.             } catch (WriteFieldException $exception) {
  157.                 $parameters->getContext()->getExceptions()->add($exception);
  158.             }
  159.         }
  160.         return $stack->getResultAsArray();
  161.     }
  162. }