View source
<?php
namespace Drupal\default_content\Normalizer;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Plugin\DataType\EntityReference;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Installer\InstallerKernel;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\TypedData\DataReferenceTargetDefinition;
use Drupal\Core\TypedData\Plugin\DataType\Uri;
use Drupal\Core\TypedData\PrimitiveInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\path\Plugin\Field\FieldType\PathItem;
use Drupal\pathauto\PathautoState;
use Drupal\serialization\Normalizer\SerializedColumnNormalizerTrait;
use Drupal\user\UserInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
class ContentEntityNormalizer implements ContentEntityNormalizerInterface {
use SerializedColumnNormalizerTrait;
protected $entityTypeManager;
protected $moduleHandler;
protected $entityRepository;
protected $languageManager;
protected $dependencies;
public function __construct(EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, EntityRepositoryInterface $entity_repository, LanguageManagerInterface $language_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->moduleHandler = $module_handler;
$this->entityRepository = $entity_repository;
$this->languageManager = $language_manager;
}
public function normalize(ContentEntityInterface $entity) {
$normalized = [
'_meta' => [
'version' => '1.0',
'entity_type' => $entity
->getEntityTypeId(),
'uuid' => $entity
->uuid(),
],
];
$entity_type = $entity
->getEntityType();
if ($bundle_key = $entity_type
->getKey('bundle')) {
$normalized['_meta']['bundle'] = $entity
->bundle();
}
if ($langcode_key = $entity_type
->getKey('langcode')) {
$normalized['_meta']['default_langcode'] = $entity
->language()
->getId();
}
$is_root = FALSE;
if ($this->dependencies === NULL) {
$is_root = TRUE;
$this->dependencies = [];
}
$field_names = $this
->getFieldsToNormalize($entity);
if ($entity instanceof MenuLinkContentInterface) {
if (strpos($entity
->getParentId(), PluginBase::DERIVATIVE_SEPARATOR) !== FALSE) {
[
$plugin_id,
$parent_uuid,
] = explode(PluginBase::DERIVATIVE_SEPARATOR, $entity
->getParentId());
if ($plugin_id === 'menu_link_content' && ($parent_entity = $this->entityRepository
->loadEntityByUuid('menu_link_content', $parent_uuid))) {
$this
->addDependency($parent_entity);
}
}
}
foreach ($entity
->getTranslationLanguages() as $langcode => $language) {
$translation = $entity
->getTranslation($langcode);
$normalized_translation = $this
->normalizeTranslation($translation, $field_names);
if ($translation
->isDefaultTranslation()) {
$normalized['default'] = $normalized_translation;
}
else {
$normalized['translations'][$langcode] = $normalized_translation;
}
}
if ($is_root) {
if ($this->dependencies) {
$normalized['_meta']['depends'] = $this->dependencies;
}
$this->dependencies = NULL;
}
return $normalized;
}
public function denormalize(array $data) {
if (!isset($data['_meta']['entity_type'])) {
throw new UnexpectedValueException('The entity type metadata must be specified.');
}
if (!isset($data['_meta']['uuid'])) {
throw new UnexpectedValueException('The uuid metadata must be specified.');
}
$is_root = FALSE;
if ($this->dependencies === NULL && !empty($data['_meta']['depends'])) {
$is_root = TRUE;
$this->dependencies = $data['_meta']['depends'];
}
$entity_type = $this->entityTypeManager
->getDefinition($data['_meta']['entity_type']);
$values = [
'uuid' => $data['_meta']['uuid'],
];
if (!empty($data['_meta']['bundle'])) {
$values[$entity_type
->getKey('bundle')] = $data['_meta']['bundle'];
}
if (!empty($data['_meta']['default_langcode'])) {
$data = $this
->verifyNormalizedLanguage($data);
$values[$entity_type
->getKey('langcode')] = $data['_meta']['default_langcode'];
}
$entity = $this->entityTypeManager
->getStorage($entity_type
->id())
->create($values);
foreach ($data['default'] as $field_name => $values) {
$this
->setFieldValues($entity, $field_name, $values);
}
if (!empty($data['translations'])) {
foreach ($data['translations'] as $langcode => $translation_data) {
if ($this->languageManager
->getLanguage($langcode)) {
$translation = $entity
->addTranslation($langcode, $entity
->toArray());
foreach ($translation_data as $field_name => $values) {
$this
->setFieldValues($translation, $field_name, $values);
}
}
}
}
if ($is_root) {
$this->dependencies = NULL;
}
return $entity;
}
protected function setFieldValues(ContentEntityInterface $entity, string $field_name, array $values) {
if (!$entity
->hasField($field_name)) {
return;
}
foreach ($values as $delta => $item_value) {
if (!$entity
->get($field_name)
->get($delta)) {
$entity
->get($field_name)
->appendItem();
}
$item = $entity
->get($field_name)
->get($delta);
if (isset($item_value['target_uuid']) && isset($item
->getProperties()['uri'])) {
$target_entity = $this
->loadEntityDependency($item_value['target_uuid']);
if ($target_entity) {
$item_value['uri'] = 'entity:' . $target_entity
->getEntityTypeId() . '/' . $target_entity
->id();
}
unset($item_value['target_uuid']);
}
$serialized_property_names = $this
->getCustomSerializedPropertyNames($item);
foreach ($item_value as $property_name => $value) {
if (\in_array($property_name, $serialized_property_names)) {
if (\is_string($value)) {
throw new \LogicException("Received string for serialized property {$field_name}.{$delta}.{$property_name}");
}
$value = serialize($value);
}
$property = $item
->get($property_name);
if ($property instanceof EntityReference) {
if (is_array($value)) {
$target_entity = $this
->denormalize($value);
}
else {
$target_entity = $this
->loadEntityDependency($value);
}
$property
->setValue($target_entity);
}
else {
$property
->setValue($value);
}
}
}
}
protected function getFieldsToNormalize(ContentEntityInterface $entity) : array {
$fields = TypedDataInternalPropertiesHelper::getNonInternalProperties($entity
->getTypedData());
$entity_type = $entity
->getEntityType();
unset($fields[$entity_type
->getKey('id')]);
unset($fields[$entity_type
->getKey('uuid')]);
if ($revision_key = $entity_type
->getKey('revision')) {
unset($fields[$revision_key]);
}
if ($bundle_key = $entity_type
->getKey('bundle')) {
unset($fields[$bundle_key]);
}
if ($langcode_key = $entity_type
->getKey('langcode')) {
unset($fields[$langcode_key]);
unset($fields[$entity_type
->getKey('default_langcode')]);
}
if ($revision_created_key = $entity_type
->getRevisionMetadataKey('revision_created')) {
unset($fields[$revision_created_key]);
}
if ($entity_type
->id() == 'media') {
unset($fields['thumbnail']);
}
$parent_reference_keys = [
'entity_revision_parent_type_field',
'entity_revision_parent_id_field',
'entity_revision_parent_field_name_field',
];
foreach ($parent_reference_keys as $parent_reference_key) {
if ($key_field_name = $entity_type
->get($parent_reference_key)) {
unset($fields[$key_field_name]);
}
}
return array_keys($fields);
}
protected function normalizeTranslation(ContentEntityInterface $translation, array $field_names) {
$translation_normalization = [];
foreach ($field_names as $field_name) {
if ($translation
->getFieldDefinition($field_name)
->getType() == 'changed') {
continue;
}
if ($translation
->isDefaultTranslation() || $translation
->getFieldDefinition($field_name)
->isTranslatable()) {
foreach ($translation
->get($field_name) as $delta => $field_item) {
if ($field_item
->isEmpty()) {
continue;
}
$serialized_property_names = $this
->getCustomSerializedPropertyNames($field_item);
foreach ($field_item
->getProperties(TRUE) as $property_name => $property) {
$value = $this
->getValueFromProperty($property, $field_item, $translation_normalization[$field_name][$delta]);
if ($value !== NULL) {
if (is_string($value) && in_array($property_name, $serialized_property_names)) {
$value = unserialize($value);
}
$translation_normalization[$field_name][$delta][$property_name] = $value;
}
}
}
}
}
return $translation_normalization;
}
protected function getValueFromProperty(TypedDataInterface $property, FieldItemInterface $field_item, &$normalized_item = NULL) {
$value = NULL;
if ($property
->getDataDefinition() instanceof DataReferenceTargetDefinition && $field_item->entity instanceof ContentEntityInterface) {
if (!$field_item->entity) {
return NULL;
}
if (!$field_item->entity instanceof UserInterface || !in_array($field_item->entity
->id(), [
0,
1,
])) {
return NULL;
}
$value = $property
->getCastedValue();
}
elseif ($property instanceof EntityReference && $property
->getValue() instanceof ContentEntityInterface) {
$target = $property
->getValue();
if ($field_item->entity instanceof UserInterface && in_array($field_item->entity
->id(), [
0,
1,
])) {
return NULL;
}
if ($field_item
->getFieldDefinition()
->getType() == 'entity_reference_revisions' && $target
->getEntityType()
->get('entity_revision_parent_type_field')) {
$value = $this
->normalize($target);
}
else {
$this
->addDependency($target);
$value = $target
->uuid();
}
}
elseif ($property instanceof Uri) {
$value = $property
->getValue();
$scheme = parse_url($value, PHP_URL_SCHEME);
if ($scheme === 'entity') {
$path = parse_url($value, PHP_URL_PATH);
[
$target_entity_type_id,
$target_id,
] = explode('/', $path);
$target = $this->entityTypeManager
->getStorage($target_entity_type_id)
->load($target_id);
$this
->addDependency($target);
$normalized_item['target_uuid'] = $target
->uuid();
$value = NULL;
}
}
elseif ($property
->getName() == 'pid' && $field_item instanceof PathItem) {
return NULL;
}
elseif ($property instanceof PathautoState && $property
->getValue() !== NULL) {
$value = (int) $property
->getValue();
}
elseif ($property instanceof PrimitiveInterface) {
$value = $property
->getCastedValue();
}
elseif (!$property
->getDataDefinition()
->isComputed()) {
$value = $property
->getValue();
}
return $value;
}
protected function addDependency(ContentEntityInterface $entity) {
$this->dependencies[$entity
->uuid()] = $entity
->getEntityTypeId();
}
protected function loadEntityDependency(string $target_uuid) {
if (isset($this->dependencies[$target_uuid])) {
return $this->entityRepository
->loadEntityByUuid($this->dependencies[$target_uuid], $target_uuid);
}
return NULL;
}
protected function verifyNormalizedLanguage(array $data) {
if (!$this->languageManager
->getLanguage($data['_meta']['default_langcode']) || InstallerKernel::installationAttempted() && $this->languageManager
->getDefaultLanguage()
->getId() != $data['_meta']['default_langcode']) {
$use_default = TRUE;
if (isset($data['translations'])) {
foreach ($data['translations'] as $langcode => $translation_data) {
if ($this->languageManager
->getLanguage($langcode)) {
$data['_meta']['default_langcode'] = $langcode;
$data['default'] = \array_merge($data['default'], $translation_data);
unset($data['translations'][$langcode]);
$use_default = FALSE;
break;
}
}
}
if ($use_default) {
$data['_meta']['default_langcode'] = $this->languageManager
->getDefaultLanguage()
->getId();
}
}
return $data;
}
}