/** * Returns the values of the identifier fields of an entity. * * Doctrine must know about this entity, that is, the entity must already * be persisted or added to the identity map before. Otherwise an * exception is thrown. * * @param object $entity The entity for which to get the identifier * * @return array The identifier values * * @throws FormException If the entity does not exist in Doctrine's identity map */ private function getIdentifierValues($entity) { if (!$this->em->contains($entity)) { throw new FormException('Entities passed to the choice field must be managed'); } $this->em->initializeObject($entity); return $this->classMetadata->getIdentifierValues($entity); }
/** * Returns the values of the identifier fields of an entity. * * Doctrine must know about this entity, that is, the entity must already * be persisted or added to the identity map before. Otherwise an * exception is thrown. * * @param object $entity The entity for which to get the identifier * * @return array The identifier values * * @throws RuntimeException If the entity does not exist in Doctrine's identity map */ private function getIdentifierValues($entity) { if (!$this->em->contains($entity)) { throw new RuntimeException('Entities passed to the choice field must be managed. Maybe ' . 'persist them in the entity manager?'); } $this->em->initializeObject($entity); return $this->classMetadata->getIdentifierValues($entity); }
/** * Returns the ID value for an object. * * This method assumes that the object has a single-column ID. * * @param object $object The object. * * @return mixed The ID value. */ public function getIdValue($object) { if (!$object) { return; } if (!$this->om->contains($object)) { throw new RuntimeException('Entities passed to the choice field must be managed. Maybe ' . 'persist them in the entity manager?'); } $this->om->initializeObject($object); return current($this->classMetadata->getIdentifierValues($object)); }
/** * @param object $entity * @param Constraint $constraint * * @throws UnexpectedTypeException * @throws ConstraintDefinitionException */ public function validate($entity, Constraint $constraint) { /** @var Unique $constraint */ if (!$constraint instanceof Unique) { throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\\Unique'); } if (!is_array($constraint->fields) && !is_string($constraint->fields)) { throw new UnexpectedTypeException($constraint->fields, 'array'); } if (null !== $constraint->errorPath && !is_string($constraint->errorPath)) { throw new UnexpectedTypeException($constraint->errorPath, 'string or null'); } $fields = (array) $constraint->fields; if (0 === count($fields)) { throw new ConstraintDefinitionException('At least one field has to be specified.'); } $class = $this->em->getClassMetadata($constraint->className); /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ $criteria = []; foreach ($fields as $fieldName) { if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) { throw new ConstraintDefinitionException(sprintf("The field '%s' is not mapped by Doctrine, so it cannot be validated for uniqueness.", $fieldName)); } $criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity); if ($constraint->ignoreNull && null === $criteria[$fieldName]) { return; } if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { /* Ensure the Proxy is initialized before using reflection to * read its identifiers. This is necessary because the wrapped * getter methods in the Proxy are being bypassed. */ $this->em->initializeObject($criteria[$fieldName]); $relatedClass = $this->em->getClassMetadata($class->getAssociationTargetClass($fieldName)); $relatedId = $relatedClass->getIdentifierValues($criteria[$fieldName]); if (count($relatedId) > 1) { throw new ConstraintDefinitionException('Associated entities are not allowed to have more than one identifier field to be ' . 'part of a unique constraint in: ' . $class->getName() . '#' . $fieldName); } $criteria[$fieldName] = array_pop($relatedId); } } $repository = $this->em->getRepository($constraint->className); $result = $repository->{$constraint->repositoryMethod}($criteria); /* If the result is a MongoCursor, it must be advanced to the first * element. Rewinding should have no ill effect if $result is another * iterator implementation. */ if ($result instanceof \Iterator) { $result->rewind(); } elseif (is_array($result)) { reset($result); } /* If no entity matched the query criteria or a single entity matched, * which is the same as the entity being validated, the criteria is * unique. */ if (0 === count($result)) { return; } if (1 === count($result)) { $props = PropertyAccess::createPropertyAccessor(); if ($props->isReadable($entity, $constraint->idField)) { $current = is_array($result) ? current($result) : $result; if ($props->getValue($entity, $constraint->idField) === $props->getValue($current, $constraint->idField)) { return; } } } $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; $invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]]; $this->buildViolation($constraint->message)->atPath($errorPath)->setInvalidValue($invalidValue)->addViolation(); }