/** * {@inheritdoc} */ public function getCanonicalRepresentation(TypedDataInterface $data) { $data_definition = $data->getDataDefinition(); // In case a list is passed, respect the 'wrapped' key of its data type. if ($data_definition instanceof ListDataDefinitionInterface) { $data_definition = $data_definition->getItemDefinition(); } // Get the plugin definition of the used data type. $type_definition = $this->getDefinition($data_definition->getDataType()); if (!empty($type_definition['unwrap_for_canonical_representation'])) { return $data->getValue(); } return $data; }
/** * Get a typed data instance for a property of a given typed data object. * * This method will use prototyping for fast and efficient instantiation of * many property objects with the same property path; e.g., * when multiple comments are used comment_body.0.value needs to be * instantiated very often. * Prototyping is done by the root object's data type and the given * property path, i.e. all property instances having the same property path * and inheriting from the same data type are prototyped. * * @param \Drupal\Core\TypedData\TypedDataInterface $object * The parent typed data object, implementing the TypedDataInterface and * either the ListInterface or the ComplexDataInterface. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. * @param mixed $value * (optional) The data value. If set, it has to match one of the supported * data type formats as documented by the data type classes. * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not * implement the ListInterface or the ComplexDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() */ public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { $definition = $object->getRoot()->getDataDefinition(); // If the definition is a list, we need to look at the data type and the // settings of its item definition. if ($definition instanceof ListDataDefinition) { $definition = $definition->getItemDefinition(); } $key = $definition->getDataType(); if ($settings = $definition->getSettings()) { $key .= ':' . Crypt::hashBase64(serialize($settings)); } $key .= ':' . $object->getPropertyPath() . '.'; // If we are creating list items, we always use 0 in the key as all list // items look the same. $key .= is_numeric($property_name) ? 0 : $property_name; // Make sure we have a prototype. Then, clone the prototype and set object // specific values, i.e. the value and the context. if (!isset($this->prototypes[$key]) || !$key) { // Create the initial prototype. For that we need to fetch the definition // of the to be created property instance from the parent. if ($object instanceof ComplexDataInterface) { $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); } elseif ($object instanceof ListInterface) { $definition = $object->getItemDefinition(); } else { throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface."); } // Make sure we have got a valid definition. if (!$definition) { throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.'); } // Now create the prototype using the definition, but do not pass the // given value as it will serve as prototype for any further instance. $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object); } // Clone from the prototype, then update the parent relationship and set the // data value if necessary. $property = clone $this->prototypes[$key]; $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value, FALSE); } return $property; }
/** * Creates a form element builder. * * @param \Drupal\Core\TypedData\TypedDataInterface $schema * Schema definition of configuration. * * @return \Drupal\config_translation\FormElement\ElementInterface|null * The element builder object if possible. */ public static function createFormElement(TypedDataInterface $schema) { $definition = $schema->getDataDefinition(); // Form element classes can be specified even for non-translatable elements // such as the ListElement form element which is used for Mapping and // Sequence schema elements. if (isset($definition['form_element_class'])) { if (!$definition->getLabel()) { $definition->setLabel(t('n/a')); } $class = $definition['form_element_class']; return $class::create($schema); } }
/** * Extracts field values from a typed data object. * * @param \Drupal\Core\TypedData\TypedDataInterface $data * The typed data object. * * @return array * An array of values. */ public static function extractFieldValues(TypedDataInterface $data) { if ($data->getDataDefinition()->isList()) { $values = array(); foreach ($data as $piece) { $values[] = self::extractFieldValues($piece); } return $values ? call_user_func_array('array_merge', $values) : array(); } $value = $data->getValue(); $definition = $data->getDataDefinition(); if ($definition instanceof ComplexDataDefinitionInterface) { $property = $definition->getMainPropertyName(); if (isset($value[$property])) { return array($value[$property]); } } elseif (is_array($value)) { return array_values($value); } return array($value); }
/** * Gets translatable configuration data for a typed configuration element. * * @param \Drupal\Core\TypedData\TypedDataInterface $element * Typed configuration element. * * @return array|\Drupal\Core\StringTranslation\TranslatableMarkup * A nested array matching the exact structure under $element with only the * elements that are translatable wrapped into a TranslatableMarkup. If the * provided $element is not traversable, the return value is a single * TranslatableMarkup. */ protected function getTranslatableData(TypedDataInterface $element) { $translatable = array(); if ($element instanceof TraversableTypedDataInterface) { foreach ($element as $key => $property) { $value = $this->getTranslatableData($property); if (!empty($value)) { $translatable[$key] = $value; } } } else { // Something is only translatable by Locale if there is a string in the // first place. $value = $element->getValue(); $definition = $element->getDataDefinition(); if (!empty($definition['translatable']) && $value !== '' && $value !== NULL) { $options = array(); if (isset($definition['translation context'])) { $options['context'] = $definition['translation context']; } return new TranslatableMarkup($value, array(), $options); } } return $translatable; }
/** * Constructs a FormElementBase. * * @param \Drupal\Core\TypedData\TypedDataInterface $element * The schema element this form element is for. */ public function __construct(TypedDataInterface $element) { $this->element = $element; $this->definition = $element->getDataDefinition(); }
/** * Translates element's value if it fits our translation criteria. * * For an element to be translatable by locale module it needs to be of base * type 'string' and have 'translatable = TRUE' in the element's definition. * Translatable elements may use these additional keys in their data * definition: * - 'translatable', FALSE to opt out of translation. * - 'translation context', to define the string context. * * @param \Drupal\Core\TypedData\TypedDataInterface $element * Configuration element. * @param array $options * Array with translation options that must contain the following keys: * - 'source', Source language code. * - 'target', Target language code. * * @return bool * Whether the element fits the translation criteria. */ protected function translateElement(TypedDataInterface $element, array $options) { if ($this->canTranslate($options['source'], $options['target'])) { $definition = $element->getDataDefinition(); $value = $element->getValue(); if ($value && !empty($definition['translatable'])) { $context = isset($definition['translation context']) ? $definition['translation context'] : ''; if ($translation = $this->localeConfig->translateString($this->name, $options['target'], $value, $context)) { $element->setValue($translation); return TRUE; } } } // The element does not have a translation. return FALSE; }
/** * Gets translatable configuration data for a typed configuration element. * * @param \Drupal\Core\TypedData\TypedDataInterface $element * Typed configuration element. * * @return array|\Drupal\Core\StringTranslation\TranslationWrapper * A nested array matching the exact structure under $element with only the * elements that are translatable wrapped into a TranslationWrapper. If the * provided $element is not traversable, the return value is a single * TranslationWrapper. */ protected function getTranslatableData(TypedDataInterface $element) { $translatable = array(); if ($element instanceof TraversableTypedDataInterface) { foreach ($element as $key => $property) { $value = $this->getTranslatableData($property); if (!empty($value)) { $translatable[$key] = $value; } } } else { $definition = $element->getDataDefinition(); if (!empty($definition['translatable'])) { $options = array(); if (isset($definition['translation context'])) { $options['context'] = $definition['translation context']; } return new TranslationWrapper($element->getValue(), array(), $options); } } return $translatable; }
/** * Extracts value and original type from a single piece of data. * * @param \Drupal\Core\TypedData\TypedDataInterface $data * The piece of data from which to extract information. * @param \Drupal\search_api\Item\FieldInterface $field * The field into which to put the extracted data. */ public static function extractField(TypedDataInterface $data, FieldInterface $field) { if ($data->getDataDefinition()->isList()) { foreach ($data as $piece) { self::extractField($piece, $field); } return; } $value = $data->getValue(); $definition = $data->getDataDefinition(); if ($definition instanceof ComplexDataDefinitionInterface) { $property = $definition->getMainPropertyName(); if (isset($value[$property])) { $value = $value[$property]; } } elseif (is_array($value)) { $value = reset($value); } // If the data type of the field is a custom one, then the value can be // altered by the data type plugin. $data_type_manager = \Drupal::service('plugin.manager.search_api.data_type'); if ($data_type_manager->hasDefinition($field->getType())) { $value = $data_type_manager->createInstance($field->getType())->getValue($value); } $field->addValue($value); $field->setOriginalType($definition->getDataType()); }
/** * Returns TRUE if at least one translatable element is found. * * @param \Drupal\Core\TypedData\TypedDataInterface $element * Configuration schema element. * * @return bool * A boolean indicating if there is at least one translatable element. */ protected function findTranslatable(TypedDataInterface $element) { // In case this is a sequence or a mapping check whether any child element // is translatable. if ($element instanceof TraversableTypedDataInterface) { foreach ($element as $child_element) { if ($this->findTranslatable($child_element)) { return TRUE; } } // If none of the child elements are translatable, return FALSE. return FALSE; } else { $definition = $element->getDataDefinition(); return isset($definition['translatable']) && $definition['translatable']; } }
/** * Get a typed data instance for a property of a given typed data object. * * This method will use prototyping for fast and efficient instantiation of * many property objects with the same property path; e.g., * when multiple comments are used comment_body.0.value needs to be * instantiated very often. * Prototyping is done by the root object's data type and the given * property path, i.e. all property instances having the same property path * and inheriting from the same data type are prototyped. * * @param \Drupal\Core\TypedData\TypedDataInterface $object * The parent typed data object, implementing the TypedDataInterface and * either the ListInterface or the ComplexDataInterface. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. * @param mixed $value * (optional) The data value. If set, it has to match one of the supported * data type formats as documented by the data type classes. * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not * implement the ListInterface or the ComplexDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() */ public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { // For performance, try to reuse existing prototypes instead of // constructing new objects when possible. A prototype is reused when // creating a data object: // - for a similar root object (same data type and settings), // - at the same property path under that root object. $root_definition = $object->getRoot()->getDataDefinition(); // If the root object is a list, we want to look at the data type and the // settings of its item definition. if ($root_definition instanceof ListDataDefinition) { $root_definition = $root_definition->getItemDefinition(); } // Root data type and settings. $parts[] = $root_definition->getDataType(); if ($settings = $root_definition->getSettings()) { // Hash the settings into a string. crc32 is the fastest way to hash // something for non-cryptographic purposes. $parts[] = crc32(serialize($settings)); } // Property path for the requested data object. When creating a list item, // use 0 in the key as all items look the same. $parts[] = $object->getPropertyPath() . '.' . (is_numeric($property_name) ? 0 : $property_name); $key = implode(':', $parts); // Create the prototype if needed. if (!isset($this->prototypes[$key])) { // Fetch the data definition for the child object from the parent. if ($object instanceof ComplexDataInterface) { $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); } elseif ($object instanceof ListInterface) { $definition = $object->getItemDefinition(); } else { throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface."); } if (!$definition) { throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.'); } // Create the prototype without any value, but with initial parenting // so that constructors can set up the objects correclty. $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object); } // Clone the prototype, update its parenting information, and assign the // value. $property = clone $this->prototypes[$key]; $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value, FALSE); } return $property; }