简体   繁体   中英

Symfony2/Doctrine UniqueEntity on ManyToOne entity ignored

I have "roles" associated to "projects". I don't care if a role name is duplicated, but what I want to make sure is that for each project the role name cannot be duplicated.

Here is what I thought should work:

<?php
// src/AppBundle/Entity/Role.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\RoleRepository")
 * @ORM\Table(name="roles")
 * @UniqueEntity(fields={"name","project"}, message="Duplicated role for this project")
 */
class Role
{
/**
 * @ORM\Column(type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\Column(type="string", length=100)
 */
protected $name;

/**
 * @ORM\Column(type="text")
 */
protected $description;

...
other fields
...

/**
 * @ORM\ManyToOne(targetEntity="Project")
 */
protected $project;
}

According to the documentation here that's exactly what I need:

This required option is the field (or list of fields) on which this entity should be unique. For example, if you specified both the email and name field in a single UniqueEntity constraint, then it would enforce that the combination value where unique (eg two users could have the same email, as long as they don't have the same name also).

The constraint is simply ignored (I mean that if I try to have the same role name for the same project, it stores the duplicated role name and projectID).

What am I missing?

EDIT: after I updated the DB with "php app/console doctrine:schema:update --force" I tried to generate the error directly with SQL, but no exceptions were thrown. Now, I don't know if this "UniqueEntity" validation is done at DB level or it's Doctrine's validator.

EDIT2: I tried to have only one field ("name") and the validation works properly (only on that field of course). I also tried to have validation on the fields "name" and "description" and it works!!! So basically it does not validate if the field to be validated is the ID pointing to another table.

Anyways, here is the controller:

/**
 * @Route("/role/create/{projectID}", name="role_create")
 */
public function createRoleAction(Request $request, $projectID)
{
    $prj = $this->getDoctrine()->getRepository('AppBundle:Project')->findOneById($projectID);

    $role = new Role();
    $form = $this->createForm(new RoleFormType(), $role);

    $form->handleRequest($request);

    if ($form->isValid())
        {
        $em = $this->getDoctrine()->getManager();
        $role->setProject($prj);
        $em->persist($role);
        $em->flush();
        return $this->redirect($this->generateUrl('hr_manage', array('projectID' => $projectID)));
        }

return $this->render('Role/createForm.html.twig', array('projectID' => $projectID, 'form' => $form->createView(),));
}

The validation is not performed, and the entity persisted on DB, with the "project" column pointing at the right project. Here is a snapshot of the 2 relevant fields: 在此处输入图片说明

Here is the RoleFormType (an extract of relevant fields):

<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RoleFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    // add your custom field
    $builder->add('name', 'text')
            ->add('description', 'text')
            ...lots of other fields, but "project" is not present as it's passed automatically from the controller
            ->add('save', 'submit', array('label' => 'Create'));

}

public function getName()
{
    return 'role';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array('data_class' => 'AppBundle\Entity\Role',));
}

}

The problem is you are not actually validating the entity to check for the unique constraint violation. When you call $form->isValid() , it does call the validator for the Role entity since you passed that as the form's data class. However, since project is not set until after that, no validation occurs on the project field.

When you call $em->persist($role); and $em->flush(); this simply tells Doctrine to insert the entity into the database. Those 2 calls do not perform validation on their own, so the duplicates will be inserted.

Try setting the project before creating the form instead:

$role = new Role();
$role->setProject($prj);

$form = $this->createForm(new RoleFormType(), $role);

Now project will be set on the entity, so when $form->isValid() is called the Symfony validator will check for uniqueness.

If that doesn't work, you'll want to add a project type to the form as a hidden field as well so it's passed back, but I don't think that will be necessary.

The other thing I would state is that you definitely want to add the unique constraint on your database itself - this way even if you try to insert a duplicate, the database will thrown an exception back to you and not allow it, regardless of your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM