sites_group_overrides-1.x-dev/src/SitesGroupOverridesService.php
src/SitesGroupOverridesService.php
<?php
declare(strict_types=1);
namespace Drupal\sites_group_overrides;
use Drupal\field\FieldConfigInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\sites_group\SitesGroupServiceInterface;
use Drupal\group\Entity\GroupRelationshipInterface;
use Drupal\group\Entity\Storage\GroupRelationshipStorageInterface;
use Drupal\sites_group_overrides\Event\SyncFieldToEntity;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
use Drupal\sites\SiteInterface;
/**
* The sites_group_override service.
*/
class SitesGroupOverridesService implements SitesGroupOverridesServiceInterface {
/**
* Constructs a SitesGroupService object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\sites_group\SitesGroupServiceInterface $sitesGroupService
* The sites_group service.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
* The event dispatcher.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
* The entity field manager.
*/
public function __construct(
protected readonly EntityTypeManagerInterface $entityTypeManager,
protected readonly SitesGroupServiceInterface $sitesGroupService,
protected readonly EventDispatcherInterface $eventDispatcher,
protected readonly EntityFieldManagerInterface $entityFieldManager,
) {}
public function getBlacklist($entity) {
return [
'uid',
'created',
'updated',
"{$entity->getEntityTypeId()}__layout_builder__layout",
];
}
/**
* {@inheritdoc}
*/
public function syncFieldsToEntity(GroupRelationshipInterface $relationship, ContentEntityInterface $entity): ContentEntityInterface {
foreach ($this->getSynchronizableFields($relationship, $entity) as $field_name => $field) {
if ($entity->hasField($field_name) && !$field->isEmpty() && empty($entity->{$field_name}->overriden)) {
// Maybe one can move this to an event subscriber too.
$entity->set($field_name, $field->getValue());
$event = new SyncFieldToEntity($entity, $field_name, $field);
$this->eventDispatcher->dispatch($event, SyncFieldToEntity::EVENT_NAME);
$entity->{$field_name}->overriden = TRUE;
}
}
return $entity->addCacheableDependency($relationship);
}
/**
* {@inheritdoc}
*
* @todo use static cache
*/
public function getSynchronizableFields(GroupRelationshipInterface $relationship, ContentEntityInterface $entity, bool $reuse_source_value = TRUE): array {
$fields = [];
foreach ($entity->getFields(FALSE) as $field_name => $field) {
if (!in_array($field_name, $this->getBlacklist($relationship))) {
$field_definition = $entity->get($field_name)->getFieldDefinition();
$override_settings = $this->getSettings($field_definition, $entity, $relationship);
if (empty($override_settings['use_overrides'])) {
continue;
}
if ($relationship->hasField($override_settings['target_field_name'])) {
$fields[$field_name] = $relationship->get($override_settings['target_field_name']);
if ($reuse_source_value && $relationship->get($override_settings['target_field_name'])->isEmpty()) {
// Overridable but not overidden -> use target value.
$fields[$field_name]->setValue($entity->get($field_name)->getValue());
}
}
}
}
return $fields;
}
/**
* {@inheritdoc}
*/
public function getSourceBundleEntityTypes(): array {
$entity_types = [];
$group_types = $this->entityTypeManager->getStorage('group_type')->loadMultiple();
foreach ($group_types as $group_type) {
foreach ($group_type->getInstalledPlugins() as $plugin) {
$definition = $plugin->getPluginDefinition();
if (!$definition->get('entity_type_id')) {
continue;
}
if ($bundle_entity_type_id = $this->entityTypeManager->getDefinition($definition->get('entity_type_id'))->get('bundle_entity_type')) {
$bundle_entity_type = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
$entity_types[$bundle_entity_type->get('id')] = $bundle_entity_type;
}
}
}
return $entity_types;
}
/**
* Get sites_group_overrides settings for the specified field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition to get the settings for.
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The (target) entity.
* @param \Drupal\group\Entity\GroupRelationshipInterface; $relationship
* The relationship entity.
* @return array
* The sites_group_overrides settings
*/
private function getSettings(FieldDefinitionInterface $field_definition, ContentEntityInterface $entity, GroupRelationshipInterface $relationship): array {
$bundle = $relationship->getRelationshipType()->id();
if ($field_definition instanceof FieldConfigInterface) {
return (array) $field_definition->getThirdPartySetting('sites_group_overrides', $bundle);
}
if ($field_definition instanceof FieldDefinitionInterface) {
return (array) $field_definition->getConfig($entity->bundle())->getThirdPartySetting('sites_group_overrides', $bundle);
}
return [];
}
/**
* {@inheritdoc}
*/
public function getTargetFieldName(GroupRelationshipInterface $relationship, string $field_name): ?string {
// Determine field-definition w/o calling ->getEntity() because it messes up the relationship.
$definition = $relationship->getPlugin()->getPluginDefinition();
$bundle = $entity_type_id = $definition->get('entity_type_id');
if ($definition->get('entity_bundle')) {
$bundle = $definition->get('entity_bundle');
}
if ($field_definition = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle)[$field_name] ?? NULL) {
$settings = $field_definition->getThirdPartySetting('sites_group_overrides', $relationship->bundle(), []);
}
if (empty($settings['use_overrides']) || empty($settings['target_field_name'])) {
return NULL;
}
return $relationship->hasField($settings['target_field_name']) ? $settings['target_field_name'] : NULL;
}
/**
* {@inheritdoc}
*/
public function entityIsOveridable(ContentEntityInterface $entity): bool {
if ($relationship = $this->getRelationship($entity)) {
return count($this->getSynchronizableFields($relationship, $entity)) > 0;
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getRelationship(ContentEntityInterface $entity): ?GroupRelationshipInterface {
return $this->sitesGroupService->getRelationship($entity);
}
/**
* {@inheritdoc}
*/
public function entityIsOverridden(ContentEntityInterface $entity, GroupRelationshipInterface $relationship): bool {
// Reload unchanged - overrides might already be in the entity cache.
$langcode = $relationship->language()->getId();
/** @var \Drupal\group\Entity\GroupRelationshipInterface $relationship */
$relationship = $this->entityTypeManager->getStorage('group_relationship')->loadUnchanged($relationship->id());
try {
$relationship = $relationship->getTranslation($langcode);
}
catch (\InvalidArgumentException $e) {
return FALSE;
}
foreach ($this->getSynchronizableFields($relationship, $entity, FALSE) as $field_name => $field) {
if (!$relationship->get($this->getTargetFieldName($relationship, $field_name))->isEmpty()) {
return TRUE;
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getRelationshipsWithOverrides(ContentEntityInterface $entity): array {
$relationships = [];
/** @var GroupRelationshipStorageInterface $relationship_storage */
$relationship_storage = $this->entityTypeManager->getStorage('group_relationship');
foreach ($this->sitesGroupService->getGroups($entity) as $group) {
$relationship_candidates = $relationship_storage->loadByEntityAndGroup($entity, $group);
foreach ($relationship_candidates as $relationship_candidate) {
try {
$relationship_candidate = $relationship_candidate->getTranslation($entity->language()->getId());
}
catch (\InvalidArgumentException $e) {
continue;
}
if ($this->entityIsOverridden($entity, $relationship_candidate)) {
$relationships[] = $relationship_candidate;
}
}
}
return $relationships;
}
/**
* {@inheritdoc}
*/
public function revertOverride(GroupRelationshipInterface $relationship) {
$group = $relationship->getGroup();
$plugin_id = $relationship->getPluginId();
$entity = $relationship->getEntity();
// Delete the relationship.
$relationship->delete();
// Create a new relationship.
$group->addRelationship($entity, $plugin_id);
}
}
