Let's say I have an Person entity and a ResearchArea entity. There is a ManyToMany Doctrine relationship between them with simple join table, a Person can have multiple ResearchAreas and a ResearchArea can have multiple Persons.
In my database, there are thousands of Persons, but only around 10 ResearchAreas.
On the Person edit form, I want to present a checkbox list for each ResearchArea. This is easy in a form builder:
->add('researchAreas', 'entity', array(
'label' => false,
'class' => 'AcmeDemoBundle:ResearchArea',
'property' => 'title',
'required' => false,
'multiple' => true,
'expanded' => true,
))
When submitting the form and binding the request data, this works well and the ManyToMany is handled nicely.
However, when editing a ResearchArea entity, I want to also provide a way to manage the Persons associated with that area. However, I can't use the same entity form type as I did above because there are so many Person entities.
Instead what I want is a collection form type, where the user can add/remove Person entities. I can do this by rendering text fields for each "row" and accept an ID of the person to add.
To support an approach like that, I need to change the relationship from a ManyToMany to a OneToMany -> ManyToOne and make the join table its own entity. But in doing that, then I can no longer use the nice checkboxes on the Person form which would only work with a direct ManyToMany.
Am I just making this all too complicated? Is there a solution to this?
For a similar use case I simply used the entity field type, rendered as a multiselect (multiple => true, expanded => false) and improved with a jquery plugins. This worked quite well, but I didn't have thousands of entities.
->add('persons', 'entity', array(
'label' => false,
'class' => 'AcmeDemoBundle:Person',
'required' => false,
'multiple' => true,
'expanded' => false,
))
I used a jquery plugin (like Chosen) to improve the multiselect and make it more user friendly. With chosen you can simply use:
$(".chosen-select").chosen();
With a bit of custom css you might be able to style it to fit your needs.
I too, would advocate the entity approach, but I used select2 (http://ivaynberg.github.io/select2/) instead.
In Many-to-Many Ajax Forms (Symfony2 Forms) I have some fairly detailed implementation examples.
Select2 can support ajax loading and searching, so the 3000 researchers is not a big deal.
Related
I have 2 related entities, e.g. Book and Publisher (Book has one publisher, publisher has many books).
When editing\adding a Book I want to present a select of the Publishers.
Publishers has a property 'isDefault' on of the Publisher records will be marked as isDefault TRUE.
How do I make use of this in my add/edit form to pre-select the default Publisher?
I would recommend injecting publisherRepository as a service into your form.
And then declare a field something like this:
$builder->add('publishers', 'choice', array(
'choices' => $this->publisherRepository->findAll(),
'data' => $this->publisherRepository->findOneBy(['isDefault' => true]),
));
This is an existing schema I'm working with and I'm trying to not make any changes for the time being. I have an entity property that represents a university semester, like "fall12", "spring11", etc.
When adding or editing this entity with a form, I want to split that property into two form fields: "Season" (fall or spring") and "Year" (2011, 2012, etc):
...
->add('formSemesterSeason', 'choice', array(
'label' => 'Season',
'mapped' => false,
'choices' => array('fall' => 'Fall', 'spring' => 'Spring'),
))
->add('formSemesterYear', 'choice', array(
'label' => 'Year',
'mapped' => false,
'choices' => $this->courseManager->getYearChoices(),
))
...
When submitting the form, the values need to be combined and saved to the "semester" property on the entity as a string
When viewing the edit form, the existing "semester" value needs to be split between the two.
I don't think data transformers help here, since they apply to transforming just one form item.
Right now I'm using a form event POST_SET_DATA to fill out the two form fields when editing an existing entity:
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) use ($course_manager) {
$course = $event->getData();
$form = $event->getForm();
$semester = $course->getSemester();
$parsed_semester = $course_manager->parseSemesterMachineName($semester);
$form->get('formSemesterSeason')->setData($parsed_semester->season);
$form->get('formSemesterYear')->setData($parsed_semester->yearFull);
});
This works well, but how do I then combine the values back after the form has been submitted? I can do it easily in the controller, but I think I should be able to use form events, and have the data manipulation take place before form validation.
You can combine them back in a POST_SUBMIT listener.
The best way (reuseable) would be to create your own custom form type with a data transformer to split/combine the fields internally.
There are "recipes" in the cookbook but the best way that I found to create it was to rip apart the DateTime field type and associated transformers (DataTransformerChain, DateTimeToArrayTransformer & ArrayToPartsTransformer) for parts and build my own from that.
I have a problem, I want to create a simple search form with a filter assembly. These filters are attributes that belong to attribute groups.
group 1
[] Attribute 1
[] Attribute 2
[] Attribute 3
group 2
[] Attribute 1
[] Attribute 2
[] Attribute 3
But the problem is that I can not do (graphic aspect)
$builder->add('attribut', 'entity', array(
'class' => 'RestoFrontBundle:Attribut',
'group_by' => 'groupeAttribut.id',
'expanded' => true,
'multiple' => true,
'query_builder' => function(AttributRepository $er) {
return $er->createQueryBuilder('a')
->join("a.groupeAttribut", 'g')
->where("a.statut = 1");
}
))
->getForm();
Also I can not manage the game if the checkbox has been checked.
You note that the graphic aspect is the hard part. That is due to the way checkboxes are used in HTML. Unlike select inputs there is no notion of an optgroup. The closest analog for checkboxes would be a fieldset with a legend.
You may want explore using a Choice Field type rather than an entity type. Provide your choices via some provider function within which you format the options array(s) whether or not you retrieved them from a database. ( For the record, that's exactly how I populate the select at http://stayattache.com which has multiple places to retrieve options from. ) You may even want to explore creating your own form field type and template to format the output. Checkout the documentation on creating your own field type.
I hope that helps a bit. There are probably plenty of other ways to approach this as well.
I googled a lot trying to find an answer for my problem, but no luck.
I have two tables in two different db db1.pages and db2.layouts I have defined the two entity managers and I can successfully manage the two as unrelated entities.
My problem is on table db1.pages, I have a layout_id column which contains a reference to the db2.layouts.id column, I didn't created an association between the two because, from my understanding, is not supported by doctrine. I successfully managed to display a selection list of possible layouts on the PageType using the following code:
$builder
->add('name', null, array('label' => 'Name'))
->add('label', null, array('label' => 'Title'))
->add('home', null, array('label' => 'Homepage'))
->add('layoutId', 'entity', array(
'class' => 'USGMCMSBundle:Layouts',
'property' => 'label',
'label' => 'Layout',
))
but it doesn't select the correct layout matched by the value stored into layoutId. Is there a way to select the current selected item and keep it in sync when the user choose a different one?
Maybe using an EventSubscriber? but on which stage? Or is there a better solution?
Hope I made myself clear (sorry for my english)
This is such a trivial problem that I can't believe I couldn't find an answer.
Symfony 2, doctrine 2.1. I've got two entities and one intermediate entity (join table). User, Pref, and UsersPrefs. Pref table is dictionary table, so that I could change pref name in one place only. Ok, let's see the picture:
infographic http://dl.dropbox.com/u/22495762/infographic.png
As You can see, I want to have a checkbox group, with all the possible choices (prefs) and preferred choices checked. So, if there are 3 prefs, and only 2 selected by the user, there should be 3 checkboxes, 2 selected.
It's simple, if done plain PHP - query database twice to get list of all prefs and user prefs, render checkboxes depending on values, add some actions to handle form submit, done.
But for the life of God I can't get this to work using symfony & doctrine. I was able to get to the point where I can update relationships in doctrine and further in database, but I'm using raw query values for that:
$data = $request->request->get('some_form');
and this supposedly isn't the way it should be done?
Morevoer, I'm completely stuck as to how should I display checkbox list. I either get list of all options, none checked, or only user options, all checked. Or 'left joined' result set with checkboxes for all cases, useless anyway.
I've come to the point where I tried to overload twig checkbox template, but I couldn't pass variables to the form template, and that was the last straw...
EDIT:
This way I'm getting group of checkboxes, not connected to user choices:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\Pref',
'expanded' => 'true',
'multiple' => 'true',
'property' => 'name'
))
And this way I'm getting only user choices, all checked:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\UserPrefs',
'multiple' => 'false',
'expanded' => 'false',
'property' => 'pref.name',
'query_builder' => function(EntityRepository $er) use ($id) {
return $er->createQueryBuilder('u')
->where("u.user = :id")
->setParameter('id', $id)
;
},
))
And I tried left joins and other options, but at best I could get list of all possible options for all possible users, checked accordingly.
EDIT (POSSIBLE SOLUTION):
I'm displaying checkbox group:
->add('pref_ids', 'choice', array(
'choices' => array(
'1' => 'pref one',
'2' => 'pref two',
'3' => 'pref three',
),
'expanded' => 'true',
'multiple' => 'true'
))
I've added $pref_ids array in User entity. Now I just need to set values in array according to preferences chosen by user:
public function setPrefIds()
{
$prefs = $this->getPrefs();
$this->pref_ids = array();
foreach($prefs as $pref){
array_push($this->pref_ids, $pref->getPref()->getId());
}
return $this;
}
This way I get appropriate checkboxes checked.
Writing values to database is reversal of the process. I'm getting input values from request:
$data = $request->request->get('edit_form');
var_dump($data['pref_ids']);
Removing all user prefs:
foreach ($userPrefs as $pref){
$em->remove($pref);
}
And setting actual associations in doctrine from ids:
$entity->setPrefsById($em, $data['pref_ids']);
Here I'm passing entity manager to entity itself, but I need to refactor it, because it looks kinda messy this way.
Then $em->flush(); and that's it.
That's the best I could come up with. Probably it's overcomplicated and should be done entirely different way. Unfortunately couldn't figure out this "other way".
You need the choice field type: http://symfony.com/doc/current/reference/forms/types/choice.html
In your builder, it will be something like
$builder->add('prefs', 'choice', array('multiple' => true, 'expanded' => true, 'choices' => fetch the available prefs from the database here);
Edit: Sorry I'm mistaken, you need the "Entity" type, which fetches automatically the choices from the database: http://symfony.com/doc/current/reference/forms/types/entity.html
You must still put multiple => true expanded => true to get checkboxes.