external_entity-1.0.x-dev/src/Plugin/ExternalEntity/RenderType/InternalEntityRenderType.php
src/Plugin/ExternalEntity/RenderType/InternalEntityRenderType.php
<?php
declare(strict_types=1);
namespace Drupal\external_entity\Plugin\ExternalEntity\RenderType;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\external_entity\AjaxFormTrait;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\external_entity\Service\EntityTypeInfo;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\external_entity\Contracts\ExternalEntityInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\external_entity\Contracts\ExternalEntityTypeInterface;
use Drupal\external_entity\Plugin\ExternalEntity\ExternalEntityRenderTypeBase;
/**
* Define the internal entity render type.
*
* @ExternalEntityRenderType(
* id = "internal_entity",
* label = @Translation("Internal Entity")
* )
*/
class InternalEntityRenderType extends ExternalEntityRenderTypeBase {
use AjaxFormTrait;
/**
* @var \Drupal\external_entity\Service\EntityTypeInfo
*/
protected $entityTypeInfo;
/**
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $externalEntityCache;
/**
* @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
*/
protected $entityDisplayRepository;
/**
* Cached internal entities array.
*
* @var array
*/
protected static $entities = [];
/**
* Internal entity render type constructor.
*
* @param array $configuration
* An array of plugin configurations.
* @param string $plugin_id
* The plugin identifier.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\external_entity\Service\EntityTypeInfo $entity_type_info
* The entity type manager service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityTypeInfo $entity_type_info,
CacheBackendInterface $external_entity_cache,
EntityTypeManagerInterface $entity_type_manager,
EntityDisplayRepositoryInterface $entity_display_repository,
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition
);
$this->entityTypeInfo = $entity_type_info;
$this->entityTypeManager = $entity_type_manager;
$this->externalEntityCache = $external_entity_cache;
$this->entityDisplayRepository = $entity_display_repository;
}
/**
* {@inheritDoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('external_entity.entity_type.info'),
$container->get('cache.external_entity'),
$container->get('entity_type.manager'),
$container->get('entity_display.repository')
);
}
/**
* {@inheritDoc}
*/
public function defaultConfiguration(): array {
return [
'bundle' => NULL,
'display' => NULL,
'entity_type' => NULL,
'property_mapping' => [],
];
}
/**
* {@inheritDoc}
*/
public function buildConfigurationForm(
array $form,
FormStateInterface $form_state,
): array {
$form['#prefix'] = '<div id="internal-entity-render-type">';
$form['#suffix'] = '<div/>';
$entity_info = $this->entityTypeInfo;
$entity_type_id = $this->getFormStateValue(
'entity_type',
$form_state,
$this->getRenderEntityType()
);
$entity_type_option = $entity_info->getEntityTypeOptions();
$form['entity_type'] = [
'#type' => 'select',
'#title' => $this->t('Entity Type'),
'#required' => TRUE,
'#empty_option' => $this->t('- Select -'),
'#options' => $entity_type_option,
'#default_value' => $entity_type_id,
'#depth' => 1,
] + $this->baseAjaxFormElement();
if (isset($entity_type_id, $entity_type_option[$entity_type_id])) {
$bundle = $this->getFormStateValue(
['bundle'],
$form_state,
$this->getRenderEntityBundle()
);
$bundle_options = $entity_info->getEntityBundleOptions($entity_type_id);
$form['bundle'] = [
'#type' => 'select',
'#title' => $this->t('Entity Bundle'),
'#required' => TRUE,
'#description' => $this->t('Select the entity bundle.'),
'#empty_option' => $this->t('- Select -'),
'#options' => $bundle_options,
'#default_value' => $bundle,
] + $this->baseAjaxFormElement();
if (isset($bundle, $bundle_options[$bundle]) && !empty($bundle)) {
$form['property_mapping'] = [
'#type' => 'table',
'#title' => $this->t('Property Mapping'),
'#header' => [
$this->t('Resource Property'),
$this->t('Entity Field'),
],
'#tree' => TRUE,
'#empty' => $this->t(
'There are no external entity properties defined.'
),
];
$field_options = $entity_info->getEntityFieldOptions(
$entity_type_id,
$bundle
);
$property_mapping = $this->getConfigurationValue('property_mapping', []);
foreach ($this->getDisplayPropertyDefinitions() as $name => $definition) {
$property_field = $property_mapping[$name]['field'] ?? NULL;
$form['property_mapping'][$name]['name']['#markup'] = $name;
$form['property_mapping'][$name]['field'] = [
'#type' => 'select',
'#options' => $field_options,
'#empty_option' => $this->t('- None -'),
'#default_value' => array_key_exists($property_field, $field_options)
? $property_field
: NULL,
];
}
}
}
return $form;
}
/**
* {@inheritDoc}
*/
public function render(
ExternalEntityInterface $external_entity,
string $mode = 'default',
): array {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->createEntity($external_entity);
return $this->renderEntityView($entity, $mode);
}
/**
* {@inheritDoc}
*/
public function createEntity(
ExternalEntityInterface $external_entity,
): ?EntityInterface {
$cid = "external_entity:render_entity:{$external_entity->id()}";
$cache = $this->externalEntityCache->get($cid);
if (
is_object($cache)
&& isset($cache->data)
&& $cache->data instanceof EntityInterface
) {
$entity = $cache->data;
}
else {
$entity = $this->instantiateEntity();
$entity->external_entity = $external_entity;
$properties = $external_entity->getProperties();
$this->attachDefaultEntityProperties($entity, $properties);
$this->attachMappingEntityProperties($entity, $properties);
$this->externalEntityCache->set(
$cid,
$entity,
CacheBackendInterface::CACHE_PERMANENT,
Cache::mergeTags($external_entity->getCacheTags(), $entity->getCacheTags())
);
}
return $entity;
}
/**
* {@inheritDoc}
*/
public function getRenderModes(): array {
return $this->entityDisplayRepository->getViewModeOptionsByBundle(
$this->getRenderEntityType(),
$this->getRenderEntityBundle()
);
}
/**
* {@inheritDoc}
*/
public function getCacheTagsToInvalidate(): array {
$tags = parent::getCacheTagsToInvalidate();
$config = $this->getConfiguration();
if (isset($config['entity_type'], $config['bundle'])) {
$tags[] = "external_entity:{$this->pluginId}:{$config['entity_type']}:{$config['bundle']}";
}
return $tags;
}
/**
* Define the base AJAX form element.
*
* @return array[]
* An array of default ajax elements.
*/
protected function baseAjaxFormElement(): array {
return [
'#ajax' => [
'event' => 'change',
'method' => 'replaceWith',
'wrapper' => 'internal-entity-render-type',
'callback' => [$this, 'ajaxFormDepthCallback'],
],
];
}
/**
* Instantiate the base internal base entity.
*
* @return \Drupal\Core\Entity\ContentEntityInterface|null
* The internal entity object.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function instantiateEntity(): ?ContentEntityInterface {
if ($entity_type = $this->getRenderEntityType()) {
$values = [];
$definition = $this->entityTypeManager->getDefinition($entity_type);
if ($bundle = $definition->getKey('bundle')) {
$values += [
$bundle => $this->getRenderEntityBundle(),
];
}
return $this->entityTypeManager->getStorage($entity_type)->create($values);
}
return NULL;
}
/**
* Attach default entity properties.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The internal entity object.
* @param array $properties
* An array of property definitions.
*/
protected function attachDefaultEntityProperties(
EntityInterface $entity,
array $properties,
): void {
$entity_type = $entity->getEntityType();
foreach (['id', 'uuid'] as $key) {
if (
!isset($properties[$key])
|| !$entity_type->hasKey($key)
) {
continue;
}
$name = $entity_type->getKey($key);
$definition = $properties[$key];
$entity->set($name, $definition['value'] ?? []);
}
}
/**
* Attach mapping entity properties.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The internal entity object.
* @param array $properties
* An array of property definitions.
*/
protected function attachMappingEntityProperties(
EntityInterface $entity,
array $properties,
): void {
foreach ($this->getConfigurationValue('property_mapping') as $name => $info) {
if (!isset($info['field'], $properties[$name]) || !$entity->hasField($info['field'])) {
continue;
}
$definition = $properties[$name];
if (!isset($definition['value'])) {
continue;
}
$items = $entity->get($info['field']);
$items->setValue($definition['value']);
}
}
/**
* Render an entity view.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* An entity instance to render.
* @param string $mode
* The entity display mode.
*
* @return array
* A renderable array that's returned from the view builder.
*/
protected function renderEntityView(
EntityInterface $entity,
string $mode = 'default',
): array {
$builder = $this->entityTypeManager->getViewBuilder(
$entity->getEntityTypeId()
);
return $builder->view($entity, $mode);
}
/**
* Get the external entity display resource.
*
* @return string|null
*/
protected function getResource(): ?string {
return $this->externalEntityResourceDisplay->getResource();
}
/**
* Get the external entity display variation.
*
* @return string|null
*/
protected function getVariation(): ?string {
return $this->externalEntityResourceDisplay->getVariation();
}
/**
* Get the render plugin entity type ID.
*
* @return string
* The entity type ID.
*/
protected function getRenderEntityType(): ?string {
return $this->getConfigurationValue('entity_type');
}
/**
* Get the render plugin entity bundle.
*
* @return string
* The entity bundle name.
*/
protected function getRenderEntityBundle(): ?string {
return $this->getConfigurationValue('bundle');
}
/**
* Get a plugin configuration value.
*
* @param string $name
* The keyed name.
*
* @return mixed
* The plugin configuration value.
*/
protected function getConfigurationValue(string $name) {
return $this->getConfiguration()[$name];
}
/**
* Get the display external entity type.
*
* @return \Drupal\external_entity\Contracts\ExternalEntityTypeInterface
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function getDisplayExternalEntityType(): ExternalEntityTypeInterface {
return $this->externalEntityResourceDisplay->externalEntityType();
}
/**
* Get display external entity type property definitions.
*
* @return array
* An array of property definitions.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\external_entity\Exception\InvalidResourceException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function getDisplayPropertyDefinitions(): array {
$external_entity_type = $this->getDisplayExternalEntityType();
return $external_entity_type->getResourceProperties(
$this->getResource(),
$this->getVariation()
);
}
}
