Customer
is the inverse side of \"keywords/customers\" relationship with Keyword
:
/**
* @ORM
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!