From 9ea8029a3f25b027b696bc7d6714d29d9d8116c9 Mon Sep 17 00:00:00 2001
From: Bernd Oliver Suenderhauf <bos@suenderhauf.de>
Date: Mon, 17 Jun 2019 17:50:54 +0200
Subject: [PATCH] Issue #3047921 by Pancho: Turn default entity ID field into a
 select if no more than 25 entries

---
 src/Form/FillPdfFormForm.php                 | 106 ++++++++++++-------
 tests/src/Functional/FillPdfFormFormTest.php |  74 ++++++++-----
 2 files changed, 111 insertions(+), 69 deletions(-)

diff --git a/src/Form/FillPdfFormForm.php b/src/Form/FillPdfFormForm.php
index 691261d..400be71 100644
--- a/src/Form/FillPdfFormForm.php
+++ b/src/Form/FillPdfFormForm.php
@@ -16,13 +16,17 @@ use Drupal\fillpdf\InputHelperInterface;
 use Drupal\fillpdf\SerializerInterface;
 use Drupal\fillpdf\TokenResolverInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\EntityInterface;
 
 /**
  * Form controller for the FillPDFForm edit form.
  */
 class FillPdfFormForm extends ContentEntityForm {
 
+  /**
+   * Maximum number of entities to be listed in a select.
+   */
+  const SELECT_MAX = 25;
+
   /**
    * The FillPdf admin form helper.
    *
@@ -129,13 +133,13 @@ class FillPdfFormForm extends ContentEntityForm {
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
 
-    /** @var \Drupal\fillpdf\FillPdfFormInterface $entity */
-    $entity = $this->entity;
+    /** @var \Drupal\fillpdf\FillPdfFormInterface $fillpdf_form */
+    $fillpdf_form = $this->entity;
 
     $form['title']['token_tree'] = $this->adminFormHelper->getAdminTokenForm();
 
     // @todo: Encapsulate this logic into a ::getDefaultEntityType() method on FillPdfForm
-    $stored_default_entity_type = $entity->get('default_entity_type');
+    $stored_default_entity_type = $fillpdf_form->get('default_entity_type');
     $default_entity_type = count($stored_default_entity_type) ? $stored_default_entity_type->first()->value : NULL;
 
     $form['default_entity_type'] = [
@@ -146,7 +150,7 @@ class FillPdfFormForm extends ContentEntityForm {
       '#weight' => 12.5,
       '#default_value' => $default_entity_type,
       '#ajax' => [
-        'callback' => '::ajaxUpdateIdAutocomplete',
+        'callback' => '::ajaxUpdateEntityId',
         'event' => 'change',
         'wrapper' => 'test-entity-wrapper',
         'progress' => ['type' => 'none'],
@@ -160,15 +164,30 @@ class FillPdfFormForm extends ContentEntityForm {
       $default_entity_id = $form_state->getValue('default_entity_id');
     }
     else {
-      $stored_default_entity_id = $entity->get('default_entity_id');
+      $stored_default_entity_id = $fillpdf_form->get('default_entity_id');
       $default_entity_id = count($stored_default_entity_id) ? $stored_default_entity_id->first()->value : NULL;
     }
 
+    $form['default_entity_id'] = [
+      '#title' => $this->t('Default entity'),
+      '#target_type' => $default_entity_type,
+      '#weight' => 13,
+      '#prefix' => '<div id="test-entity-wrapper">',
+      '#suffix' => '</div>',
+      '#ajax' => [
+        'callback' => '::ajaxUpdateEntityId',
+        'event' => 'autocompleteclose autocompletechange',
+        'wrapper' => 'test-entity-wrapper',
+        'progress' => ['type' => 'none'],
+      ],
+    ];
+
     // If a default entity type is set, allow selecting a default entity, too.
     if ($default_entity_type) {
-      $default_entity = $default_entity_id ? $this->entityTypeManager->getStorage($default_entity_type)->load($default_entity_id) : NULL;
+      $storage = $this->entityTypeManager->getStorage($default_entity_type);
 
-      if (!empty($default_entity) && $default_entity instanceof EntityInterface) {
+      $default_entity = $default_entity_id ? $storage->load($default_entity_id) : NULL;
+      if (!empty($default_entity)) {
         $description = $this->l(
           $this->t('Download this PDF template populated with data from the @type %label (@id).', [
             '@type' => $default_entity_type,
@@ -181,43 +200,50 @@ class FillPdfFormForm extends ContentEntityForm {
           ])
         );
       }
+
+      $entity_ids = $storage->getQuery()->range(0, self::SELECT_MAX + 1)->execute();
+      if (count($entity_ids) > self::SELECT_MAX) {
+        if (!isset($description)) {
+          $description = $this->t('Enter the title of a %type to test populating the PDF template.', [
+            '%type' => $default_entity_type,
+          ]);
+        }
+        $form['default_entity_id'] += [
+          '#type' => 'entity_autocomplete',
+          '#default_value' => $default_entity,
+          '#description' => $description,
+        ];
+      }
       else {
-        $description = $this->t('Enter the title of a %type to test populating the PDF template.', [
-          '%type' => $default_entity_type,
-        ]);
+        $options = [];
+        foreach ($storage->loadMultiple($entity_ids) as $id => $entity) {
+          $options[$id] = $entity->label();
+        }
+        if (!isset($description)) {
+          $description = $this->t('Choose a %type to test populating the PDF template.', [
+            '%type' => $default_entity_type,
+          ]);
+        }
+        $form['default_entity_id'] += [
+          '#type' => 'select',
+          '#options' => $options,
+          '#empty_option' => $this->t('- None -'),
+          '#default_value' => $default_entity_id,
+          '#description' => $description,
+        ];
       }
-
-      $form['default_entity_id'] = [
-        '#type' => 'entity_autocomplete',
-        '#title' => $this->t('Default entity'),
-        '#target_type' => $default_entity_type,
-        '#description' => $description,
-        '#weight' => 13,
-        '#default_value' => $default_entity,
-        '#prefix' => '<div id="test-entity-wrapper">',
-        '#suffix' => '</div>',
-        '#ajax' => [
-          'callback' => '::ajaxUpdateIdAutocomplete',
-          'event' => 'autocompleteclose autocompletechange',
-          'wrapper' => 'test-entity-wrapper',
-          'progress' => ['type' => 'none'],
-        ],
-      ];
     }
     // No default entity type set, so just provide a wrapper for AJAX replace.
     else {
-      $form['default_entity_id'] = [
+      $form['default_entity_id'] += [
         '#type' => 'hidden',
-        '#weight' => 13,
-        '#prefix' => '<div id="test-entity-wrapper">',
-        '#suffix' => '</div>',
       ];
     }
 
-    $fid = $entity->id();
+    $fid = $fillpdf_form->id();
 
     /** @var \Drupal\file\FileInterface $file_entity */
-    $file_entity = File::load($entity->get('file')->first()->target_id);
+    $file_entity = File::load($fillpdf_form->get('file')->first()->target_id);
     $pdf_info_weight = 0;
     $form['pdf_info'] = [
       '#type' => 'fieldset',
@@ -347,20 +373,20 @@ class FillPdfFormForm extends ContentEntityForm {
     // @todo: Add a button to let them attempt re-parsing if it failed.
     $form['fillpdf_fields']['fields'] = FillPdf::embedView('fillpdf_form_fields',
       'block_1',
-      $entity->id());
+      $fillpdf_form->id());
 
     $form['fillpdf_fields']['#weight'] = 100;
 
     $form['export_fields'] = [
       '#prefix' => '<div>',
-      '#markup' => $this->l($this->t('Export these field mappings'), Url::fromRoute('entity.fillpdf_form.export_form', ['fillpdf_form' => $entity->id()])),
+      '#markup' => $this->l($this->t('Export these field mappings'), Url::fromRoute('entity.fillpdf_form.export_form', ['fillpdf_form' => $fillpdf_form->id()])),
       '#suffix' => '</div>',
       '#weight' => 100,
     ];
 
     $form['import_fields'] = [
       '#prefix' => '<div>',
-      '#markup' => $this->l($this->t('Import a previous export into this PDF'), Url::fromRoute('entity.fillpdf_form.import_form', ['fillpdf_form' => $entity->id()])),
+      '#markup' => $this->l($this->t('Import a previous export into this PDF'), Url::fromRoute('entity.fillpdf_form.import_form', ['fillpdf_form' => $fillpdf_form->id()])),
       '#suffix' => '</div>',
       '#weight' => 100,
     ];
@@ -425,7 +451,7 @@ class FillPdfFormForm extends ContentEntityForm {
    * @return array
    *   A render array containing the replacement form element.
    */
-  public function ajaxUpdateIdAutocomplete(array &$form, FormStateInterface $form_state) {
+  public function ajaxUpdateEntityId(array &$form, FormStateInterface $form_state) {
     $element = $form['default_entity_id'];
     $triggering_element = reset($form_state->getTriggeringElement()['#array_parents']);
     if ($triggering_element == 'default_entity_type') {
@@ -499,9 +525,9 @@ class FillPdfFormForm extends ContentEntityForm {
     // Save custom form elements' values, resetting default_entity_id to NULL,
     // if not matching the default entity type.
     $default_entity_type = $form_state->getValue('default_entity_type');
-    $default_entity_target_type = isset($form['default_entity_id']['#target_type']) ? $form['default_entity_id']['#target_type'] : NULL;
+    $default_entity_id = ($default_entity_type == $form['default_entity_id']['#target_type']) ? $form_state->getValue('default_entity_id') : NULL;
     $entity->set('default_entity_type', $default_entity_type)
-      ->set('default_entity_id', ($default_entity_type == $default_entity_target_type) ? $form_state->getValue('default_entity_id') : NULL)
+      ->set('default_entity_id', $default_entity_id)
       ->save();
   }
 
diff --git a/tests/src/Functional/FillPdfFormFormTest.php b/tests/src/Functional/FillPdfFormFormTest.php
index a6a24a1..2382421 100644
--- a/tests/src/Functional/FillPdfFormFormTest.php
+++ b/tests/src/Functional/FillPdfFormFormTest.php
@@ -31,42 +31,58 @@ class FillPdfFormFormTest extends FillPdfUploadTestBase {
   public function testDefaultEntityId() {
     $this->uploadTestPdf('fillpdf_test_v3.pdf');
 
-    // Default entity type is not yet given, so there should be no ID element.
-    $this->assertSession()->pageTextNotContains('Default entity ID');
+    // Default entity type is not yet given, so the ID element should be hidden.
+    $this->assertSession()->hiddenFieldExists('default_entity_id');
 
-    $testcases = [];
-    // Test case 0: no entity.
-    $testcases[1]['type'] = 'user';
-    $testcases[1]['id'] = $this->adminUser->id();
-    $testcases[1]['label'] = $this->adminUser->label();
-
-    $testcases[2]['type'] = 'node';
-    $testcases[2]['id'] = $this->testNode->id();
-    $testcases[2]['label'] = $this->testNode->label();
+    // Create 25 more users to check the threshold.
+    for ($i = 0; $i < 25; $i++) {
+      $this->createUser();
+    }
 
-    foreach ($testcases as $case) {
-      $type = $case['type'];
-      $id = $case['id'];
-      $label = $case['label'];
+    $testcases = [
+      // Test case: no referenceable entity.
+      'fillpdf_file_context' => [0, 0, ''],
+      // Test case: 1 referenceable entity.
+      'node' => [1, $this->testNode->id(), $this->testNode->label()],
+      // Test case: 26 referenceable entities.
+      'user' => [26, $this->adminUser->id(), $this->adminUser->label()],
+    ];
 
+    foreach ($testcases as $type => list($count, $id, $label)) {
       // Set a default entity type and check if it's properly saved.
       $this->drupalPostForm(NULL, ['default_entity_type' => $type], self::OP_SAVE);
       $this->assertSession()->pageTextContains("FillPDF Form has been updated.");
-      $this->assertSession()->fieldValueEquals('edit-default-entity-type', $type);
-
-      // Check the default entity ID autocomplete is present now and showing the
-      // correct description.
-      $this->assertSession()->fieldValueEquals('edit-default-entity-id', '');
-      $this->assertSession()->pageTextContains("Enter the title of a $type to test populating the PDF template.");
-
-      // Now set a default entity ID and check if the entity type is unchanged.
-      $this->drupalPostForm(NULL, ['default_entity_id' => $label], self::OP_SAVE);
+      $this->assertSession()->fieldValueEquals('default_entity_type', $type);
+
+      // Check the default entity ID field is present but empty.
+      $this->assertSession()->fieldValueEquals('default_entity_id', NULL);
+
+      if ($count == 0) {
+        $options = $this->assertSession()->selectExists('default_entity_id')->findAll('xpath', 'option');
+        $this->assertCount(1, $options);
+        $this->assertEquals('', $options[0]->getValue());
+        $this->assertEquals('- None -', $options[0]->getText());
+        // Skip the rest and continue with the next test case.
+        continue;
+      }
+      elseif ($count <= 25) {
+        $this->assertSession()->pageTextContains("Choose a $type to test populating the PDF template.");
+        // Now enter an entity title.
+        $this->assertSession()->optionExists('default_entity_id', $id);
+        $this->drupalPostForm(NULL, ['default_entity_id' => $id], self::OP_SAVE);
+        $expected_value = $id;
+      }
+      else {
+        $this->assertSession()->pageTextContains("Enter the title of a $type to test populating the PDF template.");
+        // Now choose an entity and check the entity type is unchanged.
+        $this->drupalPostForm(NULL, ['default_entity_id' => $label], self::OP_SAVE);
+        $expected_value = "$label ($id)";
+      }
+
+      // Check entity type, entity ID and description.
       $this->assertSession()->pageTextContains("FillPDF Form has been updated.");
-      $this->assertSession()->fieldValueEquals('edit-default-entity-type', $type);
-
-      // Check the default entity ID autocomplete is still present and showing
-      // the updated description with the link.
-      $this->assertSession()->fieldValueEquals('edit-default-entity-id', "$label ($id)");
+      $this->assertSession()->fieldValueEquals('default_entity_type', $type);
+      $this->assertSession()->fieldValueEquals('default_entity_id', $expected_value);
       $this->assertSession()->linkExistsExact("Download this PDF template populated with data from the $type $label ($id).");
     }
   }
-- 
GitLab