View source
<?php
namespace Drupal\acquia_contenthub\EventSubscriber\ImportFailure;
use Acquia\ContentHubClient\CDF\CDFObject;
use Acquia\ContentHubClient\CDFDocument;
use Drupal\acquia_contenthub\AcquiaContentHubEvents;
use Drupal\acquia_contenthub\Event\FailedImportEvent;
use Drupal\acquia_contenthub\Event\LoadLocalEntityEvent;
use Drupal\acquia_contenthub\Event\PreEntitySaveEvent;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\SynchronizableInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\depcalc\DependencyStack;
use Drupal\depcalc\DependentEntityWrapper;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class CreateStubs implements EventSubscriberInterface {
protected static $count = 0;
public static function getSubscribedEvents() {
$events[AcquiaContentHubEvents::IMPORT_FAILURE][] = [
'onImportFailure',
100,
];
return $events;
}
public function onImportFailure(FailedImportEvent $event, string $event_name, EventDispatcherInterface $dispatcher) {
if (static::$count === $event
->getCount()) {
$exception = new \Exception("Potential infinite recursion call interrupted in CreateStubs event subscriber.");
$event
->setException($exception);
return;
}
static::$count = $event
->getCount();
$unprocessed = array_diff(array_keys($event
->getCdf()
->getEntities()), array_keys($event
->getStack()
->getDependencies()));
if (!$unprocessed) {
$event
->stopPropagation();
return;
}
if (!$event
->getSerializer()
->getTracker()
->isTracking()) {
$event
->getSerializer()
->getTracker()
->setStack($event
->getStack());
}
$this
->handleEntityProcessing($unprocessed, $event, $dispatcher);
static::$count = 0;
}
protected function handleEntityProcessing(array $unprocessed, FailedImportEvent $event, EventDispatcherInterface $dispatcher) {
try {
$cdfs = $this
->processConfigEntities($unprocessed, $event) + $this
->processContentEntities($unprocessed, $event, $dispatcher);
$document = new CDFDocument(...$cdfs);
$event
->getSerializer()
->unserializeEntities($document, $event
->getStack());
$event
->stopPropagation();
} catch (\Exception $e) {
$event
->setException($e);
}
}
protected function processConfigEntities(array &$unprocessed, FailedImportEvent $event) : array {
$cdfs = [];
foreach ($unprocessed as $key => $uuid) {
$cdf = $event
->getCdf()
->getCdfEntity($uuid);
if ($cdf
->getType() !== 'drupal8_config_entity') {
continue;
}
unset($unprocessed[$key]);
$cdfs[] = $cdf;
}
return $cdfs;
}
protected function processContentEntities(array $unprocessed, FailedImportEvent $event, EventDispatcherInterface $dispatcher) : array {
$cdfs = [];
foreach ($unprocessed as $key => $uuid) {
$cdf = $event
->getCdf()
->getCdfEntity($uuid);
$stack = $event
->getStack();
$load_event = new LoadLocalEntityEvent($cdf, $stack);
$dispatcher
->dispatch(AcquiaContentHubEvents::LOAD_LOCAL_ENTITY, $load_event);
$entity = $load_event
->getEntity() ?? $this
->createStub($cdf, $uuid, $stack, $dispatcher);
$wrapper = new DependentEntityWrapper($entity, TRUE);
$wrapper
->setRemoteUuid($uuid);
$stack
->addDependency($wrapper);
$cdfs[] = $cdf;
}
return $cdfs;
}
protected function createStub(CDFObject $cdf, string $uuid, DependencyStack $stack, EventDispatcherInterface $dispatcher) : EntityInterface {
$entity_type = $cdf
->getAttribute('entity_type')
->getValue()[CDFObject::LANGUAGE_UNDETERMINED];
$manager = $this
->getEntityTypeManager();
$definition = $manager
->getDefinition($entity_type);
$storage = $manager
->getStorage($entity_type);
$keys = $definition
->getKeys();
$values = $this
->getEntityValues($keys, $uuid, $entity_type, $cdf);
$entity = $storage
->create($values);
$this
->generateRequiredSampleItems($entity);
$pre_entity_save_event = new PreEntitySaveEvent($entity, $stack, $cdf);
$dispatcher
->dispatch(AcquiaContentHubEvents::PRE_ENTITY_SAVE, $pre_entity_save_event);
$entity = $pre_entity_save_event
->getEntity();
if ($entity instanceof SynchronizableInterface) {
$entity
->setSyncing(TRUE);
}
$entity
->save();
return $entity;
}
protected function getEntityValues(array $keys, string $uuid, string $entity_type, CDFObject $cdf) : array {
$values = [
$keys['uuid'] => $uuid,
$keys['langcode'] => $cdf
->getMetadata()['default_language'],
];
if (!empty($keys['bundle'])) {
$values[$keys['bundle']] = $cdf
->getAttribute('bundle')
->getValue()[CDFObject::LANGUAGE_UNDETERMINED];
}
if (!empty($keys['label'])) {
$field_definitions = !empty($keys['bundle']) ? $this
->getEntityFieldManager()
->getFieldDefinitions($entity_type, $keys['bundle']) : NULL;
$field_settings = isset($field_definitions) ? $field_definitions[$keys['label']]
->getItemDefinition()
->getSettings() : [];
$size = $field_settings['max_length'] ?? 255;
$values[$keys['label']] = mb_substr($cdf
->getAttribute('label')
->getValue()[CDFObject::LANGUAGE_UNDETERMINED], 0, $size);
}
return $values;
}
protected function generateRequiredSampleItems(ContentEntityInterface $entity) {
$skip_fields = [
$entity
->getEntityType()
->getKey('id'),
$entity
->getEntityType()
->getKey('revision'),
'revision_log',
];
foreach ($entity as $field_name => $field) {
if (in_array($field_name, $skip_fields)) {
continue;
}
if ($field
->isEmpty() && $this
->fieldIsRequired($field)) {
$field
->generateSampleItems();
}
}
}
protected function fieldIsRequired(FieldItemListInterface $field) : bool {
if (!$field
->getFieldDefinition() instanceof BaseFieldDefinition) {
return FALSE;
}
if ($field
->getFieldDefinition()
->isComputed()) {
return FALSE;
}
if ($field
->getFieldDefinition()
->isRequired()) {
return TRUE;
}
foreach ($field
->getFieldDefinition()
->getFieldStorageDefinition()
->getPropertyDefinitions() as $propertyDefinition) {
if ($propertyDefinition
->isRequired()) {
return TRUE;
}
}
return FALSE;
}
protected function getEntityTypeManager() : EntityTypeManagerInterface {
return \Drupal::entityTypeManager();
}
protected function getEntityFieldManager() : EntityFieldManagerInterface {
return \Drupal::service('entity_field.manager');
}
}