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?

Griva
  • 1,618
  • 20
  • 37
  • Please see [this post](http://stackoverflow.com/a/18894942/1307183) - 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. – Jason Roman Jan 06 '16 at 19:14
  • Nope, In my case `name` is not unique value – Griva Jan 06 '16 at 19:57
  • Why isn't it a unique value? – Jason Roman Jan 06 '16 at 19:59
  • In my case `Candy` exist in many `Boxes` but Candy name is Unique for every `Box` but not for all Boxes at the same time – Griva Jan 06 '16 at 20:02
  • 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. – Jason Roman Jan 06 '16 at 20:03
  • If I understand it correct - yes. I would never have 2 'Chocolate' in 1 Box but Box1 can have and Box2 too but separately – Griva Jan 06 '16 at 20:10
  • 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 automatically – Jason Roman Jan 06 '16 at 20:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/99979/discussion-between-griva-and-jason-roman). – Griva Jan 06 '16 at 20:22

1 Answers1

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.

Griva
  • 1,618
  • 20
  • 37