View source
<?php
namespace Drupal\entity_extra_field\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\entity_extra_field\EntityExtraFieldContextTrait;
use Drupal\entity_extra_field\ExtraFieldTypePluginInterface;
class EntityExtraField extends ConfigEntityBase implements EntityExtraFieldInterface {
use StringTranslationTrait;
use EntityExtraFieldContextTrait;
public $id;
public $name;
public $label;
public $description;
public $display = [];
public $field_type_id;
public $display_label = FALSE;
public $field_type_config = [];
public $field_type_condition = [];
public $field_conditions_all_pass = FALSE;
public $base_entity_type_id;
public $base_bundle_type_id;
protected $build_attachments = [];
public function id() : ?string {
if (empty($this->name) || empty($this->base_entity_type_id) || empty($this->base_bundle_type_id)) {
return NULL;
}
return "{$this->base_entity_type_id}.{$this->base_bundle_type_id}.{$this->name}";
}
public function name() : ?string {
return $this->name;
}
public function description() : ?string {
return $this->description;
}
public function displayLabel() : bool {
return $this->display_label;
}
public function getDisplay() : array {
return $this->display;
}
public function getDisplayType() : ?string {
return $this
->getDisplay()['type'] ?? NULL;
}
public function getFieldTypeLabel() : string {
return $this
->getFieldTypePlugin()
->label();
}
public function getFieldTypePluginId() : string {
return $this->field_type_id;
}
public function getFieldTypePluginConfig() : array {
return $this->field_type_config;
}
public function getFieldTypeCondition() : array {
return $this->field_type_condition;
}
public function getFieldTypeConditionsAllPass() : bool {
return $this->field_conditions_all_pass;
}
public function getBaseEntityTypeId() : string {
return $this->base_entity_type_id;
}
public function getBaseBundleTypeId() : ?string {
return $this->base_bundle_type_id;
}
public function getBaseEntityType() : EntityTypeInterface {
return $this
->entityTypeManager()
->getDefinition($this
->getBaseEntityTypeId());
}
public function getBaseEntityTypeBundle() : EntityTypeInterface {
$entity_type = $this
->getBaseEntityType();
return $this
->entityTypeManager()
->getDefinition($entity_type
->getBundleEntityType());
}
public function getBaseEntityContext() : EntityContext {
$definition = $this
->getBaseEntityType();
$label = $this
->t('@entity being viewed', [
'@entity' => $definition
->getLabel(),
]);
$entity_context = EntityContext::fromEntityType($definition, $label);
$context_definition = $entity_context
->getContextDefinition();
$context_definition
->addConstraint('Bundle', [
$this
->getBaseBundleTypeId(),
]);
return $entity_context;
}
public function getCacheDiscoveryId() : string {
$langcode = $this
->languageManager()
->getCurrentLanguage()
->getId();
return "entity_bundle_extra_fields:{$this->getBaseEntityTypeId()}:{$this->getBaseBundleTypeId()}:{$langcode}";
}
public function getCacheRenderTag() : string {
return "entity_extra_field:{$this->getDisplayType()}.{$this->getBaseEntityTypeId()}.{$this->getBaseBundleTypeId()}";
}
public function getBuildAttachments() : array {
return $this->build_attachments;
}
public function getActiveFieldTypeConditions() : array {
return array_filter($this
->getFieldTypeCondition(), function ($value) {
unset($value['id'], $value['negate'], $value['context_mapping']);
return !$this
->isArrayEmpty($value);
});
}
public function setBuildAttachment($type, array $attachment) : self {
if (!isset($this->build_attachments[$type])) {
$this->build_attachments[$type] = [];
}
$this->build_attachments[$type] = array_replace_recursive($this->build_attachments[$type], $attachment);
return $this;
}
public function build(EntityInterface $entity, EntityDisplayInterface $display) : array {
$field_type_plugin = $this
->getFieldTypePlugin();
if (!$field_type_plugin instanceof ExtraFieldTypePluginInterface) {
return [];
}
return [
'#field' => $this,
'#view_mode' => $display
->getMode(),
'#theme' => 'entity_extra_field',
'label' => [
'#plain_text' => $this
->displayLabel() ? $this
->label() : NULL,
],
'content' => $field_type_plugin
->build($entity, $display),
];
}
public function hasDisplayComponent(EntityDisplayInterface $display) : bool {
return $display
->getComponent($this
->name()) !== NULL;
}
public function getCacheContexts() : array {
$contexts = parent::getCacheContexts();
foreach ($this
->getActiveFieldTypeConditions() as $plugin_id => $configuration) {
$condition = $this
->conditionPluginManager()
->createInstance($plugin_id, $configuration);
$contexts = Cache::mergeContexts($contexts, $condition
->getCacheContexts());
}
return $contexts;
}
public function getCacheTagsToInvalidate() : array {
$tags = parent::getCacheTagsToInvalidate();
foreach ($this
->getActiveFieldTypeConditions() as $plugin_id => $configuration) {
$condition = $this
->conditionPluginManager()
->createInstance($plugin_id, $configuration);
$tags = Cache::mergeTags($tags, $condition
->getCacheTags());
}
return $tags;
}
public function hasConditionsBeenMet(array $contexts, bool $all_must_pass = FALSE) : bool {
$conditions = $this
->getActiveFieldTypeConditions();
if (empty($conditions)) {
return TRUE;
}
$verdicts = [];
foreach ($conditions as $plugin_id => $configuration) {
$condition = $this
->conditionPluginManager()
->createInstance($plugin_id, $configuration);
if ($condition instanceof ContextAwarePluginInterface) {
try {
$this
->applyPluginRuntimeContexts($condition, [
'entity_extra_field' => EntityContext::fromEntity($this),
] + $contexts);
} catch (\Exception $exception) {
return FALSE;
}
}
$verdict = $condition
->evaluate();
if ($verdict && !$all_must_pass) {
return TRUE;
}
$verdicts[] = $verdict;
}
$verdicts = array_unique($verdicts);
return count($verdicts) === 1 && current($verdicts) === TRUE;
}
public function exists($name) : bool {
return (bool) $this
->getQuery()
->condition('id', "{$this->getBaseEntityTypeId()}.{$this->getBaseBundleTypeId()}.{$name}")
->execute();
}
public function toUrl($rel = 'edit-form', array $options = []) : Url {
$base_route_name = $this
->getBaseRouteName();
$route_parameters = $this
->urlRouteParameters($rel);
switch ($rel) {
case 'collection':
return URL::fromRoute($base_route_name, $route_parameters, $options);
case 'add-form':
return Url::fromRoute("{$base_route_name}.add", $route_parameters, $options);
case 'edit-form':
return Url::fromRoute("{$base_route_name}.edit", $route_parameters, $options);
case 'delete-form':
return Url::fromRoute("{$base_route_name}.delete", $route_parameters, $options);
}
throw new \RuntimeException(sprintf('Unable to find %s to built a URL.', $rel));
}
public function calculateDependencies() : self {
parent::calculateDependencies();
if ($field_type_plugin = $this
->getFieldTypePlugin()) {
$this
->calculatePluginDependencies($field_type_plugin);
}
return $this;
}
protected function isArrayEmpty(array $array) : bool {
foreach (NestedArray::filter($array) as $value) {
if (!empty($value)) {
return FALSE;
}
if (is_array($value)) {
$this
->isArrayEmpty($value);
}
}
return TRUE;
}
protected function getFieldTypePlugin() : ExtraFieldTypePluginInterface {
return \Drupal::service('plugin.manager.extra_field_type')
->createInstance($this
->getFieldTypePluginId(), $this
->getFieldTypePluginConfig());
}
protected function urlRouteParameters($rel) : array {
$base_bundle_type_id = $this
->getBaseEntityTypeBundle()
->id();
$uri_route_parameters = [];
$uri_route_parameters[$base_bundle_type_id] = $this
->getBaseBundleTypeId();
switch ($rel) {
case 'edit-form':
case 'delete-form':
$uri_route_parameters[$this
->getEntityTypeId()] = $this
->id();
break;
}
return $uri_route_parameters;
}
protected function linkTemplates() : array {
$templates = [];
$ui_base_path = $this
->getBaseEntityBundleUiPath();
$entity_type = $this
->getEntityType();
$entity_handlers = $entity_type
->getHandlerClasses();
if (isset($entity_handlers['form'])) {
foreach (array_keys($entity_handlers['form']) as $rel) {
$template_path = "{$ui_base_path}/extra-fields";
switch ($rel) {
case 'add':
$template_path = "{$template_path}/{$rel}";
break;
case 'edit':
case 'delete':
$template_path = "{$template_path}/{" . $entity_type
->id() . "}/{$rel}";
break;
}
$templates[$rel . '-form'] = $template_path;
}
}
$templates['collection'] = "{$ui_base_path}/extra-fields";
return $templates;
}
protected function getBaseEntityBundleUiPath() : ?string {
$base_route = $this
->getBaseEntityType()
->get('field_ui_base_route');
if (!isset($base_route)) {
return NULL;
}
$base_route_rel = strtr(substr($base_route, strrpos($base_route, '.') + 1), [
'_' => '-',
]);
$base_entity_bundle = $this
->getBaseEntityTypeBundle();
if (!$base_entity_bundle
->hasLinkTemplate($base_route_rel)) {
return NULL;
}
return $base_entity_bundle
->getLinkTemplate($base_route_rel);
}
protected function getBaseRouteName() : string {
return "entity.{$this->getBaseEntityTypeId()}.extra_fields";
}
protected function getQuery() : QueryInterface {
return $this
->getStorage()
->getQuery();
}
protected function getStorage() : EntityStorageInterface {
return $this
->entityTypeManager()
->getStorage($this
->getEntityTypeId());
}
protected function conditionPluginManager() : PluginManagerInterface {
return \Drupal::service('plugin.manager.condition');
}
}