1<?php
2
3namespace Drupal\serialization\Normalizer;
4
5use Drupal\Component\Plugin\PluginInspectionInterface;
6use Drupal\Core\Field\FieldItemInterface;
7
8/**
9 * A trait providing methods for serialized columns.
10 */
11trait SerializedColumnNormalizerTrait {
12
13  /**
14   * Checks if there is a serialized string for a column.
15   *
16   * @param mixed $data
17   *   The field item data to denormalize.
18   * @param string $class
19   *   The expected class to instantiate.
20   * @param \Drupal\Core\Field\FieldItemInterface $field_item
21   *   The field item.
22   */
23  protected function checkForSerializedStrings($data, $class, FieldItemInterface $field_item) {
24    // Require specialized denormalizers for fields with 'serialize' columns.
25    // Note: this cannot be checked in ::supportsDenormalization() because at
26    // that time we only have the field item class. ::hasSerializeColumn()
27    // must be able to call $field_item->schema(), which requires a field
28    // storage definition. To determine that, the entity type and bundle
29    // must be known, which is contextual information that the Symfony
30    // serializer does not pass to ::supportsDenormalization().
31    if (!is_array($data)) {
32      $data = [$field_item->getDataDefinition()->getMainPropertyName() => $data];
33    }
34    if ($this->dataHasStringForSerializeColumn($field_item, $data)) {
35      $field_name = $field_item->getParent() ? $field_item->getParent()->getName() : $field_item->getName();
36      throw new \LogicException(sprintf('The generic FieldItemNormalizer cannot denormalize string values for "%s" properties of the "%s" field (field item class: %s).', implode('", "', $this->getSerializedPropertyNames($field_item)), $field_name, $class));
37    }
38  }
39
40  /**
41   * Checks if the data contains string value for serialize column.
42   *
43   * @param \Drupal\Core\Field\FieldItemInterface $field_item
44   *   The field item.
45   * @param array $data
46   *   The data being denormalized.
47   *
48   * @return bool
49   *   TRUE if there is a string value for serialize column, otherwise FALSE.
50   */
51  protected function dataHasStringForSerializeColumn(FieldItemInterface $field_item, array $data) {
52    foreach ($this->getSerializedPropertyNames($field_item) as $property_name) {
53      if (isset($data[$property_name]) && is_string($data[$property_name])) {
54        return TRUE;
55      }
56    }
57    return FALSE;
58  }
59
60  /**
61   * Gets the names of all serialized properties.
62   *
63   * @param \Drupal\Core\Field\FieldItemInterface $field_item
64   *   The field item.
65   *
66   * @return string[]
67   *   The property names for serialized properties.
68   */
69  protected function getSerializedPropertyNames(FieldItemInterface $field_item) {
70    $field_storage_definition = $field_item->getFieldDefinition()->getFieldStorageDefinition();
71
72    if ($custom_property_names = $this->getCustomSerializedPropertyNames($field_item)) {
73      return $custom_property_names;
74    }
75
76    $field_storage_schema = $field_item->schema($field_storage_definition);
77    // If there are no columns then there are no serialized properties.
78    if (!isset($field_storage_schema['columns'])) {
79      return [];
80    }
81    $serialized_columns = array_filter($field_storage_schema['columns'], function ($column_schema) {
82      return isset($column_schema['serialize']) && $column_schema['serialize'] === TRUE;
83    });
84    return array_keys($serialized_columns);
85  }
86
87  /**
88   * Gets the names of all properties the plugin treats as serialized data.
89   *
90   * This allows the field storage definition or entity type to provide a
91   * setting for serialized properties. This can be used for fields that
92   * handle serialized data themselves and do not rely on the serialized schema
93   * flag.
94   *
95   * @param \Drupal\Core\Field\FieldItemInterface $field_item
96   *   The field item.
97   *
98   * @return string[]
99   *   The property names for serialized properties.
100   */
101  protected function getCustomSerializedPropertyNames(FieldItemInterface $field_item) {
102    if ($field_item instanceof PluginInspectionInterface) {
103      $definition = $field_item->getPluginDefinition();
104      $serialized_fields = $field_item->getEntity()->getEntityType()->get('serialized_field_property_names');
105      $field_name = $field_item->getFieldDefinition()->getName();
106      if (is_array($serialized_fields) && isset($serialized_fields[$field_name]) && is_array($serialized_fields[$field_name])) {
107        return $serialized_fields[$field_name];
108      }
109      if (isset($definition['serialized_property_names']) && is_array($definition['serialized_property_names'])) {
110        return $definition['serialized_property_names'];
111      }
112    }
113    return [];
114  }
115
116}
117