association-1.0.0-alpha2/src/Plugin/Association/Behavior/EntityListBehavior.php
src/Plugin/Association/Behavior/EntityListBehavior.php
<?php namespace Drupal\association\Plugin\Association\Behavior; use Drupal\association\Attribute\AssociationBehavior; use Drupal\association\Entity\AssociationTypeInterface; use Drupal\association\Plugin\AssociationPluginFormInterface; use Drupal\association\Plugin\BehaviorBase; use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Render\MarkupInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\toolshed\Strategy\Exception\StrategyException; /** * Behavior for managing associations that have a entity list. * * Manages entities linked to association and dictates the controls, which * entities/bundles are allowed. The behavior also is responsible for building * the admin UI for the association management. */ #[AssociationBehavior( id: 'entity_list', label: new TranslatableMarkup('Entity list'), manager_builder: '\Drupal\association\Behavior\Manager\EntityListBuilder', )] class EntityListBehavior extends BehaviorBase implements AssociationPluginFormInterface { /** * List of labels for behavior tags, keyed by the tag identifier. * * @var \Drupal\Component\Render\MarkupInterface[]|string[] */ protected $tagLabels = []; /** * Get the key to use for an entity tag of specified entity type and bundle. * * @param string $entity_type_id * The entity type ID. * @param string $bundle_id * The entity bundle ID. * * @return string * The machine name / tag key to use for an entity type and bundle. */ protected function getTagKey(string $entity_type_id, string $bundle_id): string { return $entity_type_id . ':' . $bundle_id; } /** * Formats the association entity tag label. * * @param \Drupal\Core\StringTranslation\TranslatableMarkup|string $entity_type_label * The entity type label. * @param \Drupal\Core\StringTranslation\TranslatableMarkup|string $bundle_label * The entity bundle label. * * @return \Drupal\Component\Render\MarkupInterface * A formatted label to use for the entity tag for that entity type and * bundle. */ protected function formatTagLabel($entity_type_label, $bundle_label): MarkupInterface { return new FormattableMarkup('@entity_type_label: @bundle_label', [ '@entity_type_label' => $entity_type_label, '@bundle_label' => $bundle_label, ]); } /** * {@inheritdoc} */ public function defaultConfiguration() { return [ 'entity_types' => [], ]; } /** * {@inheritdoc} */ public function getEntityTypes(): array { $entityTypes = $this->getConfiguration()['entity_types'] ?? []; $adapterTypes = $this->adapterManager->getEntityTypes(); return array_intersect_key($adapterTypes, $entityTypes); } /** * {@inheritdoc} */ public function isValidEntity($tag, $entity_type, $bundle): bool { // Tag is no longer relevant for entity list behaviors. $allowedTypes = $this->getConfiguration()['entity_types'] ?? []; return !empty($allowedTypes[$entity_type][$bundle]); } /** * {@inheritdoc} */ public function getTags(): array { $types = $this->getConfiguration()['entity_types'] ?? []; $tags = []; foreach ($types as $entityTypeId => $bundles) { if ($adapter = $this->adapterManager->getAdapterByEntityType($entityTypeId)) { $avail = $adapter->getBundles(); foreach ($bundles as $bundle) { if (!empty($bundles[$bundle])) { $key = $this->getTagKey($entityTypeId, $bundle); $tags[$key] = $this->formatTagLabel($adapter->getLabel(), $avail[$bundle]); } } } } return $tags; } /** * {@inheritdoc} */ public function getTagLabel($tag, $entity_type_id, $bundle) { $key = $this->getTagKey($entity_type_id, $bundle); if (!isset($this->tagLabels[$key])) { $this->tagLabels[$key] = FALSE; if ($adapter = $this->adapterManager->getAdapterByEntityType($entity_type_id)) { $bundles = $adapter->getBundles(); if (!empty($bundles[$bundle])) { $this->tagLabels[$key] = $this->formatTagLabel($adapter->getLabel(), $bundles[$bundle]); } } } return $this->tagLabels[$key] ?: ''; } /** * {@inheritdoc} */ public function validateConfigUpdate(AssociationTypeInterface $assocation_type, array $changes): array { $errors = []; $current = $this->getConfiguration()['entity_types'] ?? []; $entityTypes = $changes['entity_types']; if ($diff = array_diff($current, $entityTypes)) { $errors[] = $this->t('Cannot remove entity types from entity list behavior when there is data: @list', [ '@list' => implode(', ', $diff), ]); } else { foreach ($current as $type => $bundles) { if ($bundleDiff = array_diff($bundles, $entityTypes[$type])) { $errors[] = $this->t('Cannot remove @type bundles (@list) when Entity Association has content.', [ '@type' => $type, '@list' => implode(', ', $bundleDiff), ]); } } } return []; } /** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state, AssociationTypeInterface $association_type = NULL): array { $entityTypes = $this->getConfiguration()['entity_types'] ?? []; $form_state->set('association_type', $association_type->id()); foreach ($this->adapterManager->getEntityTypes() as $type) { try { $adapter = $this->adapterManager->getAdapterByEntityType($type); $form['entity_types'][$type] = [ '#type' => 'checkboxes', '#title' => $this->t('Allowed @label types', [ '@label' => $adapter->getLabel(), ]), '#options' => $adapter->getBundles(), '#default_value' => $entityTypes[$type] ?? [], ]; } catch (StrategyException $e) { // Unable to create the association entity adapter for the entity type. // Skip as this probably means a providing module is missing. } } return $form; } /** * {@inheritdoc} */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { $current = $this->getConfiguration()['entity_types'] ?? []; $entityTypes = $form_state->getValue('entity_types'); if ($typeId = $form_state->get('association_type')) { /** @var \Drupal\association\Entity\AssociationTypeInterface */ $type = $this->entityTypeManager->getStorage('association_type')->load($typeId); if (!($type && $type->hasData())) { return; } } foreach ($current as $type => $bundles) { $updated = array_filter($entityTypes[$type]); // Check for missing entity bundles. if (empty($updated)) { $form_state->setError($form['entity_types'][$type], $this->t('Cannot remove @type bundles (@list) when entity association has content.', [ '@type' => $type, '@list' => implode(', ', $bundles), ])); } elseif ($bundleDiff = array_diff($bundles, $updated)) { $form_state->setError($form['entity_types'][$type], $this->t('Cannot remove @type bundles (@list) when entity association has data.', [ '@type' => $type, '@list' => implode(', ', $bundleDiff), ])); } } } /** * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { $entityTypes = $form_state->getValue('entity_types') ?? []; foreach ($entityTypes as &$bundles) { $bundles = array_filter($bundles); } $config = ['entity_types' => array_filter($entityTypes)]; $this->setConfiguration($config); } }