i use Symfony3.3 and want to make a form for the admin administration. The user should be have a group and the group sould have the roles for the backend access.
The form for groups (name and roles) i finished and the form for the admins (name, passwort...) is finish too.
The admin will be find and have the group. If i load the admin it have the arraycollection with the groups.
Here my classes
admin:
class Admin extends BaseUser
{
/**
* #ORM\Id()
* #ORM\Column(name="idAdmin", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string", length=255, options={"default":NULL})
*/
protected $style;
/**
* #ORM\ManyToMany(targetEntity="AdminBundle\Entity\AdminGroup")
* #ORM\JoinTable(
* name="admin_has_group",
* joinColumns={
* #ORM\JoinColumn(name="idAdmin", referencedColumnName="idAdmin")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="idGroup", referencedColumnName="idGroup")
* }
* )
*/
protected $groups;
/**
* #return string
*/
public function getStyle()
{
return $this->style;
}
/**
* #param string $style
*/
public function setStyle($style)
{
$this->style = $style;
return $this;
}
public function setGroups($groups)
{
$this->groups = $groups;
return $this;
}
public function getGroups()
{
return $this->groups;
}
}
groups
class AdminGroup extends BaseGroup
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="idGroup", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Group constructor.
*
* #param string $name
* #param array $roles
*/
public function __construct($name = '', $roles = array())
{
parent::__construct($name, $roles);
}
}
form generation
$admin = $this->getDoctrine()->getRepository(Admin::class)->find(1);
$admingroupList = $this->getDoctrine()->getRepository(AdminGroup::class)->findAll();
$form = $this->createFormBuilder($admin)
->add("username", TextType::class)
->add('plainPassword', PasswordType::class, $passwordSettings)
->add(
'groups', ChoiceType::class, [
'required' => false,
'multiple' => true,
'choices' => $admingroupList,
])
->add('save', SubmitType::class, ['label' => 'Save'])->getForm()->createView();
save form
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$em->persist($user);
$em->flush();
....
}
The first problem is that i have in the overview only display the id's in the select.
The second problem is that i submit the form (with selected groups) symfony crashed with the following message
Call to a member function contains() on array
I try to convert the grouplist to an normal array they will be crashed at the save the data
Expected argument of type "FOS\UserBundle\Model\GroupInterface", "integer" given
I dont know that i sould do to make a simple symfony form with the admin data and group selection... i dont find any example for a form with fosuserbundle...
Have someone an idea what i can to without manipulate the fosuserbundle entites or the symfonycode?
If you need more source, tell me with part :)
Editing 10.12.17
I try to convert the ChoosenArray into this format
$list = [
'user' => 0,
'admin' => 1
];
but than it will be broken at
$form->handleRequest($request);
with the error:
Expected argument of type "FOS\UserBundle\Model\GroupInterface", "integer" given
I do not think the data returned from
$admingroupList = $this->getDoctrine()->getRepository(AdminGroup::class)->findAll();
Will work as you want it too. choices wants something like this
'choices' => [
'Admin' => 'admin',
'User' => 'user'
]
Where the key of the array is the name the user sees, and the value of the array is the value used in the <option>.
You probably need to manipulate the $admingroupList array to mimic the demo array above. Or write your own query in the AdminGroup Repo to return a pre-formatted array for use with a Symfony form.
Related
So I have a Symfony 3 form with a multiple ChoiceType list with a few constant options.
Those choices are then saved in a database (manage with doctrine) in a 'json_array' field.
When I save the form, the selected choices are correctly saved in database as :
["my.Choice.1","my.Choice.2"]
But when I want to edit this form again, none of the selected options are "selected". They stay unchecked.
I have an other field (EntityType, multiple) which is correctly prepopulated with data from database.
MyEntityType :
$builder
->add('myChoices', ChoiceType::class, [
'choices' => [
'choice #1' => MyEntity::CHOICE_1,
'choice #2' => MyEntity::CHOICE_2,
],
'expanded' => true,
'multiple' => true,
])
Here is how my field is defined in the entity.
MyEntity :
const CHOICE_1 = 'my.Choice.1';
const CHOICE_2 = 'my.Choice.2';
/**
* #var array
*
* #ORM\Column(name="my_choices", type="json_array", nullable=true)
*/
private $myChoices;
/**
* #return array
*/
public function getMyChoices()
{
return array_unique($this->myChoices);
}
/**
* #param array $myChoices
*
* #return MyEntity
*/
public function setMyChoices(array $myChoices)
{
$this->myChoices = [];
foreach ($myChoices as $myChoice) {
$this->addMyChoice($myChoice);
}
return $this;
}
/**
* #param string $myChoice
*
* #return MyEntity
*/
public function addMyChoice($myChoice)
{
$myChoice = strtolower($myChoice);
if (!$this->hasMyChoice($myChoice)) {
$this->myChoices[] = $myChoice;
}
return $this;
}
/**
* #param string $myChoice
*
* #return bool
*/
public function hasMyChoice($myChoice)
{
return in_array(strtolower($myChoice), $this->myChoices, true);
}
And my form is called like this :
$myEntity = ..... // loaded from database
$form = $this->createForm(MyEntityType::class, $myEntity);
So is there anybody who can tell my what I am missing ??
Thanks again.
My bad.
All is working like it should.
My mistake is in my choices names, which are containing upper lettermy.Choice.1, ...
And in my getter and setter, I force strings to be lowercase strtolower($myChoice)
I am working on a REST webservice (FOSRestBundle 2.0.0, Symfony 3.1.3) and testing the creation of entities. The creation itself works fine with a correct set of data but if I try to omit a required value the controller still says the form is valid.
The entity itself:
class Customer implements ExportableEntity
{
use Traits\FilterableTrait;
use Traits\UuidTrait;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Serializer\Exclude()
* #Serializer\ReadOnly()
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="customer_index", type="integer", unique=true)
*/
private $customerIndex;
/**
* #var string
*
* #ORM\Column(name="customerName", type="string", length=255)
*/
private $customerName;
// [... accessors ...]
The controller:
/**
* #ApiDoc(
* resource=false,
* description="Create a new customer",
* section="Customers",
* statusCode={
* 200="Action successful",
* 403="Authorization required but incorrect / missing information or unsufficient rights",
* 500="Returned if action failed for unknown reasons"
* }
* )
*
* #param Customer $customer
* #return RestResponse
*/
public function postCustomerAction(Request $request) {
$manager = $this->container->get('corebundle.managers.customer');
// Internal usage only, no link with the WS issue
$manager->setChecksEnabled(false);
$customer = new Customer();
$form = $this->get('form.factory')->createNamed(null, CustomerType::class, $customer, ['csrf_protection' => false]);
$form->handleRequest($request);
//if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
print('VALID');
exit();
$manager->create($customer);
// Return 201 + Location
}
return \FOS\RestBundle\View\View::create($form, 400);
}
And the FormType:
class CustomerType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customerName', Type\TextType::class, array('label' => 'Customer name'))
->add('customerIndex', Type\IntegerType::class, array('label' => 'Customer Index'))
->add('comment', Type\TextareaType::class, array('label' => 'Comments',
'required' => false, ))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('data_class' => 'NetDev\CoreBundle\Entity\Customer'));
}
/**
* #return string
*/
public function getBlockPrefix()
{
return 'netdev_corebundle_customer';
}
}
If I try to create a new Customer and omit the "customerIndex" field, I belieev that I should get an invalid form error but I ain't getting it.
I tried to change the "handleRequest" with
$form->submit([])
and
$form->submit($request->request->get($form->getName()))
to no avail. If I add a "NotBlank()" constraint to the entity itself it works but I am under the impression that this would be a workaround, not a fix. Did I miss something ?
$form->isValid()
This line will verify that your submitted data respected all the constraints written in your entity files (with Assert annotation, for example #Assert\NotBlank()).
So, you did not miss something.
I am trying to create an admin page for a sports club website so that each month, the admin user can generate new invoices (Invoice entity) for all active members (Member entity) of the club.
I'm trying to create the form so that I have one row for each member pre-populated with their standard monthly fee and the current date (both of which can be changed for individual member entries if needed):
I have tried just about everything I can think of to get this working in a form but so far I've had no success. Below is the code as it currently stands but this gives me an individual form for just the last member....any advice on what I'm doing wrong would be very welcome - thanks in advance!
Entities (Member and Invoice):
class Member
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Invoice", mappedBy="member_id")
*/
protected $invoice_ids;
/**
* #ORM\COLUMN(type="string", length=100)
* #Assert\NotBlank()
*/
protected $firstname;
/**
* #ORM\COLUMN(type="string", length=100)
*/
protected $familyname;
/**
* #ORM\COLUMN(type="boolean")
*/
protected $active = true;
/**
* #ORM\COLUMN(type="decimal", precision=7, scale=2)
* #Assert\Regex(
* pattern="/^\s*-?[1-9]\d*(\.\d{1,2})?\s*$/",
* match=true,
* message="Error")
*/
protected $defaultinvoiceamount;
public function __construct()
{
$this->invoice_ids = new ArrayCollection();
}
public function FullName()
{
return $this->firstname . ' ' . $this->familyname;
}
}
class Invoice
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Member", inversedBy="invoice_ids")
* #ORM\JoinColumn(name="member_id", referencedColumnName="id")
*/
protected $member_id;
/**
* #ORM\COLUMN(type="decimal", precision=7, scale=2)
* #Assert\Regex(
* pattern="/^\s*-?[1-9]\d*(\.\d{1,2})?\s*$/",
* match=true,
* message="Error")
*/
protected $amount;
/**
* #ORM\COLUMN(type="datetime")
* #Assert\DateTime()
*/
protected $invoicedate;
/**
* #ORM\COLUMN(type="datetime")
* #Assert\DateTime()
*/
protected $createdate;
/**
* #ORM\COLUMN(type="text", nullable=True)
*/
protected $comments;
}
FormType:
class InvoiceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('member_id', EntityType::class, array(
'class' => 'AppBundle:Member',
'choice_label' => 'FullName',
'attr' => array(
'readonly' => 'readonly'
)
)
)
->add('invoicedate', DateType::class, array(
'widget' => 'single_text',
'data' => new \DateTime('now'),
'format' => 'dd/MMM/yyyy',
'label' => 'Date of invoice',
))
->add('createdate', DateType::class, array(
'widget' => 'single_text',
'data' => new \DateTime('now'),
'format' => 'dd/MMM/yyyy',
'label' => 'Date invoice recorded in database',
'disabled' => 'true'
))
->add('amount', MoneyType::class, array(
'label' => 'Amount',
))
->add('comments')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Invoice',
));
}
public function getName()
{
return 'invoice';
}
}
Repository:
class MemberRepository extends EntityRepository
{
/**
* #return \Doctrine\ORM\QueryBuilder
*/
public function findAllActiveMembers()
{
return $this->getEntityManager()
->createQuery
(
'SELECT m
FROM AppBundle:Member m
WHERE m.active= :active
ORDER BY m.surname, m.firstname'
)
->setParameter('active' ,true)
->getResult();
}
}
The Controller:
/**
* #Route("/batchinvoices" ,name="batchinvoices")
*/
public function newBatchInvoicesAction(Request $request)
{
$members = $this->getDoctrine()->getRepository('AppBundle:Members')->findAllActiveMembers();
foreach ($members as $member) {
$invoices = new Invoice();
$invoices->setMemberId($member);
$form=$this->createForm(InvoiceType::class, $invoices);
}
$form->handleRequest($request);
if ($form->isSubmitted() && ($form->isValid())) {
$em = $this->getDoctrine()->getManager();
$em->persist($invoices);
$em->flush();
return $this->redirectToRoute('invoices_added');
}
return $this->render('admin/batchinvoices.html.twig', array(
'form' => $form->createView(),
));
}
The reason you are only getting the last entry in the form is that you are continually overwriting your own variables. Take a look at this code specifically:
foreach ($members as $member) {
$invoices = new Invoice();
$invoices->setMemberId($member);
$form=$this->createForm(InvoiceType::class, $invoices);
}
You are continually overwriting the $form value every single time through your loop, so not only can you only handle the last entry, it is the only one that will show up.
I have come across this situation before but it was usually just to delete a single record. If you are fine with displaying all the forms at once but only updating one member at a time, you can generate a single form for each member (like you are doing now), and then add it to a forms array that you pass to your template. So your code would now look like:
$forms = array();
foreach ($members as $member) {
$invoices = new Invoice();
$invoices->setMemberId($member);
$forms[] = $this->createForm(InvoiceType::class, $invoices)->createView();
}
//...
return $this->render('admin/batchinvoices.html.twig', array(
'forms' => $forms,
));
Notice that I am calling ->createView() which is what is acceptable for the Twig template. Then your twig template is going to look something like this:
{% for form in forms %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endfor %}
Obviously you could change the templating to your liking, or even put the individual form definition in its own template and include that like so:
{% for form in forms %}
{{ include('#AppBundle/YourController/batchInvoiceForm.html.twig', {'form': form}) }}
{% endfor %}
Keep in mind that doing this would only allow you to update one record at a time, so if you didn't want to refresh the page you would want to create an AJAX request that posts to a separate controller action and handles the modification of results and sending a success/fail response back. The benefit of doing it this way is that you're not posting heaps of data for all members when you're only modifying a small amount.
It could be simpler to just display all member information and then have an Edit button that takes you to a separate form just for updating that member that would then redirect back to your list after submitting - unless I need on-the-fly or bulk updates I always go for this route.
If you want to update all records at once you will have to use the CollectionType as Onema said.
I am using symfony2 with doctrine 2.
I have a many to many relationship between two entities :
/**
* #ORM\ManyToMany(targetEntity="\AppBundle\Entity\Social\PostCategory", inversedBy="posts")
* #ORM\JoinTable(
* name="post_postcategory",
* joinColumns={#ORM\JoinColumn(name="postId", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="postCategoryId", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
private $postCategories;
Now I want to let the user only select one category. For this I use the option 'multiple' => false in my form.
My form:
->add('postCategories', 'entity', array(
'label'=> 'Catégorie',
'required' => true,
'empty_data' => false,
'empty_value' => 'Sélectionnez une catégorie',
'class' => 'AppBundle\Entity\Social\PostCategory',
'multiple' => false,
'by_reference' => false,
'query_builder' => $queryBuilder,
'position' => array('before' => 'name'),
'attr' => array(
'data-toggle'=>"tooltip",
'data-placement'=>"top",
'title'=>"Choisissez la catégorie dans laquelle publier le feedback",
)))
This first gave me errors when saving and I had to change the setter as following :
/**
* #param \AppBundle\Entity\Social\PostCategory $postCategories
*
* #return Post
*/
public function setPostCategories($postCategories)
{
if (is_array($postCategories) || $postCategories instanceof Collection)
{
/** #var PostCategory $postCategory */
foreach ($postCategories as $postCategory)
{
$this->addPostCategory($postCategory);
}
}
else
{
$this->addPostCategory($postCategories);
}
return $this;
}
/**
* Add postCategory
*
* #param \AppBundle\Entity\Social\PostCategory $postCategory
*
* #return Post
*/
public function addPostCategory(\AppBundle\Entity\Social\PostCategory $postCategory)
{
$postCategory->addPost($this);
$this->postCategories[] = $postCategory;
return $this;
}
/**
* Remove postCategory
*
* #param \AppBundle\Entity\Social\PostCategory $postCategory
*/
public function removePostCategory(\AppBundle\Entity\Social\PostCategory $postCategory)
{
$this->postCategories->removeElement($postCategory);
}
/**
* Get postCategories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPostCategories()
{
return $this->postCategories;
}
/**
* Constructor
* #param null $user
*/
public function __construct($user = null)
{
$this->postCategories = new \Doctrine\Common\Collections\ArrayCollection();
}
Now, when editing a post, I also have an issue because it uses a getter which ouputs a collection, not a single entity, and my category field is not filled correctly.
/**
* Get postCategories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPostCategories()
{
return $this->postCategories;
}
It's working if I set 'multiple' => true but I don't want this, I want the user to only select one category and I don't want to only constraint this with asserts.
Of course there are cases when I want to let the user select many fields so I want to keep the manyToMany relationship.
What can I do ?
If you want to set the multiple option to false when adding to a ManyToMany collection, you can use a "fake" property on the entity by creating a couple of new getters and setters, and updating your form-building code.
(Interestingly, I saw this problem on my project only after upgrading to Symfony 2.7, which is what forced me to devise this solution.)
Here's an example using your entities. The example assumes you want validation (as that's slightly complicated, so makes this answer hopefully more useful to others!)
Add the following to your Post class:
public function setSingleCategory(PostCategory $category = null)
{
// When binding invalid data, this may be null
// But it'll be caught later by the constraint set up in the form builder
// So that's okay!
if (!$category) {
return;
}
$this->postCategories->add($category);
}
// Which one should it use for pre-filling the form's default data?
// That's defined by this getter. I think you probably just want the first?
public function getSingleCategory()
{
return $this->postCategories->first();
}
And now change this line in your form:
->add('postCategories', 'entity', array(
to be
->add('singleCategory', 'entity', array(
'constraints' => [
new NotNull(),
],
i.e. we've changed the field it references, and also added some inline validation - you can't set up validation via annotations as there is no property called singleCategory on your class, only some methods using that phrase.
You can setup you form type to not to use PostCategory by reference (set by_reference option to false)
This will force symfony forms to use addPostCategory and removePostCategory instead of setPostCategories.
UPD
1) You are mixing working with plain array and ArrayCollection. Choose one strategy. Getter will always output an ArrayCollection, because it should do so. If you want to force it to be plain array add ->toArray() method to getter
2) Also I understand that choice with multiple=false return an entity, while multiple=true return array independend of mapped relation (*toMany, or *toOne). So just try to remove setter from class and use only adder and remover if you want similar behavior on different cases.
/** #var ArrayCollection|PostCategory[] */
private $postCategories;
public function __construct()
{
$this->postCategories = new ArrayCollection();
}
public function addPostCategory(PostCategory $postCategory)
{
if (!$this->postCategories->contains($postCategory) {
$postCategory->addPost($this);
$this->postCategories->add($postCategory);
}
}
public function removePostCategory(PostCategory $postCategory)
{
if ($this->postCategories->contains($postCategory) {
$postCategory->removePost($this);
$this->postCategories->add($postCategory);
}
}
/**
* #return ArrayCollection|PostCategory[]
*/
public function getPostCategories()
{
return $this->postCategories;
}
In my case, the reason was that Doctrine does not have relation One-To-Many, Unidirectional with Join Table. In Documentations example is show haw we can do this caind of relation by ManyToMany (adding flag unique=true on second column).
This way is ok but Form component mixes himself.
Solution is to change geters and seters in entity class... even those generated automatically.
Here is my case (I hope someone will need it). Assumption: classic One-To-Many relation, Unidirectional with Join Table
Entity class:
/**
* #ORM\ManyToMany(targetEntity="B2B\AdminBundle\Entity\DictionaryValues")
* #ORM\JoinTable(
* name="users_responsibility",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="responsibility_id", referencedColumnName="id", unique=true, onDelete="CASCADE")}
* )
*/
private $responsibility;
/**
* Constructor
*/
public function __construct()
{
$this->responsibility = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add responsibility
*
* #param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
*
* #return User
*/
public function setResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility = null)
{
if(count($this->responsibility) > 0){
foreach($this->responsibility as $item){
$this->removeResponsibility($item);
}
}
$this->responsibility[] = $responsibility;
return $this;
}
/**
* Remove responsibility
*
* #param \B2B\AdminBundle\Entity\DictionaryValues $responsibility
*/
public function removeResponsibility(\B2B\AdminBundle\Entity\DictionaryValues $responsibility)
{
$this->responsibility->removeElement($responsibility);
}
/**
* Get responsibility
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getResponsibility()
{
return $this->responsibility->first();
}
Form:
->add('responsibility', EntityType::class,
array(
'required' => false,
'label' => 'Obszar odpowiedzialności:',
'class' => DictionaryValues::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('n')
->where('n.parent = 2')
->orderBy('n.id', 'ASC');
},
'choice_label' => 'value',
'placeholder' => 'Wybierz',
'multiple' => false,
'constraints' => array(
new NotBlank()
)
)
)
I know its a pretty old question, but the problem is still valid today.
Using a simple inline data transformer did the trick for me.
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('profileTypes', EntityType::class, [
'multiple' => false,
'expanded' => true,
'class' => ProfileType::class,
]);
// data transformer so profileTypes does work with multiple => false
$builder->get('profileTypes')
->addModelTransformer(new CallbackTransformer(
// return first item from collection
fn ($data) => $data instanceof Collection && $data->count() ? $data->first() : $data,
// convert single ProfileType into collection
fn ($data) => $data && $data instanceof ProfileType ? new ArrayCollection([$data]) : $data
));
}
PS: Array functions are available in PHP 7.4 and above.
I'm struggling with a symfony2 form. Basically i would like to manage User's preference to receive (or not) an email for each type of action an User could do.
Here my schema :
User (extending FOSUB)
EmailUserPreference
class EmailUserPreference {
public function __construct(User $user, \Adibox\Bundle\ActionBundle\Entity\ActionType $actionType) {
$this->user = $user;
$this->actionType = $actionType;
$this->activated = true;
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Adibox\Bundle\UserBundle\Entity\User", inversedBy="id")
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Adibox\Bundle\ActionBundle\Entity\ActionType", inversedBy="id")
*/
private $actionType;
/**
* #ORM\Column activated(type="boolean")
*/
private $activated;
/*getters / setters ... */
}
ActionType
class ActionType
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $value
*
* #ORM\Column(name="value", type="string", length=255)
*/
private $value;
/* and some others */
}
Here, i build my form EmailUserPreferenceType :
class EmailUserPreferenceType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('emailPreference', 'entity', array(
'class' => 'AdiboxActionBundle:ActionType',
'property' => 'value',
'expanded' => true,
'multiple' => true,
'query_builder' => function(\Adibox\Bundle\ActionBundle\Entity\ActionTypeRepository $er) {
return $er->getAllActionsWithPreferences();
}
));
}
public function getName() {
return 'emailUserPreference';
}
public function getDefaultOptions(array $options) {
return array('data_class' => 'Adibox\Bundle\UserBundle\Entity\EmailUserPreference');
}
}
And finally the ActionTypeRepository with the function called in the FormType :
class ActionTypeRepository extends EntityRepository {
public function getAllActionsWithPreferences() {
$arrayActionWithPreferences = array(
'share',
'refuse',
'validate',
'validatePayment',
'createPayment',
'estimateChangeState',
'comment',
'createRepetition',
'display',
'DAFLate',
);
$qb = $this->createQueryBuilder('a');
$qb->where($qb->expr()->in('a.value', $arrayActionWithPreferences));
return $qb;
}
}
At this point, I thought it was OK : i got a good rendering, with the right form. But in fact, each checkbox has the same form name than the other. In other words each time the form is submitted, it only send in post a $builderemailUserPreference[emailUserPreference][] data. Obviously, it does not work as i expected.
I show these posts
http://sf.khepin.com/2011/08/basic-usage-of-the-symfony2-collectiontype-form-field/
Here he's using a widget Collection. I'm not sure i should use it or entity (like i did). But what i can read from http://symfony.com/fr/doc/current/reference/forms/types/collection.html, it seems more like an embedding form than an entity.
And finally i saw this : symfony2 many-to-many form checkbox
This one is using (indeed) Collection and many-to-many relations. I read somewhere (can't find the link anymore) that i can't use it since i need to add some attributes to the relation (in this case bool activated). I'm pretty sure the solution is near the link above, but can't find the good way to reach it.
Thank you in advance.
Any advice on what i'm doing wrong or if i should use Collections instead of Entity would be appreciated.