Updating (from the inverse side) bidirectional many-to-many relationships in Doctrine 2?

后端 未结 2 688
小鲜肉
小鲜肉 2021-02-09 06:58

Customer is the inverse side of \"keywords/customers\" relationship with Keyword:

/**
 * @ORM         


        
2条回答
  •  梦毁少年i
    2021-02-09 07:22

    Ok, found the way, even if I'm not fully satisfied. The key was this example form collection field type. Basically what's happening with my previous form definition was:

    $customer->getKeywords() = $postData; // $postData is somewhere in form framework
    

    And that is just an assignment of a collection (of selected keywords) to customer keywords. No method were invoked on Keyword instances (owning side). The key option is by_reference (for me it's just a bad name, but anyways...):

    $form
        ->add($this->factory->createNamed('entity', 'keywords', null, array(
            // ...
            'by_reference' => false
        ))
    );
    

    This way the form framework is going to call the setter, that is $customer->setKeywords(Collection $keywords). In that method, you can "tell" the owning side to store your association:

    public function setKeywords(Collection $keywords)
    {
        foreach($keywords as $keyword) {
            $keyword->addCustomer($this); // Owning side call!
        }
    
        $this->keywords = $keywords;
    
        return $this;
    }
    

    (Always check for duplicate instances on the owning side, using contains method).

    At this point, only checked keywords will be added ($keyword argument). There is the need to manage removal of unchecked keywords (controller side):

    $originalKeywords = $customer->getKeywords()->toArray(); // When GET or POST
    
    // When POST and form valid
    $checkedKeywords = $customer->getKeywords()->toArray(); // Thanks to setKeywords
    
    // Loop over all keywords
    foreach($originalKeywords as $keyword) {
        if(!in_array($keyword, $checkedKeywords)) { // Keyword has been unchecked
            $keyword->removeCustomer($customer);
            $manager->persist($keyword);
        }
    }
    

    Ugly, but works. I would have the code for removal moved to the Customer class, but it's not possible at all. If you'll find a better solution, let me know!

提交回复
热议问题