我正在创建一个使用NestedTree行为实现的文件夹结构。
此外,如果两个文件夹是兄弟姐妹,我不希望这两个文件夹具有相同的名称。
为此,我使用@UniqueEntity
和@UniqueConstraint
注释的组合,但它不起作用。
首先我的实体(因为它与NestedTree默认值100%相同而被剥离到最小值):
/**
* @ORM\Entity
* @Gedmo\Tree(type="nested")
* @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
* @UniqueEntity(fields={"parent", "name"})
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="uniq_url", columns={"parent_id", "name"})})
*/
class Folder
{
/**
* @ORM\Column(type="string", nullable=false)
*/
protected $name;
/**
* @Gedmo\TreeParent
* @ORM\ManyToOne(targetEntity="Folder", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $parent;
}
当我创建两个具有相同名称的文件夹时,我违反了完整性约束,这意味着数据库中的@UniqueConstraints
有效,但@UniqueEntity
没有:
Integrity constraint violation: 1062 Duplicate entry 'name_of_folder' for key 'uniq_url'
我也尝试将ignoreNull键设置为false(默认为true):
@UniqueEntity(fields={"parent", "name"}, ignoreNull=false)
然后我收到了这个错误:
Warning: ReflectionProperty::getValue() expects parameter 1 to be object, null given in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php line 670
我已将错误归结为Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator
中的这些行:
$criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity);
if ($constraint->ignoreNull && null === $criteria[$fieldName]) {
return;
}
if ($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.
*/
$em->initializeObject($criteria[$fieldName]);
$relatedClass = $em->getClassMetadata($class->getAssociationTargetClass($fieldName));
//problem
$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);
}
问题出现在标有//problem
的行上。似乎$criteria[$fieldName] === null
是错误的原因。
所以我在这里,不知道该做什么...... 有没有人知道发生了什么?
谢谢。
答案 0 :(得分:0)
没有简单的方法可以摆脱这种情况。 我终于走自己的路,创造了一个验证器:
/**
* @ORM\Entity(repositoryClass="Ibiz\DoctrineExtensionsBundle\Entity\Repository\NestedTreeRepository")
* @Gedmo\Tree(type="nested")
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="uniq_url", columns={"parent_id", "name"})})
* @IbizAssert\UniquePath("getName")
*/
class Folder
{
/**
* @ORM\Column(type="string", nullable=false)
*/
protected $name;
public function getName()
{
return $this->name;
}
}
namespace Ibiz\DoctrineExtensionsBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class UniquePath extends Constraint
{
public $em = null;
public $errorMethod = null;
public $message = 'The name "%name%" already exists.';
public $service = 'ibiz.validator.unique_path';
public function validatedBy()
{
return $this->service;
}
public function getRequiredOptions()
{
return array('errorMethod');
}
public function getDefaultOption()
{
return 'errorMethod';
}
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
namespace Ibiz\DoctrineExtensionsBundle\Validator\Constraints;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class UniquePathValidator extends ConstraintValidator
{
private $registry;
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
}
public function validate($entity, Constraint $constraint)
{
if ($constraint->errorMethod === null)
{
throw new ConstraintDefinitionException('ErrorMethod should be set');
} else if (!is_string($constraint->errorMethod)) {
throw new UnexpectedTypeException($constraint->errorMethod, 'string');
}
if ($constraint->em) {
$em = $this->registry->getManager($constraint->em);
} else {
$em = $this->registry->getManagerForClass(get_class($entity));
}
$className = $this->context->getClassName();
$repo = $em->getRepository($className);
$count = $repo->getSameNameSiblingsCount($entity);
if ($count != 0) {
$this->context->addViolation($constraint->message, array('%name%' => $entity->{$constraint->errorMethod}()));
}
}
}
namespace Ibiz\DoctrineExtensionsBundle\Entity\Repository;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository as BaseRepository;
class NestedTreeRepository extends BaseRepository
{
public function getSameNameSiblingsCountQueryBuilder($node)
{
$meta = $this->getClassMetadata();
if (!$node instanceof $meta->name) {
throw new InvalidArgumentException("Node is not related to this repository");
}
$config = $this->listener->getConfiguration($this->_em, $meta->name);
$qb = $this->_em->createQueryBuilder();
$qb->select($qb->expr()->count('n.id'))
->from($config['useObjectClass'], 'n');
if ($node->getParent() === null) {
$qb->where($qb->expr()->andx(
$qb->expr()->eq('n.name', ':name'),
$qb->expr()->isNull('n.parent')
))
->setParameters(array(
'name' => $node->getName(),
));
} else {
$qb->leftJoin('n.parent', 'p')
->where($qb->expr()->andx(
$qb->expr()->eq('n.name', ':name'),
$qb->expr()->eq('p.name', ':parent')
))
->setParameters(array(
'name' => $node->getName(),
'parent' => $node->getParent()->getName(),
));
}
return $qb;
}
public function getSameNameSiblingsCountQuery($node)
{
return $this->getSameNameSiblingsCountQueryBuilder($node)->getQuery();
}
public function getSameNameSiblingsCount($node)
{
return $this->getSameNameSiblingsCountQuery($node)->getSingleScalarResult();
}
}