3

How would you prevent a user from being allowed to enter duplicate entity reference items on an entity reference field?

1 Answer 1

3

Using form alter, you could add an #element_validate function to the field.

function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  if (isset($form['field_articles'])) {
    if (empty($form['field_articles']['widget']['#element_validate'])) {
      $form['field_articles']['widget']['#element_validate'] = ['mymodule_prevent_duplicate_entity_reference'];
    }
    else {
      $form['field_articles']['widget']['#element_validate'][] = 'mymodule_prevent_duplicate_entity_reference';
    }
  }
}

Then in your custom validation function, if there are duplicate entity reference items, set a form error.

function mymodule_prevent_duplicate_entity_reference(array &$element, FormStateInterface $form_state, array &$complete_form) {

  $input = NestedArray::getValue($form_state->getValues(), $element['#parents']);

  $ids = array_filter(array_column($input, 'target_id'));

  // Check that there aren't duplicate entity_id values.
  if (count($ids) !== count(array_flip($ids))) {
    $form_state->setError($element, 'Field "' . $element['#title'] . '" doesn\'t allow duplicates.');
  }

}

If you would like to create a reusable widget, you could extend one of the entity reference widgets, for example, EntityReferenceAutocompleteWidget:

namespace Drupal\mymodule\Plugin\Field\FieldWidget;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
use Drupal\Component\Utility\NestedArray;

/**
 * Plugin implementation of the 'entity_reference_autocomplete' widget.
 *
 * @FieldWidget(
 *   id = "no_duplicate_entity_reference_autocomplete",
 *   label = @Translation("Autocomplete - Disallow Duplicates"),
 *   description = @Translation("An autocomplete text field that validates against duplicates."),
 *   field_types = {
 *     "entity_reference"
 *   }
 * )
 */
class NoDuplicateEntityAutocomplete extends EntityReferenceAutocompleteWidget {


  /**
   * {@inheritdoc}
   */
  public static function afterBuild(array $element, FormStateInterface $form_state) {
    parent::afterBuild($element, $form_state);

    $class = get_class();
    $element['#element_validate'][] = [$class, 'validateNoDuplicates'];

    return $element;
  }

  /**
   * Set a form error if there are duplicate entity ids.
   */
  public static function validateNoDuplicates(array &$element, FormStateInterface $form_state, array &$complete_form) {

    $input = NestedArray::getValue($form_state->getValues(), $element['#parents']);

    $ids = array_filter(array_column($input, 'target_id'));

    // Check that there aren't duplicate entity_id values.
    if (count($ids) !== count(array_flip($ids))) {
      $form_state->setError($element, 'Field "' . $element['#title'] . '" doesn\'t allow duplicates.');
    }

  }
}
2
  • Surprised that core doesn't do this.
    – No Sssweat
    Nov 7, 2017 at 2:12
  • It could easily be a checkbox on the configuration form.
    – oknate
    Nov 7, 2017 at 12:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.