symfony2 对子实体的验证防止编辑父实体
Posted
技术标签:
【中文标题】symfony2 对子实体的验证防止编辑父实体【英文标题】:symfony2 validation of child entity prevents editing of parent entity 【发布时间】:2013-09-04 19:52:59 【问题描述】:我的几个实体现在遇到了这个问题,所以我想尝试了解真正发生的事情,我在这里求助于我最好的来源(将尽快为这个问题添加一个赏金符合条件)。
我的用户属于某个用户组。我有一个 userGroup 实体的验证器,以确保没有两个 userGroup 具有相同的名称。
问题是,当我去编辑一个用户并尝试为该用户选择该用户组时,symfony2 的行为就像我试图创建另一个具有相同名称的用户组,而实际上我所做的只是我正在尝试为用户选择该用户组。
用户实体
<?php
// src/BizTV/UserBundle/Entity/User.php
namespace BizTV\UserBundle\Entity;
use BizTV\UserBundle\Validator\Constraints as BizTVAssert;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use BizTV\BackendBundle\Entity\company as company;
/**
* @ORM\Entity
* @ORM\Table(name="fos_user")
*/
class User extends BaseUser implements AdvancedUserInterface
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
//TODO: Add constraint on $name * @BizTVAssert\NameExists (and finish coding this constraint)
/**
* @var object BizTV\BackendBundle\Entity\company
*
* @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
* @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
*/
protected $company;
/**
* @var object BizTV\UserBundle\Entity\UserGroup
* @ORM\ManyToOne(targetEntity="BizTV\UserBundle\Entity\UserGroup")
* @ORM\JoinColumn(name="userGroup", referencedColumnName="id", nullable=true)
*/
protected $userGroup;
/**
* @ORM\ManyToMany(targetEntity="BizTV\ContainerManagementBundle\Entity\Container", inversedBy="users")
* @ORM\JoinTable(name="access")
*/
private $access;
/**
* @var object BizTV\ContainerManagementBundle\Entity\Container
*
* This only applies to the BizTV server user accounts or "screen display accounts". Others will have null here.
*
* @ORM\ManyToOne(targetEntity="BizTV\ContainerManagementBundle\Entity\Container")
* @ORM\JoinColumn(name="screen", referencedColumnName="id", nullable=true)
*/
protected $screen;
/**
* @ORM\Column(type="boolean", nullable=true)
*/
protected $isServer;
public function __construct()
parent::__construct();
$this->access = new \Doctrine\Common\Collections\ArrayCollection();
/**
* Get id
*
* @return integer
*/
public function getId()
return $this->id;
/**
* Set company
*
* @param BizTV\BackendBundle\Entity\company $company
*/
public function setCompany(\BizTV\BackendBundle\Entity\company $company)
$this->company = $company;
/**
* Get company
*
* @return BizTV\BackendBundle\Entity\company
*/
public function getCompany()
return $this->company;
/**
* Add access
*
* @param BizTV\ContainerManagementBundle\Entity\Container $access
*/
public function addContainer(\BizTV\ContainerManagementBundle\Entity\Container $access)
$this->access[] = $access;
/**
* Get access
*
* @return Doctrine\Common\Collections\Collection
*/
public function getAccess()
return $this->access;
/**
* Set screen
*
* @param BizTV\ContainerManagementBundle\Entity\Container $screen
*/
public function setScreen(\BizTV\ContainerManagementBundle\Entity\Container $screen)
$this->screen = $screen;
/**
* Get screen
*
* @return BizTV\ContainerManagementBundle\Entity\Container
*/
public function getScreen()
return $this->screen;
/**
* Set isServer
*
* @param boolean $isServer
*/
public function setIsServer($isServer)
$this->isServer = $isServer;
/**
* Get isServer
*
* @return boolean
*/
public function getIsServer()
return $this->isServer;
/**
* Set userGroup
*
* @param BizTV\UserBundle\Entity\UserGroup $userGroup
*/
public function setUserGroup(\BizTV\UserBundle\Entity\UserGroup $userGroup = null)
$this->userGroup = $userGroup;
/**
* Get userGroup
*
* @return BizTV\UserBundle\Entity\UserGroup
*/
public function getUserGroup()
return $this->userGroup;
用户链接到的用户组实体:
<?php
namespace BizTV\UserBundle\Entity;
use BizTV\UserBundle\Validator\Constraints as BizTVAssert;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* BizTV\UserBundle\Entity\UserGroup
*
* @ORM\Table()
* @ORM\Entity
*/
class UserGroup
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $name
* @BizTVAssert\NameExists
* @ORM\Column(name="name", type="string", length=255)
* @Assert\NotBlank(message = "Du måste ange ett gruppnamn")
*/
private $name;
/**
* @var object BizTV\BackendBundle\Entity\company
*
* @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
* @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
*/
protected $company;
/**
* Get id
*
* @return integer
*/
public function getId()
return $this->id;
/**
* Set name
*
* @param string $name
*/
public function setName($name)
$this->name = $name;
/**
* Get name
*
* @return string
*/
public function getName()
return $this->name;
/**
* Set company
*
* @param BizTV\BackendBundle\Entity\company $company
*/
public function setCompany(\BizTV\BackendBundle\Entity\company $company)
$this->company = $company;
/**
* Get company
*
* @return BizTV\BackendBundle\Entity\company
*/
public function getCompany()
return $this->company;
NameExistsValidator
<?php
namespace BizTV\UserBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager as EntityManager;
class NameExistsValidator extends ConstraintValidator
private $container;
private $em;
public function __construct(Container $container, EntityManager $em)
$this->container = $container;
$this->em = $em;
public function isValid($value, Constraint $constraint)
$em = $this->em;
$container = $this->container;
$company = $this->container->get('security.context')->getToken()->getUser()->getCompany();
//Fetch entities with same name
$repository = $em->getRepository('BizTVUserBundle:UserGroup');
//$repository = $this->getDoctrine()->getRepository('BizTVContainerManagementBundle:Container');
$query = $repository->createQueryBuilder('c')
->where('c.company = :company')
->setParameter('company', $company)
->orderBy('c.name', 'ASC')
->getQuery();
$groups = $query->getResult();
foreach ($groups as $g)
if ($g->getName() == $value)
$this->setMessage('Namnet '.$value.' är upptaget, vänligen välj ett annat', array('%string%' => $value));
return false;
return true;
用户编辑表单
<?php
namespace BizTV\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\CallbackValidator;
use Symfony\Component\Form\FormValidatorInterface;
use Symfony\Component\Form\FormError;
use Doctrine\ORM\EntityRepository;
class editUserType extends AbstractType
function __construct($company)
$this->company = $company;
public function buildForm(FormBuilder $builder, array $options)
$company = $this->company;
$builder
->add('locked', 'checkbox', array('label' => 'Kontot är låst, användaren kan inte logga in '))
->add('username', 'text', array('label' => 'Användarnamn '))
;
$builder
->add('userGroup', 'entity', array(
'label' => 'Användargrupp',
'empty_value' => 'Ingen grupptillhörighet',
'property' => 'name',
'class' => 'BizTV\UserBundle\Entity\UserGroup',
'query_builder' => function(\Doctrine\ORM\EntityRepository $er) use ($company)
$qb = $er->createQueryBuilder('a');
$qb->where('a.company = :company');
$qb->setParameters( array('company' => $company) );
$qb->orderBy('a.name', 'ASC');
return $qb;
));
$builder
->add('email', 'email', array('label' => 'Epost '))
->add('plainPassword', 'repeated', array('type' => 'password', 'first_name' => 'Nytt lösenord ', 'second_name' => 'Upprepa lösenord ',));
$builder
->add('roles', 'choice', array(
'label' => 'Roller',
'expanded' => true,
'multiple' => true,
'choices' => array(
'ROLE_CONTENT' => 'Innehåll (Användaren kan lägga till, redigera och ta bort innehåll där du nedan beviljar åtkomst)',
'ROLE_LAYOUT' => 'Skärmlayout (Användaren kan skapa ny skärmlayout, redigera befintlig eller ta bort gällande skärmlayout där du nedan beviljar åtkomst)',
'ROLE_VIDEO' => 'Videouppladdning (Användaren har rätt att ladda upp videofiler till företagets mediabibliotek)',
'ROLE_ADMIN' => 'Administratör (Användaren är administratör med fulla rättigheter till allt precis som det konto du nu är inloggad på, var mycket restriktiv med att tilldela denna behörighet).',
),
))
;
$builder
->add('access', 'entity', array(
'label' => 'Behörigheter',
'multiple' => true, // Multiple selection allowed
'expanded' => true, // Render as checkboxes
'property' => 'select_label',
'class' => 'BizTV\ContainerManagementBundle\Entity\Container',
'query_builder' => function(\Doctrine\ORM\EntityRepository $er) use ($company)
$qb = $er->createQueryBuilder('a');
$qb->innerJoin('a.containerType', 'ct');
$qb->where('a.containerType IN (:containers)', 'a.company = :company');
$qb->setParameters( array('containers' => array(1,2,3,4), 'company' => $company) );
$qb->orderBy('ct.id', 'ASC');
return $qb;
));
$builder-> addValidator(new CallbackValidator(function(FormInterface $form)
$email = $form->get('email')->getData();
if (empty( $email ))
$form['email']->addError(new FormError("Du måste ange en epostadress för användaren"));
));
$builder-> addValidator(new CallbackValidator(function(FormInterface $form)
$username = $form->get('username')->getData();
if (strpos($username,'#') !== false)
$form['username']->addError(new FormError("Användarnamnet får inte innehålla tecknet #"));
));
$builder-> addValidator(new CallbackValidator(function(FormInterface $form)
$username = $form->get('username')->getData();
if (empty($username))
$form['username']->addError(new FormError("Du måste ange ett namn för användaren"));
));
//TODO check if username exists
public function getName()
return 'biztv_userbundle_newusertype';
【问题讨论】:
【参考方案1】:您的NameExistsValidator
这样做:
但我认为你希望它这样做:
如果我发现 另一个 用户组与我正在检查的名称相同,则会失败。换句话说:验证器需要完整的UserGroup
实体(或至少它的 id 和名称)来检查具有相同名称但不同 id 的用户组。
Symfony 2 已经有一个UniqueEntity 验证器,你为什么不使用它?
使用注解看起来像这样:
/**
* @ORM\Entity
* @AssertUniqueEntity(fields="name", message="This name already exists")
*/
class UserGroup
【讨论】:
【参考方案2】:一种可能且最简单的解决方案是定义Validation Groups。例如,当您创建一个组时,您可以使用名为“create”或“groups”的验证组,而在创建用户时不指定组。那么验证器将不会应用于用户创建过程。
验证组可以在表单类中动态分配。您可以在documentation 中看到这方面的一个示例。
【讨论】:
所以你是说我应该将验证绑定到表单而不是绑定到实体?以上是关于symfony2 对子实体的验证防止编辑父实体的主要内容,如果未能解决你的问题,请参考以下文章
Symfony2 使用父级中的一个字段和具有不同注释/映射的扩展实体