entity_references_map-1.0.0-alpha2/src/Form/MapForm.php
src/Form/MapForm.php
<?php
namespace Drupal\entity_references_map\Form;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
use Drupal\node\NodeTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Entity Reference map configuration form.
*/
class MapForm extends ConfigFormBase {
public const CONFIG_NAME = 'entity_references_map.config';
/**
* Drupal entity fields manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected EntityFieldManagerInterface $entityFieldManager;
/**
* Drupal entity type bundle info manager.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected EntityTypeBundleInfoInterface $entityTypeBundleInfoManager;
/**
* The EntityTypeManager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* Constructs MapForm instance.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
EntityFieldManagerInterface $entityFieldManager,
EntityTypeBundleInfoInterface $entityTypeBundleInfoManager,
EntityTypeManagerInterface $entityTypeManager
) {
parent::__construct($config_factory);
$this->entityFieldManager = $entityFieldManager;
$this->entityTypeBundleInfoManager = $entityTypeBundleInfoManager;
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('entity_field.manager'),
$container->get('entity_type.bundle.info'),
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [self::CONFIG_NAME];
}
/**
* {@inheritdoc}
*/
public function getFormId(): string {
return 'entity_references_map.map_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, NodeTypeInterface $node_type = NULL): array {
if (is_null($node_type)) {
$form['#markup'] = $this->t('Cannot find this node type.');
return $form;
}
$form_state->set('node_type', $node_type);
$config_data = $this->config(self::CONFIG_NAME)->get();
$form['#tree'] = TRUE;
$form['#attributes'] = [
'id' => 'map_form',
];
$form['enable_checkbox'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable Entity References Map for this node type'),
'#default_value' => array_key_exists($node_type->id(), $config_data),
'#attributes' => [
'id' => 'map_enable_checkbox',
],
];
$form['relationships'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'map_relationships_container',
],
'#states' => [
'visible' => [
':input[id="map_enable_checkbox"]' => ['checked' => TRUE],
],
],
];
$entity_bundle = $node_type->id();
$forward_reference_fields = $this->getForwardReferenceFieldsData($entity_bundle);
$backward_reference_fields = $this->getBackwardReferenceFields($entity_bundle);
$form_state->set('forward_reference_fields', $forward_reference_fields);
$form_state->set('backward_reference_fields', $backward_reference_fields);
$forward_references = $this->buildForwardReferenceCheckboxes($forward_reference_fields, $entity_bundle);
$backward_references = $this->buildBackWardReferenceCheckboxes($backward_reference_fields, $entity_bundle);
$description = $this->t('There are no reference fields in this node type.');
if (!empty($forward_references) || !empty($backward_references)) {
$description = $this->t('Please configure entity type relationships.');
}
$form['relationships']['description'] = [
'#markup' => $description,
];
$form['relationships']['forward_reference']['fields'] = $forward_references;
$form['relationships']['backward_reference']['fields'] = $backward_references;
return parent::buildForm($form, $form_state);
}
/**
* Builds render array of form checkboxes for forward_reference fields.
*/
protected function buildForwardReferenceCheckboxes(array $forward_reference_fields, string $entity_bundle): array {
$build = [];
$config_data = $this->config(self::CONFIG_NAME)->get();
$entity_types = $this->entityTypeManager->getDefinitions();
// Allow to configure entity types available in the mapping.
$content_entity_types = array_filter($entity_types, static function ($entity_type) {
return $entity_type instanceof ContentEntityTypeInterface;
});
foreach ($forward_reference_fields as $field_id => $field_info) {
[
'label' => $field_label,
'multiple' => $is_multiple,
'machine_name' => $machine_name,
'referenced_entity_type' => $referenced_entity_type,
'referenced_entity_bundle' => $referenced_entity_bundle,
'referenced_entity_label' => $referenced_entity_label,
] = $field_info;
$build[$field_id] = [
'#type' => 'fieldset',
'#title' => "{$referenced_entity_label}: {$field_label} ({$machine_name})" . ($is_multiple ? ' [multiple]' : ''),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
];
$build[$field_id]['enabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable mapping'),
'#default_value' => $this->getCheckboxDefaultValue($referenced_entity_bundle, 'forward_reference', $field_id),
];
$input_id = sprintf(":input[id=\"edit-relationships-forward-reference-fields-%s-enabled\"]", str_replace([
'.',
'_',
], ['', '-'], $field_id));
$build[$field_id]['header_color'] = [
'#type' => 'color',
'#title' => $this->t('Header background color'),
'#states' => [
'visible' => [
$input_id => ['checked' => TRUE],
],
],
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'header_color',
]) ?: '#cccccc',
];
$build[$field_id]['content_color'] = [
'#type' => 'color',
'#title' => $this->t('Content background color'),
'#states' => [
'visible' => [
$input_id => ['checked' => TRUE],
],
],
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'content_color',
]) ?: '#ffffff',
];
$build[$field_id]['nested_levels'] = [
'#type' => 'select',
'#title' => $this->t('Build nested levels'),
'#states' => [
'visible' => [
$input_id => ['checked' => TRUE],
],
],
'#options' => [
'none' => $this->t('- None -'),
'all' => $this->t('Build all nested levels'),
// @todo implement manual settings for the referenced fields settings.
],
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'nested_levels',
]) ?: 'none',
];
$select_id = sprintf(":input[id=\"edit-relationships-forward-reference-fields-%s-nested-levels\"]", str_replace([
'.',
'_',
], ['', '-'], $field_id));
// Form elements for all nested elements settings.
$build[$field_id]['container_all'] = [
'#type' => 'fieldset',
'#title' => $this->t('All nested levels'),
'#states' => [
'visible' => [
$select_id => ['value' => 'all'],
],
],
'#collapsible' => TRUE,
'#collapsed' => TRUE,
];
$build[$field_id]['container_all']['entity_types'] = [
'#type' => 'fieldset',
'#title' => $this->t('Choose entity types that will be excluded from mapping'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
];
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface $content_entity_type */
foreach ($content_entity_types as $content_entity_type) {
$entity_type_id = $content_entity_type->id();
$excluded_entity_types = array_values(NestedArray::getValue($config_data, ['excluded_entity_types']) ?? []);
if (!in_array($entity_type_id, $excluded_entity_types, TRUE)) {
$build[$field_id]['container_all']['entity_types'][$entity_type_id] = [
'#type' => 'checkbox',
'#title' => $content_entity_type->getLabel(),
'#default_value' => (bool) NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'all',
'entity_types',
$entity_type_id,
]),
];
}
}
$build[$field_id]['container_all']['levels_depth'] = [
'#type' => 'select',
'#title' => $this->t('Number of nested levels shown'),
'#options' => [
'all' => $this->t('All'),
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
],
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'all',
'levels_depth',
]) ?: 'all',
];
$build[$field_id]['container_all']['header_color'] = [
'#type' => 'color',
'#title' => $this->t('Headers background color'),
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'all',
'header_color',
]) ?: '#cccccc',
];
$build[$field_id]['container_all']['content_color'] = [
'#type' => 'color',
'#title' => $this->t('Contents background color'),
'#default_value' => NestedArray::getValue($config_data, [
'node',
$entity_bundle,
$machine_name,
'all',
'content_color',
]) ?: '#ffffff',
];
}
return $build;
}
/**
* Helper function to return array of backward-reference checkboxes.
*/
protected function buildBackWardReferenceCheckboxes(array $backward_reference_fields, string $node_type): array {
$build = [];
foreach ($backward_reference_fields as $entity_type => $entity_bundle) {
foreach ($entity_bundle as $field_item) {
foreach ($field_item as $field_key => $field_info) {
[
'label' => $field_label,
'multiple' => $is_multiple,
'machine_name' => $machine_name,
'referenced_entity_type' => $referenced_entity_type,
'referenced_entity_bundle' => $referenced_entity_bundle,
'referenced_entity_label' => $referenced_entity_label,
] = $field_info;
$field_id = $referenced_entity_type . '.' . $referenced_entity_bundle . '.' . $machine_name;
$build[$field_id] = [
'#type' => 'checkbox',
'#title' => "{$referenced_entity_label} : {$field_label} ({$machine_name})" . ($is_multiple ? ' [multiple]' : ''),
'#default_value' => $this->getCheckboxDefaultValue($node_type, 'backward_reference', $field_id),
];
}
}
}
return $build;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getUserInput();
$config = $this->config(self::CONFIG_NAME);
$node_type = $form_state->get('node_type');
$bundle_machine_name = $node_type->id();
if (empty($values['enable_checkbox'])) {
$config->clear($bundle_machine_name);
$config->save();
return;
}
foreach ($values['relationships'] as $relationship_type => $fields) {
$entity_reference_fields = array_keys(array_filter($fields['fields']));
$allowed_fields = [];
$config->clear("$bundle_machine_name.$relationship_type");
foreach ($entity_reference_fields as $allowed_field) {
$allowed_field_data = $fields['fields'][$allowed_field];
if ($allowed_field_data['enabled']) {
$allowed_fields[] = $allowed_field;
}
$config->set("$allowed_field.header_color", $allowed_field_data['header_color']);
$config->set("$allowed_field.content_color", $allowed_field_data['content_color']);
$config->set("$allowed_field.nested_levels", $allowed_field_data['nested_levels']);
if ($fields['fields'][$allowed_field]['nested_levels'] === 'all') {
$nested_levels_all_data = $allowed_field_data['container_all'];
$config->set("$allowed_field.all.header_color", $nested_levels_all_data['header_color']);
$config->set("$allowed_field.all.content_color", $nested_levels_all_data['content_color']);
if (is_numeric($nested_levels_all_data['levels_depth'])) {
$config->set("$allowed_field.all.levels_depth", $nested_levels_all_data['levels_depth']);
}
else {
$config->set("$allowed_field.all.levels_depth", NULL);
}
foreach ($nested_levels_all_data['entity_types'] as $entity_type => $enabled) {
if (!empty($enabled)) {
$config->set("$allowed_field.all.entity_types.$entity_type", $entity_type);
}
else {
$config->clear("$allowed_field.all.entity_types.$entity_type");
}
}
}
else {
$config->clear("$allowed_field.all");
}
}
$config->set("$bundle_machine_name.$relationship_type", $allowed_fields);
}
$config->set("$bundle_machine_name.type", $node_type->getEntityType()
->getBundleOf());
$config->save();
parent::submitForm($form, $form_state);
}
/**
* Get "Entity reference" FieldConfigInterface items list of given node type.
*
* @param string $bundle
* Node bundle name.
* @param string $entity_type
* Entity type name.
*
* @return array
* Fields [field_config_id => FieldConfigInterface $item] associative array.
*/
public function getForwardReferenceFieldsList(string $bundle, string $entity_type = 'node'): array {
$fields = array_filter(
$this->entityFieldManager->getFieldDefinitions($entity_type, $bundle), function ($field_definition) {
return $field_definition instanceof FieldConfigInterface;
}
);
$transformedFields = [];
foreach ($fields as &$field) {
/** @var \Drupal\field\Entity\FieldConfig $field */
if ($field->getType() !== 'entity_reference') {
continue;
}
$transformedFields[$field->id()] = $field;
}
return $transformedFields;
}
/**
* Get given entity bundle type "entity_reference" fields array data.
*
* @param string $bundle
* Entity bundle name.
* @param string $entity_type
* Entity type name.
*
* @return array
* [
* field_config_id => [
* 'label' => $item->label(),
* 'multiple' => $isMultiple,
* 'machine_name' => $item->getName(),
* 'referenced_entity_type' => $item->get('entity_type'),
* 'referenced_entity_bundle' => $item->get('bundle'),
* 'referenced_entity_label' => $label,
* ],
* ];
*/
public function getForwardReferenceFieldsData(string $bundle, string $entity_type = 'node'): array {
$entity_type_bundle_info_manager = $this->entityTypeBundleInfoManager;
return array_map(static function ($item) use ($entity_type_bundle_info_manager) {
/** @var \Drupal\field\Entity\FieldConfig $item */
return [
'label' => $item->label(),
'machine_name' => $item->getName(),
'multiple' => $item->getFieldStorageDefinition()->get('cardinality') !== 1,
'referenced_entity_type' => $item->get('entity_type'),
'referenced_entity_bundle' => $item->get('bundle'),
'referenced_entity_label' => $entity_type_bundle_info_manager->getBundleInfo($item->get('entity_type'))[$item->get('bundle')]['label'] ?? t('Undefined'),
];
}, $this->getForwardReferenceFieldsList($bundle, $entity_type));
}
/**
* Get other nodes "entity_reference" fields that reference this node.
*
* @param string $bundle
* Entity bundle name.
* @param string $entity_type
* Entity type name.
*
* @return array
* $childEntitiesFields = [
* entity_type_id => [
* entity_bundle => [
* 'machine_name' => $field_machine_name,
* 'label' => $field_label,
* 'multiple' => $is_field_multiple,
* ]
* ]
* ]
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getBackwardReferenceFields(string $bundle, string $entity_type = 'node'): array {
$map = $this->entityFieldManager->getFieldMapByFieldType(
'entity_reference'
);
$ids = [];
foreach ($map as $type => $info) {
foreach ($info as $name => $data) {
foreach ($data['bundles'] as $bundle_name) {
$ids[] = "$type.$bundle_name.$name";
}
}
}
$filtered_map = [];
foreach (FieldConfig::loadMultiple($ids) as $field_config) {
$field_name = $field_config->getName();
$target_type = $field_config->getSetting('target_type');
if (!empty($target_type) && $target_type === $entity_type) {
$handler_settings = $field_config->getSetting('handler_settings');
if (isset($handler_settings['target_bundles'][$bundle])) {
$node_type = $this->entityTypeManager->getStorage('node_type')
->load($field_config->get('bundle'));
$filtered_map[$entity_type][$field_config->get('bundle')][] = [
'machine_name' => $field_name,
'label' => $field_config->getLabel(),
'multiple' => $field_config->getFieldStorageDefinition()->get('cardinality') !== 1,
'referenced_entity_type' => $field_config->get('entity_type'),
'referenced_entity_bundle' => $field_config->get('bundle'),
'referenced_entity_label' => $node_type ? $node_type->label() : $this->t('Undefined node type label.'),
];
}
}
}
return $filtered_map;
}
/**
* Helper method that returns true if ER field is already enabled in config.
*/
public function getCheckboxDefaultValue(string $entity_bundle, string $reference_type, string $field_id): bool {
$config_data = $this->config(self::CONFIG_NAME)->get();
if (!empty($config_data[$entity_bundle][$reference_type])) {
return in_array($field_id, $config_data[$entity_bundle][$reference_type], TRUE);
}
return FALSE;
}
}
