Validate UniqueEntity for One-To-Many, Unidirectional with Join Table [Symfony2]
Asked Answered
N

1

2

I have 2 mapped entities,

Box

class Box{
    //[...]
    /**
     * @ORM\ManyToMany(targetEntity="Candy", cascade={"remove"})
     * @ORM\OrderBy({"power" = "DESC"})
     * @ORM\JoinTable(name="box_candies",
     *      joinColumns={@ORM\JoinColumn(name="box_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="candy_id", referencedColumnName="id", unique=true)}
     *      )
     */
    private $candies;
}

And Candy

class Candy
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

As you can see, this is One-To-Many, Unidirectional with Join Table association. Box can "store" candies, but Candy knows nothing about Box (where is).

Now I have page where I can make candy and there is form and standard isValid() and after that:

$box->addCandy($candy);
$entity_manager->persist($candy);
$entity_manager->persist($box);
$entity_manager->flush();

Now, where is my problem?

I would like to Box can store only unique candies (by name), that means Box can store Candy objects with names "Choco" and "Orange" but can't "Mayonnaise" and "Mayonnaise"

When making candy i can't validate with UniqueEntity constraint because the candy does not know about the box. I thought about Callback validator for Box or create own Constraint but i think it's better to ask:

How should I do it?

Neils answered 30/12, 2015 at 4:45 Comment(8)
Please see this post - you should put a UniqueEntity on the name field of your Candy entity. When you are doing the @ManyToMany annotation on your Box entity, Doctrine will automatically treat that as a composite primary key for you and enforce uniqueness exactly as you want.Alameda
Nope, In my case name is not unique valueNeils
Why isn't it a unique value?Alameda
In my case Candy exist in many Boxes but Candy name is Unique for every Box but not for all Boxes at the same timeNeils
I'm not sure I understand that - but Candy is its own entity, completely unrelated to anything with Boxes, correct? So you would never have 2 entries for 'Chocolate' in your candy table. Then each box would simply link to whatever candies are in them with the many-to-many relationship you already have.Alameda
If I understand it correct - yes. I would never have 2 'Chocolate' in 1 Box but Box1 can have and Box2 too but separatelyNeils
Right - so you can put UniqueEntity on the name field of your Candy entity, and then your @ManyToMany annotation will handle the uniqueness for each Box for you automaticallyAlameda
Let us continue this discussion in chat.Neils
N
0

Answer is late but it may help someone so this is solution I implemented:

In my case I can create Candy only in one place via form so finally I decided to create additional special validation for that case in my controller/service.

Simply speaking I made code checking the name in my own way and when it's invalid, form just trow Error to prevent creation. I want to emphasize that this is a little dirty solution and also it is not scalable because you have to remember always to add it in correct place if you create Candy in other places.

    // special unique name validation
    $candy_name = $form->get('name')->getData();
    if($candy_name){
        $found_candy = $box->getCandyByName($candy_name);
        if($found_candy){
            $error = new FormError( $this->get('translator')->trans("candy.name.exist", array(), "validators") );
            $form->get('name')->addError($error);
        }
    }

Anyway it worked but depending on your case, Callback can be way better solution or even simple UniqueEntity constraint.

Neils answered 23/2, 2018 at 18:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.