eav_field-2.x-dev/src/Plugin/Field/FieldWidget/EavWidget.php
src/Plugin/Field/FieldWidget/EavWidget.php
<?php
namespace Drupal\eav_field\Plugin\Field\FieldWidget;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\Attribute\FieldWidget;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Field\WidgetPluginManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\eav_field\EavAttributeStorage;
use Drupal\eav_field\Entity\EavAttribute;
use Drupal\eav_field\Entity\EavAttributeInterface;
use Drupal\eav_field\Entity\EavValue;
use Drupal\eav_field\Plugin\Field\FieldType\EavItemInterface;
use Drupal\eav_field\Plugin\Field\FieldType\EavItemListInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
#[FieldWidget(
id: 'eav_widget',
label: new TranslatableMarkup('EAV widget'),
field_types: ['eav'],
multiple_values: TRUE,
)]
class EavWidget extends WidgetBase {
protected EntityTypeManagerInterface $entityTypeManager;
protected WidgetPluginManager $fieldWidgetManager;
protected RouteMatchInterface $routeMatch;
/**
* {@inheritdoc}
*/
public function __construct(
$plugin_id,
$plugin_definition,
FieldDefinitionInterface $field_definition,
array $settings,
array $third_party_settings,
EntityTypeManagerInterface $entity_type_manager,
WidgetPluginManager $field_widget_manager,
RouteMatchInterface $route_match,
) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->entityTypeManager = $entity_type_manager;
$this->fieldWidgetManager = $field_widget_manager;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['third_party_settings'],
$container->get('entity_type.manager'),
$container->get('plugin.manager.field.widget'),
$container->get('current_route_match'),
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings(): array {
return [
'show_in_local_task' => TRUE,
'load_global_attributes' => TRUE,
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state): array {
$element['show_in_local_task'] = [
'#type' => 'checkbox',
'#title' => t('Show widget in local task'),
'#default_value' => $this->getSetting('show_in_local_task'),
];
$element['load_global_attributes'] = [
'#type' => 'checkbox',
'#title' => t('Show attributes without category'),
'#default_value' => $this->getSetting('load_global_attributes'),
];
return $element;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
/** @var EavItemListInterface $items */
$host_entity = $items->getEntity();
$field_name = $this->fieldDefinition->getName();
$attribute_storage = $this->entityTypeManager->getStorage('eav_attribute'); /** @var EavAttributeStorage $attribute_storage */
if ($this->getSetting('show_in_local_task') && $this->routeMatch->getRouteName() != 'entity.' . $host_entity->getEntityTypeId() . '.edit_eav') {
$element['#access'] = FALSE;
return $element;
}
$element['table'] = [
'#type' => 'table',
'#header' => [
$this->t('Attribute'),
$this->t('Value'),
'', // For "value_id" field
],
'#parents' => array_merge($element['#field_parents'], [$items->getName()]),
'#empty' => $this->t('No available attributes.'),
'#title' => $this->fieldDefinition->getLabel(),
'#theme_wrappers' => !$this->getSetting('show_in_local_task') ? ['form_element'] : [],
];
// Load category attributes
$attributes = $attribute_storage->loadByHostEntityCategory($host_entity, $this->getSetting('load_global_attributes'));
$eav_values = [];
/** @var EavItemInterface $item */
foreach ($items as $item) {
if (($eav_value = $item->getValueEntity()) && ($attribute = $eav_value->getAttributeEntity())) {
$eav_values[$attribute->getMachineName()] = $item->getValueEntity();
// Load items attributes
$attributes[$attribute->id()] = $attribute;
}
}
// Load user attributes
$widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
if (!empty($widget_state['custom_attributes'])) {
$attributes += EavAttribute::loadMultiple($widget_state['custom_attributes']);
}
if ($attributes) {
foreach ($attributes as $attribute) {
// Value field need configure before EavValue::create() and before $eav_value->getValueFieldItems()
$attribute->configureValueFieldDefinition();
$attribute_machine_name = $attribute->getMachineName();
$eav_value = $eav_values[$attribute_machine_name] ?? EavValue::create(['aid' => $attribute->id()]);
$value_field_name = $attribute->getValueFieldName();
$value_field_items = $eav_value->getValueFieldItems();
$element['table'][$attribute_machine_name]['#eav_attribute'] = $attribute;
$element['table'][$attribute_machine_name]['attribute'] = [
'#markup' => $attribute->label(),
];
$element['table'][$attribute_machine_name][$value_field_name] = $this->valueWidget($attribute, $value_field_items, $element, $form, $form_state);
// @TODO Find way of pass value_id in ::massageFormValues without create table cell
$element['table'][$attribute_machine_name]['value_id'] = [
'#type' => 'value',
'#value' => $eav_value->id(),
];
}
}
else {
$element['table']['#value'] = [];
}
// "Add more"
$element['add_more'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['container-inline'],
],
];
$element['add_more']['attribute'] = [
'#type' => 'entity_autocomplete',
'#target_type' => 'eav_attribute',
];
// @TODO Add #ajax
// @TODO Add #limit_validation_errors
$element['add_more']['add_more_button'] = [
'#type' => 'submit',
'#value' => $this->t('Add attribute'),
'#submit' => [[static::class, 'addMoreSubmit']],
];
return $element;
}
/**
* "Add more" button submit callback.
*/
public static function addMoreSubmit(array $form, FormStateInterface $form_state): void {
/** @see \Drupal\Core\Field\WidgetBase::addMoreSubmit() */
$triggering_button = $form_state->getTriggeringElement();
$widget_element_parents = array_slice($triggering_button['#array_parents'], 0, -2);
$widget_element = NestedArray::getValue($form, $widget_element_parents);
$field_name = $widget_element['#field_name'];
$field_parents = $widget_element['#field_parents'];
$widget_form_values = $form_state->getValue($widget_element['#parents']);
$widget_state = static::getWidgetState($field_parents, $field_name, $form_state);
if ($attribute_id = $widget_form_values['add_more']['attribute']) {
$widget_state['custom_attributes'][] = $attribute_id;
$widget_state['custom_attributes'] = array_unique($widget_state['custom_attributes']);
static::setWidgetState($field_parents, $field_name, $form_state, $widget_state);
}
$form_state->setRebuild();
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state): array {
$new_values = [];
unset($values['add_more']);
foreach ($values as $attribute_name => $item_values) {
$attribute = EavAttribute::loadByMachineName($attribute_name);
$eav_value = $item_values['value_id']
? EavValue::load($item_values['value_id'])
: EavValue::create(['aid' => $attribute->id()]);
$value_field_widget = $this->fieldWidgetManager->getInstance([
'field_definition' => $attribute->configureValueFieldDefinition(),
'configuration' => [
'type' => $attribute->getValueWidgetType(),
'settings' => $attribute->getValueWidgetSettings(),
],
]);
$value_field_items = $eav_value->getValueFieldItems();
$value_field_form = $form[$this->fieldDefinition->getName()]['widget']['table'][$attribute_name];
$value_field_widget->extractFormValues($value_field_items, $value_field_form, $form_state);
if (!$value_field_items->isEmpty() || $attribute->getValueFieldDefaultValues()) {
$eav_value->save();
$new_values[]['entity'] = $eav_value;
}
}
return $new_values;
}
/**
* Return value field widget.
*/
protected function valueWidget(EavAttributeInterface $attribute, FieldItemListInterface $value_field_items, array $element, array $form, FormStateInterface $form_state): array {
$widget = $this->fieldWidgetManager->getInstance([
'field_definition' => $attribute->getValueFieldDefinition(),
'configuration' => [
'type' => $attribute->getValueWidgetType(),
'settings' => $attribute->getValueWidgetSettings(),
],
]);
$form['#parents'] = array_merge($element['table']['#parents'], [$attribute->getMachineName()]);
// @TODO Use $element without $form
$widget_form = $widget->form($value_field_items, $form, $form_state);
// Hide widget title
$this->hideElementTitle($widget_form['widget']);
foreach (Element::children($widget_form['widget']) as $key) {
$this->hideElementTitle($widget_form['widget'][$key]);
if (isset($widget_form['widget'][$key]['value'])) {
$this->hideElementTitle($widget_form['widget'][$key]['value']);
}
}
return $widget_form;
}
/**
* Hide element title.
*/
protected function hideElementTitle(&$element): void {
if (isset($element['#title'])) {
$element['#title_display'] = 'invisible';
}
}
}
