문제

I have run into this problem with a couple of my entities now so I thought to try and get a hang of what really goes on, and I turn to my best source here (will add a bounty to this question as soon as it is eligible).

My user is part of a user group. I have a validator for the userGroup entity to make sure no two userGroups have the same name.

The problem is that when I go to editing a user, and try to select that userGroup for the user, symfony2 is behaving as if I were trying to create another userGroup with that same name, when in reality all I am doing is I am trying to select that userGroup for the user.

A user entity

<?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;
    }
}

The UserGroup entity that the User is linked to:

<?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;
    }
}

The 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;
    }
}

User edit form

<?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';
    }
}
도움이 되었습니까?

해결책

Your NameExistsValidator does this:

  • Fail if I find any user-group with the name I'm checking.

But I think you want it to do this:

  • Fail if I find another user-group with the name I'm checking.

In other words: the validator needs the complete UserGroup entity (or at least its id and name) to check for a user-group with the same name but different id.

Symfony 2 already has a UniqueEntity validator, why don't you use it?

Using annotations this would look something like this:

/**
 * @ORM\Entity
 * @AssertUniqueEntity(fields={"name"}, message="This name already exists")
 */
class UserGroup
{

다른 팁

One possible and simplest solution is to define Validation Groups. For example, when you create a group, you can use the validation group named 'create' or 'groups' and when you create a user does not specify a group. Then validator will not apply to user creation process.

Validation Groups can be assigned dynamically in the form class. An example of this you can see in the documentation.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top