Summary
I want to create a form with multiple fields, each associated with a many-to-many relation, to be able to assign dictionary values from different dictionaries to an entity.
Description
I'm working on a polling system. It goes like this (slightly simplified):
There are multiple Respondents (users) in the system, and multiple Dictionaries.
Each Dictionary describes one personal trait, for example gender, profession or age range. Each Dictionary has then multiple DictItems, representing different values that Dictionary can take (for example, for a gender Dictionary there would be two DictItems: male and female).
There are multiple Respondents; each of them can be described by multiple traits (Dictionaries), and those traits can be different for each of them - e.g. one Respondent can have gender, age range and profession traits, while another can have only gender. The traits are specified by administrator; their values are specified by user (the admin may decide that the Respondent should specify their gender and profession, and thus the Dictionaries gender and profession should be available to the Respondent in his user profile).
Finally, Respondents and DictItems are tied together by a many-to-many relationship (a single Respondent can have many trait values, one for each trait; a single trait value can be shared among many Respondents - e.g. there can be many Respondents whose gender is male).
Problem
I want to create a form that would list all the traits assigned to a Respondent as a collection of selection boxes / radio buttons. That is, for each Dictionary assigned to that Respondent show a list of all available DictItems to allow the user to fill in his trait value, and then upon submit add the selected DictItem to a Collection in the Respondent object, overwriting the old relation if present.
Code
The Respondent - our user:
/**
* @ORM\Table("respondent")
* @ORM\Entity
*/
class Respondent {
// ID, email etc. go here...
/**
* collection of trait values for this Respondent
* @var Collection
* @ORM\ManyToMany(targetEntity="Mp\RespondentBundle\Entity\DictItem", mappedBy="respondents")
*/
private $dictItems;
/**
* collection of Dictionaries (traits) of this Respondent
* these are not set up by Doctrine, but instead lazily loaded
* from another source - but they are always a valid collection
* of Dictionaries attached to this Respondent
* note: the Respondent can have a Dictionary available,
* but no appropriate DictItem specified (e.g. there is a 'profession'
* Dictionary available for this Respondent with various professions,
* but the Respondent didn't specify any profession - though he can
* specify it at a later time)
* @var Collection
*/
private $dictionaries;
// additional code goes here
}
The Dictionary class, describing a trait:
/**
* @ORM\Table("dict")
* @ORM\Entity
*/
class Dict {
/**
* Collection of values this Dictionary can take
* (eg. for 'gender' dictionary the values are 'male' and 'female'
* @var Collection
* @ORM\OneToMany(targetEntity="DictItem", mappedBy="dict", cascade={"persist"})
*/
protected $dictItems;
// additional code
}
And finally a DictItem class, describing a single value of a trait:
/*
* @ORM\Table("dict_item")
* @ORM\Entity
*/
class DictItem {
/**
* The Dictionary this value belongs to
* @ORM\ManyToOne(targetEntity="Dict", inversedBy="dictItems")
* @ORM\JoinColumn(name="dict_id", referencedColumnName="id", nullable=FALSE)
*/
private $dict;
/**
* @var Collection
* @ORM\ManyToMany(targetEntity="Respondent", inversedBy="dictItems")
* @ORM\JoinTable(name="respondent_dict_items")
**/
private $respondents;
// additional code
}
And finally my form code. The point is, $dict->getName() cannot be used as input field name, because Dictionary names are assigned dynamically, so I can't just implement all the methods for retrieving all possible Dictionaries from Respondent object; and even if I override __get() magic method, Symfony has no idea what's happening and cannot bind proper DictItems to input fields and vice versa.
class EditRespondentType extends AbstractType {
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
/* @var $respondent Respondent */
$respondent = $options['data'];
/* @var $dict Dict */
foreach ($respondent->getDictionaries() as $dict) {
$builder->add($dict->getName(), 'entity', array(
'required' => false,
'class' => 'MpRespondentBundle:DictItem',
'property' => 'value',
'choices' => $dict->getDictItems(),
'expanded' => false,
'multiple' => false,
'placeholder' => '--'
))
->add('submit', 'submit');
}
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Mp\RespondentBundle\Entity\Respondent'
));
}
}
Aucun commentaire:
Enregistrer un commentaire