entity_change_notifier-8.x-1.0/src/Form/PublisherForm.php
src/Form/PublisherForm.php
<?php namespace Drupal\entity_change_notifier\Form; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Url; use Drupal\entity_change_notifier\Entity\PublisherInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Class PublisherForm. */ class PublisherForm extends EntityForm { /** * The entity type bundle info. * * @var \Drupal\Core\Entity\EntityTypeManager */ protected $entityTypeManager; /** * The entity type bundle info. * * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface */ protected $entityTypeBundleInfo; /** * Constructs a PublisherForm object. * * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager * The entity type manager. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info * The entity type bundle info. */ public function __construct(EntityTypeManager $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) { $this->entityTypeManager = $entity_type_manager; $this->entityTypeBundleInfo = $entity_type_bundle_info; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), $container->get('entity_type.bundle.info') ); } /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); /** @var \Drupal\entity_change_notifier\Entity\PublisherInterface $publisher */ $publisher = $this->entity; $form['label'] = [ '#type' => 'textfield', '#title' => $this->t('Label'), '#maxlength' => 255, '#default_value' => $publisher->label(), '#description' => $this->t("Label for the Publisher."), '#required' => TRUE, ]; $form['id'] = [ '#type' => 'machine_name', '#default_value' => $publisher->id(), '#machine_name' => [ 'exists' => '\Drupal\entity_change_notifier\Entity\Publisher::load', ], '#disabled' => !$publisher->isNew(), ]; $this->destinationElement($form, $publisher); $this->publishTypesElement($form, $publisher); return $form; } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { $form_state->cleanValues(); /** @var \Drupal\entity_change_notifier\Entity\PublisherInterface $publisher */ $publisher = $this->entity; // First, merge the values from the additional entity types with the entity // types that have bundles. This gives us entity and bundle types sorted by // their group, but additional entities still have string values while // bundles have array values. $publishTypes = []; foreach (['content', 'configuration', 'other'] as $type) { $publishTypes[$type] = []; $values = $form_state->getValue($type); foreach (['publish_types', 'no_bundles'] as $key) { if (isset($values[$key])) { $publishTypes[$type] = array_merge((array) $publishTypes[$type], $values[$key]); } } } // Now, we need to make the additional entities values be arrays too. foreach ($publishTypes as $type => &$publishType) { foreach ($publishType as &$entityType) { // The entity type contains bundles. if (is_array($entityType)) { // Filter out unselected bundles. Then, remove the keys from any // remaining bundles since we store them as a sequence. $entityType = array_values(array_filter($entityType)); } else { // Only convert it if $entityType is 1 (meaning checked). if ($entityType) { // If it's selected, we need to make it an array for it's internal // bundle type (which will equal the entity type. $publishType[$entityType] = [$entityType]; } } } // Remove any unselected publish types. This handles both additional // entities set to 0 and empty arrays from entities with bundles. $publishTypes[$type] = array_filter($publishTypes[$type]); } // Finally, remove our grouping and implode them into one configuration // array, since the grouping is only for the UI and not functionality. $publishTypes = array_merge([], (array) $publishTypes['content'], (array) $publishTypes['configuration'], (array) $publishTypes['other']); $publisher->setPublishTypes($publishTypes); $status = $publisher->save(); switch ($status) { case SAVED_NEW: drupal_set_message($this->t('Created the %label Publisher.', [ '%label' => $publisher->label(), ])); break; default: drupal_set_message($this->t('Saved the %label Publisher.', [ '%label' => $publisher->label(), ])); } $form_state->setRedirectUrl($publisher->toUrl('collection')); } /** * Builds a list containing all bundles for a given entity type. */ private function buildEntityList($entity_type_id) { $entityList = []; $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); foreach ($bundleInfo as $bundle_name => $bundle) { $entityList[$bundle_name] = $bundle['label']; } return $entityList; } /** * Create the "destination" dropdown. * * @param array $form * The form being built. * @param \Drupal\entity_change_notifier\Entity\PublisherInterface $publisher * The publisher being added or edited. */ private function destinationElement(array &$form, PublisherInterface $publisher) { $destinationEntities = $this->entityTypeManager->getStorage('ecn_destination') ->loadMultiple(); if (empty($destinationEntities)) { \drupal_set_message($this->t('No destinations exist to publish entities to. <a href="@add-destination">Add a destination</a> before adding a publisher.', [ '@add-destination' => Url::fromRoute('entity.ecn_destination.add_form')->toString(), ]), 'warning'); } $options = []; foreach ($destinationEntities as $destination) { $options[$destination->id()] = $destination->label(); } // Use the set destination or the first if we are adding a new Publisher. if (!empty($publisher->getDestination())) { $destination_id = $publisher->getDestination(); } else { $keys = array_keys($options); $destination_id = reset($keys); } $form['destination'] = [ '#type' => 'select', '#title' => $this->t('Destination'), '#description' => $this->t('The destination to send notifications to.'), '#default_value' => $destination_id, '#options' => $options, '#required' => TRUE, ]; } /** * Add the publish types fields to the form. * * Entities typically are classified as either Content Entities or * Configuration Entities. However, nothing prevents an entity from being it's * own type. To make the form easier to use, we split the list of all entities * and bundles into three sections: * * - Content entities * - Configuration entities * - Other * * Other will usually be empty and not shown at all. * * Within each group, we have to handle two cases: * * - Entities with bundles (nodes, taxonomies, etc). * - Entities with no bundles (though they have a bundle ID equalling their * entity ID). * * Since showing bundles for bundle-less entities would be very noisy on the * form, we group them together. With each section, we end up with: * * - A list of checkboxes of entities with bundles, sorted alphabetically * by the entity label. * - A single checkboxes element for all bundle-less entities. * * @param array $form * The form being built. * @param \Drupal\entity_change_notifier\Entity\PublisherInterface $publisher * The publisher being edited. */ private function publishTypesElement(array &$form, PublisherInterface $publisher) { $definitions = $this->getSortedEntityDefinitions(); // Initialize each details element so that form values are put into arrays. $form['content'] = [ '#type' => 'details', '#title' => $this->t('Content entity types'), '#open' => TRUE, '#tree' => TRUE, ]; $form['configuration'] = [ '#type' => 'details', '#title' => $this->t('Configuration entity types'), '#tree' => TRUE, ]; $form['other'] = [ '#type' => 'details', '#title' => $this->t('Other entity types'), '#tree' => TRUE, ]; // Loop through the three groups of entity types. foreach ($definitions as $type => &$group) { // Sort each entity type by label. uasort($group, function (EntityTypeInterface $a, EntityTypeInterface $b) { return (string) $a->getLabel() > (string) $b->getLabel(); }); // Store all entity types that don't have bundles. $additionalEntityTypes = []; /** @var \Drupal\Core\Entity\EntityTypeInterface $definition */ foreach ($group as $definition) { // If the entity definition has bundles, then we put all of it's bundles // into one checkbox list. if ($definition->getBundleEntityType()) { // Fetch the array of publish types that may already be set for this // entity type when editing a publisher. $default_value = []; if (isset($publisher->getPublishTypes()[$definition->id()])) { $default_value = $publisher->getPublishTypes()[$definition->id()]; } $form[$type]['publish_types'][$definition->id()] = [ '#type' => 'checkboxes', '#title' => $this->t('@entity types', ['@entity' => $definition->getLabel()]), '#default_value' => $default_value, '#options' => $this->buildEntityList($definition->id()), ]; } else { $additionalEntityTypes[$definition->id()] = $definition->getLabel(); } } // If we have any bundle-less entities, put them at the end. if (!empty($additionalEntityTypes)) { $default_value = array_keys(array_intersect_key($additionalEntityTypes, $publisher->getPublishTypes())); $form[$type]['no_bundles'] = [ '#type' => 'checkboxes', '#title' => $this->t('Additional entity types'), '#options' => $additionalEntityTypes, '#default_value' => $default_value, ]; } } // Sites will always have content and config entities, but "other" is just // our fallback. Most sites won't have entities that are outside of those // two groups. if (empty(Element::children($form['other']))) { unset($form['other']); } } /** * Return all entity definitions sorted by their group. * * First, sort all entity definitions into 'content', 'configuration', and * 'other'. The group is a free-form string and can be overridden in an * entity annotation, so we don't know all of the options. For example, * \Drupal\Core\Entity\Annotation\ConfigEntityType::$group doesn't use a * a class constant. * * @return array * An array of entity definitions sorted into: * - 'content' * - 'configuration' * - 'other' */ private function getSortedEntityDefinitions() { $definitions = []; $definitions['content'] = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $v) { return $v->getGroup() == 'content'; }); $definitions['configuration'] = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $v) { return $v->getGroup() == 'configuration'; }); $definitions['other'] = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $v) { return $v->getGroup() != 'content' && $v->getGroup() != 'configuration'; }); return $definitions; } }