Ошибка проверки Symfony2 UniqueEntity с наследованием объекта

У меня есть патнер, покупатель и класс администратора, которые наследуют класс пользователя

Когда я хочу добавить партнера, валидатор не работает

 * @DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
 * @DoctrineAssert\UniqueEntity(fields="mail", message="Cette adresse mail est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})

Если я выберу имя пользователя для "партнера", который уже находится в базе данных, он покажет мне правильную ошибку проверки. Но если я выберу имя пользователя "покупателя", которое уже есть в базе данных, проверка не будет произведена, и в моих базах будет ошибка уникального поля.

Класс пользователя

<?php

namespace Antho\Test\CoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;

/**
 * Antho\Test\CoreBundle\Entity\User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\UserRepository")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"user" = "User", "partner" = "Partner", "buyer" = "Buyer", "admin" = "Admin"})
 * @ORM\HasLifecycleCallbacks()
 * @DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
 * @DoctrineAssert\UniqueEntity(fields="mail", message="Cette adresse mail est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
 */
class User implements UserInterface
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $username
     *
     * @ORM\Column(name="username", type="string", length=255, unique=true)
     */
    private $username;

    /**
     * @var string $lastName
     *
     * @ORM\Column(name="last_name", type="string", length=255)
     */
    private $lastName;

    /**
     * @var string $firstName
     *
     * @ORM\Column(name="first_name", type="string", length=255)
     */
    private $firstName;

    /**
     * @var string $mail
     *
     * @ORM\Column(name="mail", type="string", length=255, unique=true)
     */
    private $mail;

    /**
     * @var string $password
     *
     * @ORM\Column(name="password", type="string", length=255)
     */
    private $password;


    public function __construct()
    {
        if ($this->createdAt === null) {
            $this->createdAt = new \DateTime('now');
        }
        $this->isEnabled = true;
    }

    public function __toString()
    {
        return $this->username;
    }

    GETTER and SETTER ...
}

Партнер класса

<?php

namespace Antho\Test\CoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;

/**
 * Antho\Test\CoreBundle\Entity\Partner
 *
 * @ORM\Table(name="partner")
 * @ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\PartnerRepository")
 */
class Partner extends User
{
    /**
     * @ORM\OneToMany(targetEntity="Restaurant", mappedBy="partner", cascade={"remove", "persist"})
     */
    private $restaurants;

    /**
     * @var string $company
     *
     * @ORM\Column(name="company", type="string", length=255)
     */
    private $company;


    public function __construct()
    {
        parent::__construct();
        $this->restaurants = new \Doctrine\Common\Collections\ArrayCollection();
        $this->isValid = false;
    }

    GETTER and SETTER ...
}

Класс Покупатель

<?php

namespace Antho\Test\CoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;

/**
 * Antho\Test\CoreBundle\Entity\Buyer
 *
 * @ORM\Table(name="buyer")
 * @ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\BuyerRepository")
 */
class Buyer extends User
{
    /**
     * @var string $address
     *
     * @ORM\Column(name="address", type="string", length=255)
     */
    private $address;

    /**
     * @var string $city
     *
     * @ORM\Column(name="city", type="string", length=255)
     */
    private $city;

    /**
     * @var string $zip
     *
     * @ORM\Column(name="zip", type="string", length=255)
     */
    private $zip;

    /**
     * @var boolean $newsletter
     *
     * @ORM\Column(name="newsletter", type="boolean")
     */
    private $newsletter;


    public function __construct()
    {
        parent::__construct();
        if ($this->newsletter === null) {
            $this->newsletter = false;
        }
    }

    GETTER and SETTER ...
}

1 ответ

Это старый вопрос без ответа, однако люди все еще могут столкнуться с этой проблемой. И это может помочь и сэкономить их время.

Проблема в том, что UniqueEntityValidator по умолчанию принимает только текущий (тот, который проверяет) репозиторий сущностей. Другими словами, если вы отправляете Партнера, он проверяет только записи Партнера. Он не принимает во внимание другие объекты, такие как покупатель или пользователь, в сопоставлении наследования single_table.

Чтобы решить вашу проблему, вы можете просто добавить в аннотацию entityClass атрибут с именем вашего родительского класса:

@DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"}, entityClass="Antho\Test\CoreBundle\Entity\User")

Эта часть entityClass="Antho\Test\CoreBundle\Entity\User" фактически решает вашу проблему.

В этом случае валидатор будет просматривать все ваши пользовательские записи (включая его дочерние записи).

Мои два цента. Не притворяйся правильным ответом.

Удалить все ненужное @Table из подклассов и сделать поля видимыми (protected). Я сделал то же самое (с Symfony 2.0.x), и это работает как шарм.

Это немного отличается от вашего кода, здесь имя тега (или ключевое слово) уникально для каждого пользователя. Но вы можете проверить это в любом случае:

/**
 * @ORM\Entity
 * @ORM\Table(
 *     name="meta",
 *     uniqueConstraints={
 *         @ORM\UniqueConstraint(columns={"name", "user_id", "type"})
 *     },
 *     indexes={
 *         @ORM\index(columns={"description"}),
 *         @ORM\index(columns={"type"})
 *     }
 * )
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"tag" = "Tag", "keyword" = "Keyword"})
 * @UniqueEntity(fields={"name", "user"})
 */
abstract class Meta
{
    protected $name;

    protected $user;
}

Детские классы:

/**
 * @ORM\Entity
 */
class Tag extends Meta { }

/**
 * @ORM\Entity
 */
class Keyword extends Meta { }
Другие вопросы по тегам