You are here

public function ValidDynamicReferenceConstraintValidator::validate in Dynamic Entity Reference 8

Same name and namespace in other branches
  1. 8.2 src/Plugin/Validation/Constraint/ValidDynamicReferenceConstraintValidator.php \Drupal\dynamic_entity_reference\Plugin\Validation\Constraint\ValidDynamicReferenceConstraintValidator::validate()

File

src/Plugin/Validation/Constraint/ValidDynamicReferenceConstraintValidator.php, line 59

Class

ValidDynamicReferenceConstraintValidator
Checks if referenced entities are valid.

Namespace

Drupal\dynamic_entity_reference\Plugin\Validation\Constraint

Code

public function validate($value, Constraint $constraint) {

  /** @var \Drupal\dynamic_entity_reference\Plugin\Validation\Constraint\ValidDynamicReferenceConstraint $constraint */

  /** @var \Drupal\Core\Field\FieldItemListInterface $value */
  if (!isset($value)) {
    return;
  }

  // Collect new entities, IDs and target types of existing entities across
  // the field items.
  $new_entities = [];
  $target_ids = [];
  $target_types = [];
  $valid_target_types = DynamicEntityReferenceItem::getTargetTypes($value
    ->getFieldDefinition()
    ->getSettings());
  foreach ($value as $delta => $item) {

    // We don't use a regular NotNull constraint for the target_id property as
    // NULL is allowed if the entity property contains an unsaved entity.
    // @see \Drupal\Core\TypedData\DataReferenceTargetDefinition::getConstraints()
    if (!$item
      ->isEmpty() && ($item->target_id === NULL || $item->target_type === NULL)) {
      $property = $item->target_id ? 'target_type' : 'target_id';
      if (!$item->entity
        ->isNew()) {
        $this->context
          ->buildViolation($constraint->nullMessage)
          ->setParameter('%property', $property)
          ->atPath((string) $delta . '.' . $property)
          ->addViolation();
        continue;
      }
      $new_entities[$delta] = $item->entity;
    }

    // Validate target type.
    $valid_type = empty($item->target_type) || !empty($item->target_type) && DynamicEntityReferenceItem::entityHasIntegerId($item->target_type) && in_array($item->target_type, $valid_target_types);
    if (!$valid_type) {

      // Remove new entity if the target type is not valid.
      if (isset($new_entities[$delta])) {
        unset($new_entities[$delta]);
      }
      $this->context
        ->buildViolation($constraint->wrongTypeMessage)
        ->setParameter('%type', $item->target_type)
        ->atPath((string) $delta . '.target_type')
        ->setInvalidValue($item->target_type)
        ->addViolation();
      continue;
    }

    // '0' or NULL are considered valid empty references.
    if (!empty($item->target_id)) {
      $target_ids[$delta] = $item->target_id;
    }
    $target_types[$delta] = $item->target_type;
  }

  // Collect all new and created valid entities fot bundle validation.
  $valid_entities = [];
  $entity = !empty($value
    ->getParent()) ? $value
    ->getEntity() : NULL;

  // Validate new entities.
  foreach ($new_entities as $delta => $new_entity) {

    /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */
    $handler = $this->selectionManager
      ->getSelectionHandler($value
      ->getFieldDefinition(), $entity, $target_types[$delta]);
    if ($handler instanceof SelectionWithAutocreateInterface && ($new_entity
      ->getEntityTypeId() == $target_types[$delta] || empty($target_types[$delta]))) {
      if (!$handler
        ->validateReferenceableNewEntities([
        $new_entity,
      ])) {
        $this->context
          ->buildViolation($constraint->invalidAutocreateMessage)
          ->setParameter('%type', $new_entity
          ->getEntityTypeId())
          ->setParameter('%label', $new_entity
          ->label())
          ->atPath((string) $delta . '.entity')
          ->setInvalidValue($new_entity)
          ->addViolation();
        continue;
      }
    }
    $valid_entities[$delta] = $new_entity;
  }

  // Validate id for non existent entities.
  foreach ($target_ids as $delta => $target_id) {
    $definition = $this->entityTypeManager
      ->getDefinition($target_types[$delta]);
    $existing_id = $this->entityTypeManager
      ->getStorage($target_types[$delta])
      ->getQuery()
      ->accessCheck(FALSE)
      ->condition($definition
      ->getKey('id'), $target_id)
      ->execute();
    if (!$existing_id) {
      $this->context
        ->buildViolation($constraint->nonExistingMessage)
        ->setParameter('%type', $target_types[$delta])
        ->setParameter('%id', $target_id)
        ->atPath((string) $delta . '.target_id')
        ->setInvalidValue($target_id)
        ->addViolation();
      unset($target_ids[$delta]);
      continue;
    }
    $valid_entities[$delta] = $this->entityTypeManager
      ->getStorage($target_types[$delta])
      ->load($target_id);
  }

  // Validate bundles.
  foreach ($valid_entities as $delta => $entity) {
    if (array_key_exists('target_bundles', $value
      ->getFieldDefinition()
      ->getSetting($target_types[$delta])['handler_settings'])) {
      $valid_bundle = FALSE;
      $target_bundles = $value
        ->getFieldDefinition()
        ->getSetting($target_types[$delta])['handler_settings']['target_bundles'];
      if ($target_bundles === []) {
        $this->context
          ->buildViolation($constraint->noBundleAllowed)
          ->setParameter('%type', $target_types[$delta])
          ->atPath((string) $delta . '.entity')
          ->setInvalidValue($entity)
          ->addViolation();
        if (isset($target_ids[$delta])) {
          unset($target_ids[$delta]);
        }
        continue;
      }
      if ($target_bundles === NULL || in_array($entity
        ->bundle(), $target_bundles)) {
        $valid_bundle = TRUE;
      }
      if (!$valid_bundle) {
        $this->context
          ->buildViolation($constraint->wrongBundleMessage)
          ->setParameter('%label', $entity
          ->label())
          ->setParameter('%bundles', implode(', ', $target_bundles))
          ->atPath((string) $delta . '.entity')
          ->setInvalidValue($entity)
          ->addViolation();
        if (isset($target_ids[$delta])) {
          unset($target_ids[$delta]);
        }
      }
    }
  }

  // Validate target ids.
  foreach ($target_ids as $delta => $id) {
    $handler = $this->selectionManager
      ->getSelectionHandler($value
      ->getFieldDefinition(), $entity, $target_types[$delta]);
    $valid_target_id = $handler
      ->validateReferenceableEntities([
      $id,
    ]);
    if (!$valid_target_id) {
      $this->context
        ->buildViolation($constraint->message)
        ->setParameter('%type', $target_types[$delta])
        ->setParameter('%id', $id)
        ->atPath((string) $delta . '.target_id')
        ->setInvalidValue($id)
        ->addViolation();
    }
  }
}