entity_value_inheritance-1.3.0/src/Plugin/EntityValueInheritanceUpdater/InheritanceOverridePlugin.php
src/Plugin/EntityValueInheritanceUpdater/InheritanceOverridePlugin.php
<?php
namespace Drupal\entity_value_inheritance\Plugin\EntityValueInheritanceUpdater;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\entity_value_inheritance\EntityValueInheritanceUpdaterPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Allow content editor the ability to override form values.
*
* @EntityValueInheritanceUpdater(
* id = "override",
* title = @Translation("Override"),
* description = @Translation("Allow the end user to override the field value."),
* )
*/
class InheritanceOverridePlugin extends EntityValueInheritanceUpdaterPluginBase {
/**
* State Service.
*/
protected StateInterface $state;
/**
* Database Connection.
*/
protected Connection $connection;
/**
* Module Handler Service.
*/
protected ModuleHandler $moduleHandler;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->state = $container->get('state');
$instance->connection = $container->get('database');
$instance->moduleHandler = $container->get('module_handler');
return $instance;
}
/**
* {@inheritdoc}
*/
public function alterForm(array $form, FormStateInterface $formState, EntityInterface $entity, FieldDefinitionInterface $fieldDefinition): array {
$form = parent::alterForm($form, $formState, $entity, $fieldDefinition);
array_unshift($form['#after_build'], [$this, 'afterBuildForm']);
if (stripos($form['#prefix'] ?? '', 'evi-override') === FALSE) {
$form['#prefix'] = ($form['#prefix'] ?? '') . '<div id="evi-override">';
$form['#suffix'] = '</div>' . ($form['#suffix'] ?? '');
$form[$this->inheritance->getDestinationReferenceField()]['widget'][0]['target_id']['#ajax'] = [
'callback' => [$this, 'updateForm'],
'wrapper' => 'evi-override',
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Updating Form...'),
],
];
$form['#validate'][] = [$this, 'validateForm'];
array_unshift($form['actions']['submit']['#submit'], [$this, 'submitForm']);
}
return $form;
}
/**
* Alter the build form.
*/
public function afterBuildForm(array $form, FormStateInterface &$form_state) {
// Remove the override for conditional fields setup if override_values is found.
if (
$this->moduleHandler->moduleExists('conditional_fields') &&
isset($form['#conditional_fields']) &&
isset($form['#conditional_fields']['override_values'])
) {
unset($form['#conditional_fields']['override_values']);
}
return $form;
}
/**
* Validation function used to remove errors for non overridden fields.
*/
public function validateForm(array $form, FormStateInterface $formState) {
// Get the override values.
if (!empty($overrideValues = $formState->getValue('override_values', []))) {
// Get all the previously defined errors.
$errors = $formState->getErrors();
// Clear the errors so that we can remove the fields that were overridden.
$formState->clearErrors();
// Get a list of all inheritances to loop through.
$inheritances = $this->helper->getInheritanceItemsBySource($formState->getBuildInfo()['callback_object']->getEntity());
// Filter out values that are not marked for being overridden.
$overrideValues = array_filter($overrideValues, function ($item) {
return $item === 0;
});
// Loop through values and remove errors from list.
foreach ($overrideValues as $overrideValue => $override) {
$inheritance = $inheritances[$overrideValue] ?? FALSE;
if ($inheritance) {
$keys = preg_grep('#^' . $inheritance->getDestinationField() . '#', array_keys($errors));
foreach ($keys as $key) {
unset($errors[$key]);
}
}
}
// Set the errors back to the form state.
foreach ($errors as $field_name => $error) {
$formState->setErrorByName($field_name, $error);
}
}
}
/**
* Submit Form Functionality.
*/
public function submitForm(array $form, FormStateInterface $formState) {
if ($formState->getValue('override_values')) {
$formState->getFormObject()->getEntity()->overrides = $formState->getValue('override_values', []);
}
}
/**
* Ajax Response.
*
* @param array $form
* Form Array.
* @param \Drupal\Core\Form\FormStateInterface $formState
* Form State.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Return Ajax Response.
*/
public function updateForm(array $form, FormStateInterface $formState): AjaxResponse {
$formState->setRebuild();
$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand('#evi-override', $form));
return $response;
}
/**
* {@inheritdoc}
*/
public function alterFormField(array $element, FormStateInterface $formState, EntityInterface $entity, FieldDefinitionInterface $fieldDefinition): array {
$id = $this->inheritance->id();
$element = parent::alterFormField($element, $formState, $entity, $fieldDefinition);
$referenceField = $this->inheritance->getDestinationReferenceField();
if (
($entity->isNew() && $formState->getValue($referenceField) !== NULL) ||
(
!$entity->isNew() &&
(
!empty($entity->get($referenceField)->getString()) ||
$formState->getValue($referenceField) !== NULL
)
)
) {
$element['#states'] = [
'visible' => [
':input[name="override_values[' . $id . ']"]' => ['checked' => TRUE],
],
];
$element = [
'override_' . $id => [
'#type' => 'checkbox',
'#title' => $this->t('Override @field', ['@field' => $fieldDefinition->getLabel()]),
'#parents' => ['override_values', $id],
'#default_value' => $entity->overrides[$id]->override ?? 0,
'#weight' => array_key_exists('#weight', $element) ? $element['#weight'] : 0,
],
$fieldDefinition->getName() => $element,
];
if ($this->moduleHandler->moduleExists('field_group') && function_exists('field_group_info_groups')) {
// If the field_group module is used add the field to the current group.
$group_fields = field_group_info_groups($entity->getEntityTypeId(), $entity->bundle(), 'form', 'default');
foreach ($group_fields as $group_field) {
if (in_array($fieldDefinition->getName(), $group_field->children)) {
$element['override_' . $id]['#group'] = $group_field->group_name;
$element[$fieldDefinition->getName()]['#group'] = $group_field->group_name;
}
}
}
}
return $element;
}
/**
* {@inheritdoc}
*/
public function fieldGroupFormProcessAlter(array &$element, &$group, &$complete_form): void {
$element['#show_empty_fields'] = TRUE;
}
/**
* Set the Overrides.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* Entity to upsert the overrides.
*/
protected function updateOverrides(EntityInterface $entity): void {
if ($entity->overrides_processed ?? FALSE) {
return;
}
$overrides = $entity->overrides ?? [];
if (count($overrides) > 0) {
$upsert = $this->connection
->upsert('evi_overrides')
->fields(['key', 'inheritance', 'entity_type', 'entity_id', 'override'])
->key('key');
foreach ($overrides as $override => $override_value) {
$override_value = (is_int($override_value) ? $override_value : (is_object($override_value) ? $override_value->override : 0));
$upsert->values([
'key' => sprintf('%s:%s:%s', $entity->getEntityTypeId(), $override, $entity->id()),
'inheritance' => $override,
'entity_type' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
'override' => $override_value,
]);
if ($override_value === 0) {
// Set the field value to the parent.
if ($entity->{$this->inheritance->getDestinationReferenceField()} !== NULL) {
$referenceEntities = $entity->{$this->inheritance->getDestinationReferenceField()}->referencedEntities();
if (count($referenceEntities) > 0) {
$entity->{$this->inheritance->getDestinationField()} = $referenceEntities[0]->get($this->inheritance->getSourceField());
}
}
}
}
if ($upsert->count() > 0) {
$upsert->execute();
}
}
$entity->overrides_processed = TRUE;
}
/**
* {@inheritdoc}
*/
public function entityLoadProperties(string $entity_type_id, array $entities = []): void {
$ids = array_map(function ($entity) {
return $entity->id();
}, $entities);
// Check static cache first
$cache = &drupal_static(__METHOD__, []);
$cache_key = $entity_type_id . ':' . implode('|', $ids);
if (!isset($cache[$cache_key])) {
$results = $this->connection
->select('evi_overrides', 'overrides')
->fields('overrides')
->condition('overrides.entity_type', $entity_type_id)
->condition('overrides.entity_id', $ids, 'IN')
->execute()
->fetchAll();
$group = [];
foreach ($results as $result) {
$group[$result->entity_id][$result->inheritance] = $result;
}
$cache[$cache_key] = $group;
}
foreach ($entities as $entity) {
if (isset($cache[$cache_key][$entity->id()])) {
$entity->overrides = $cache[$cache_key][$entity->id()];
}
}
}
/**
* {@inheritdoc}
*/
public function updateDestination(EntityInterface $sourceEntity, EntityInterface $destinationEntity): bool {
$this->updateOverrides($sourceEntity);
$this->updateOverrides($destinationEntity);
$overrides = isset($destinationEntity->overrides) && array_key_exists($this->inheritance->id(), $destinationEntity->overrides) && $destinationEntity->overrides[$this->inheritance->id()]?->override === '1';
if (!$overrides) {
return parent::updateDestination($sourceEntity, $destinationEntity);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function deleteEntity(EntityInterface $entity): void {
$this->connection
->delete('evi_overrides')
->condition('entity_type', $entity->getEntityTypeId())
->condition('entity_id', $entity->id())
->execute();
}
/**
* {@inheritdoc}
*/
public function preSaveEntity(EntityInterface $entity): void {
$this->updateOverrides($entity);
parent::preSaveEntity($entity);
}
}
