blacksmith-8.x-1.x-dev/src/Blacksmith/EntityImporter/EntityImporter.php
src/Blacksmith/EntityImporter/EntityImporter.php
<?php
namespace Drupal\blacksmith\Blacksmith\EntityImporter;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\blacksmith\Exception\BlacksmithImportSkip;
use Drupal\blacksmith\Blacksmith\EntityImporter\FieldFormatter\FieldFormatterFactory;
use Drupal\blacksmith\BlacksmithItem;
use Drupal\blacksmith\BlacksmithTranslation;
/**
* Class EntityImporter.
*
* @package Drupal\blacksmith\Blacksmith\EntityImporter
*/
abstract class EntityImporter implements EntityImporterInterface {
use LoggerChannelTrait;
use MessengerTrait;
/**
* Entity storage used to create the entities.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $entityStorage;
/**
* Entity field manager service.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* Field formatter factory service.
*
* @var \Drupal\blacksmith\Blacksmith\EntityImporter\FieldFormatter\FieldFormatterFactory
*/
protected $fieldFormatterFactory;
/**
* Entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Entity importer factory service.
*
* @var \Drupal\blacksmith\Blacksmith\EntityImporter\EntityImporterFactory
*/
protected $entityImporterFactory;
/**
* NodeEntityImporter constructor.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $entityStorage
* Node entity storage.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* Entity type manager service.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
* Entity field manager service.
* @param \Drupal\blacksmith\Blacksmith\EntityImporter\EntityImporterFactory $entityImporterFactory
* Entity importer factory service.
*/
public function __construct(EntityStorageInterface $entityStorage, EntityTypeManagerInterface $entityTypeManager, EntityFieldManagerInterface $entityFieldManager, EntityImporterFactory $entityImporterFactory) {
$this->entityStorage = $entityStorage;
$this->entityTypeManager = $entityTypeManager;
$this->entityFieldManager = $entityFieldManager;
$this->entityImporterFactory = $entityImporterFactory;
$this->fieldFormatterFactory = new FieldFormatterFactory($entityTypeManager);
}
/**
* {@inheritdoc}
*/
public function import(BlacksmithItem $item) : EntityInterface {
$values = $this->presetValues($item);
foreach ($this->getFieldDefinitions($item) as $fieldDefinition) {
$fieldName = $fieldDefinition->getName();
// Properly format the value from the Yaml file.
if ($item->hadField($fieldName)) {
$fieldFormatter = $this->fieldFormatterFactory->create($fieldDefinition);
$values[$fieldName] = $fieldFormatter->format($item->get($fieldName));
}
// Get the default value.
elseif ($defaults = $this->getDefaultValues($fieldDefinition)) {
$values[$fieldName] = $defaults;
}
// Throw an error if the field is supposed to have a value.
elseif (!array_key_exists($fieldName, $values) && $fieldDefinition->isRequired()) {
$entityTypeName = $this->entityStorage->getEntityType()->getLabel();
throw new BlacksmithImportSkip("The entity type '$entityTypeName' needs the field : '$fieldName'.", $item);
}
}
// Create the entity and save it to the database.
$entity = $this->createEntity($values);
$entity = $this->addEntityTranslations($entity, $item);
return $entity;
}
/**
* Generates an new entity and saves it in the database.
*
* @param array $values
* Keyed array of values used to generated an entity.
*
* @return \Drupal\Core\Entity\EntityInterface
* The created entity.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function createEntity(array $values) : EntityInterface {
$entity = $this->entityStorage->create($values);
$entity->save();
return $entity;
}
/**
* Generates all translations of the entity based on the Blacksmith item.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to which we add translations.
* @param \Drupal\blacksmith\BlacksmithItem $item
* The Blacksmith item used to generate the translations.
*
* @return \Drupal\Core\Entity\EntityInterface
* The entity with additional translations.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function addEntityTranslations(EntityInterface $entity, BlacksmithItem $item) : EntityInterface {
if ($entity instanceof ContentEntityBase && $entity->isTranslatable()) {
foreach ($item->getTranslations() as $langcode => $translation) {
$translationValues = $this->presentTranslationValues($translation);
foreach ($this->getFieldDefinitions($item) as $fieldDefinition) {
$fieldName = $fieldDefinition->getName();
if (!$fieldDefinition->isTranslatable()) {
if ($translation->get($fieldName)) {
$this->messenger()->addWarning("$fieldName is not translatable");
}
continue;
}
// Properly format the value from the Yaml file.
if ($fieldValue = $translation->get($fieldName)) {
$fieldFormatter = $this->fieldFormatterFactory->create($fieldDefinition);
$translationValues[$fieldName] = $fieldFormatter->format($translation->get($fieldName));
}
}
// Create the translation if any.
$entity->addTranslation($langcode, $translationValues);
$entity->save();
}
}
return $entity;
}
/**
* Prepares some values that have a specific key and/or value.
*
* @param \Drupal\blacksmith\BlacksmithItem $item
* Blacksmith item used to build the entity.
*
* @return array
* The keyed values that will be set in the entity.
*/
protected function presetValues(BlacksmithItem $item) : array {
return [
$this->entityStorage->getEntityType()->getKey('label') => $item->label(),
$this->entityStorage->getEntityType()->getKey('bundle') => $item->bundle(),
];
}
/**
* Prepares some values that have a specific key and/or value.
*
* @param \Drupal\blacksmith\BlacksmithTranslation $translations
* Blacksmith translation used to build the entity translation.
*
* @return array
* The keyed values that will be set in the entity.
*/
protected function presentTranslationValues(BlacksmithTranslation $translations) : array {
return [
$this->entityStorage->getEntityType()->getKey('label') => $translations->label(),
];
}
/**
* Get all fields of a specific entity type with bundle.
*
* @param \Drupal\blacksmith\BlacksmithItem $item
* The Blacksmith item about to be imported.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* The list of fields.
*/
protected function getFieldDefinitions(BlacksmithItem $item) : array {
return $this->entityFieldManager->getFieldDefinitions($this->entityStorage->getEntityTypeId(), $item->bundle());
}
/**
* Finds the default values of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $fieldDefinition
* The field that is about to be saved in the entity.
*
* @return array
* Raw default values.
*/
protected function getDefaultValues(FieldDefinitionInterface $fieldDefinition) : array {
$defaultValues = [];
foreach ($fieldDefinition->getDefaultValueLiteral() as $value) {
$defaultValues[] = $value['value'];
}
return $defaultValues;
}
}
