blacksmith-8.x-1.x-dev/src/Blacksmith/EntityImporter/FieldFormatter/EntityReferenceFieldFormatter.php

src/Blacksmith/EntityImporter/FieldFormatter/EntityReferenceFieldFormatter.php
<?php

namespace Drupal\blacksmith\Blacksmith\EntityImporter\FieldFormatter;

use Drupal;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Random;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\blacksmith\BlacksmithItem;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\blacksmith\Blacksmith\EntityImporter\EntityImporterFactory;
use Drupal\blacksmith\Exception\BlacksmithImportSkip;

/**
 * Class EntityReferenceFieldFormatter.
 *
 * @package Drupal\blacksmith\Blacksmith\EntityImporter\FieldFormatter
 */
class EntityReferenceFieldFormatter extends FieldFormatterBase implements ContainerInjectionInterface {

  /**
   * Entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Blacksmith entity importer.
   *
   * @var \Drupal\blacksmith\Blacksmith\EntityImporter\EntityImporter
   */
  protected $entityImporter;

  /**
   * The type of entity the field refers to.
   *
   * @var string
   */
  protected $targetEntityType;

  /**
   * The storage of the entity type referred by the field.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $targetEntityTypeStorage;

  /**
   * The bundles that the field can create.
   *
   * @var array
   */
  protected $targetBundles;

  /**
   * Random string generator service.
   *
   * @var \Drupal\Component\Utility\Random
   */
  protected $randomService;

  /**
   * {@inheritdoc}
   */
  public function __construct(FieldDefinitionInterface $fieldDefinition, EntityTypeManagerInterface $entityTypeManager, EntityImporterFactory $entityImporter) {
    parent::__construct($fieldDefinition);
    $this->entityTypeManager = $entityTypeManager;
    $this->randomService = new Random();

    // @todo Find out why instance of ConfigEntityBase doesn't work.
    if (method_exists($fieldDefinition, 'getSetting')) {
      $this->targetEntityType = $fieldDefinition->getSetting('target_type');
      $this->targetBundles = $fieldDefinition->getSetting('handler_settings')['target_bundles'] ?? [];

      if ($this->targetBundles) {
        $this->targetBundles = array_keys($this->targetBundles);
      }

      try {
        $this->targetEntityTypeStorage = $entityTypeManager->getStorage($this->targetEntityType);
      }
      catch (InvalidPluginDefinitionException | PluginNotFoundException $exception) {
        $this->logger->error($exception->getMessage());
      }
    }

    // Set the entity importer.
    try {
      if ($this->targetEntityType) {
        // @todo Find a way to do Dependency Injection without a recursive service issue.
        $this->entityImporter = $entityImporter->create($this->targetEntityType);
      }
    }
    catch (BlacksmithImportSkip $exception) {
      $this->logger->error(($exception->getMessage()));
    }
  }

  /**
   * {@inheritDoc}
   */
  public static function create(ContainerInterface $container, FieldDefinitionInterface $fieldDefinition = NULL) {
    return new static(
      $fieldDefinition,
      $container->get('entity_type.manager'),
      $container->get('blacksmith.entity_importer.factory')
    );
  }

  /**
   * {@inheritdoc}
   *
   * @throws \Drupal\blacksmith\Exception\BlacksmithInvalidItemConfiguration
   */
  public function formatUniqueValue($value) {
    if (is_int($value)) {
      return $value;
    }

    // @todo Find a more suitable way to check if the value is a sub-entity.
    //   Maybe get the ID?
    if (is_array($value)) {
      $value['entity_type'] = $value['entity_type'] ?? $this->targetEntityType;

      // If the bundle isn't specified and the field only allows one bundle
      // consider the bundle as already set.
      if (!isset($value['bundle']) && count($this->targetBundles) === 1) {
        $value['bundle'] = $value['bundle'] ?? reset($this->targetBundles);
      }

      // @todo Find out a way to get the parent's langcode, status and author
      // Check if the sub-entity's bundle is set and allowed by the field.
      if (!in_array($value['bundle'], $this->targetBundles, FALSE) && !empty($this->targetEntityTypeStorage->getEntityType()->getKey('bundle'))) {
        $this->messenger()->addWarning("Missing 'bundle' key in sub entity.");
        return NULL;
      }

      // @todo Yeah... That's no good. The sub entities should not require a
      //   specific Blacksmith ID. Only the top level items should have one.
      if (!isset($value['id'])) {
        $value['id'] = $this->randomService->name(32);
      }

      // Try to find existing content instead of creating it for nothing.
      if ($this->targetEntityType === 'taxonomy_term' && $term = $this->findTerm($value)) {
        return $term;
      }

      if ($this->targetEntityType === 'user' && $user = $this->findUser($value)) {
        return $user;
      }

      // @todo Find a way to get the parent's selector and add it as the group.
      $item = new BlacksmithItem($value, 'todo');

      try {
        $entity = $this->entityImporter->import($item);
        return $this->formatEntity($entity);
      }
      catch (EntityStorageException | Exception $exception) {
        Drupal::messenger()->addWarning($exception->getMessage());
        return NULL;
      }
    }
    // Make sure that the $value is a array at this point.
    elseif (is_string($value)) {
      $labelKey = $this->targetEntityTypeStorage->getEntityType()->getKey('label') ?: 'name';
      $value = [$labelKey => $value];

      if (count($this->targetBundles) === 1) {
        $value['bundle'] = reset($this->targetBundles);
      }
      elseif (!empty($this->targetEntityTypeStorage->getEntityType()->getKey('bundle'))) {
        // @todo Proper error and warning handling.
        $this->messenger()->addWarning('You need to specify the bundle of the ' . $this->targetEntityType);
      }

      return self::formatUniqueValue($value);
    }

    return parent::formatUniqueValue($value);
  }

  /**
   * The way the entity is referenced in the database.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The sub entity that was just created.
   *
   * @return mixed
   *   The way the entity is saved the database.
   */
  protected function formatEntity(EntityInterface $entity) {
    return $entity->id();
  }

  /**
   * Finds a user based on it's username.
   *
   * @param mixed $value
   *   A unique value from the Blacksmith file.
   *
   * @return int|null
   *   A user's unique identifier or NULL if it doesn't exist.
   */
  protected function findUser($value) : ?int {
    $uid = array_keys($this->targetEntityTypeStorage->loadByProperties(['name' => $value['name']]));

    if (empty($uid)) {
      return NULL;
    }

    if (is_array($uid)) {
      $uid = reset($uid);
    }

    return $uid;
  }

  /**
   * Looks for an existing taxonomy term with specific values.
   *
   * @param array $value
   *   Taxonomy values used to find an existing its counterpart.
   *
   * @return int|null
   *   The found taxonomy term if one is found.
   */
  protected function findTerm(array $value) : ?int {
    $tid = array_keys($this->targetEntityTypeStorage->loadByProperties([
      'name' => $value['name'],
      'vid' => $value['bundle'],
    ]));

    if (empty($tid)) {
      return NULL;
    }

    if (is_array($tid)) {
      $tid = reset($tid);
    }

    return $tid;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc