I have an Entity called News which contains a ManyToMany relation with the User Entity. This means that an article can be written by multiple authors.
/**
* #ORM\ManyToMany(targetEntity="GW2\UserBundle\Entity\User")
*/
private $authors;
The problem is that I would like to add authors dynamically in the form I use to add News, but I don't know how to store the data, and keep the relation between my two Entities. The number of authors is not fixed, and not limited.
I did some research about the collection type and added it to my NewsType, but I don't really know how to use it.
Edit: Here is my current NewsType (without the authors collection):
<?php
namespace GW2\SiteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class NewsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text', array('error_bubbling' => true))
->add('introduction', 'textarea', array('attr' => array('class' => 'input-block-level redactor_content'), 'error_bubbling' => true))
->add('content', 'textarea', array('attr' => array('class' => 'input-block-level redactor_content'), 'error_bubbling' => true))
->add('publication', 'checkbox', array('required' => false, 'error_bubbling' => true))
->add('categories', 'entity', array(
'class' => 'GW2SiteBundle:Category',
'property' => 'name',
'required' => false,
'multiple' => true
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'GW2\SiteBundle\Entity\News'
));
}
public function getName()
{
return 'gw2_sitebundle_newstype';
}
}
I finally fixed my problem by adding a collection with the FOSUB type, while specifying the allow_add and allow_delete options.
->add('authors', 'collection', array(
'type'=> 'fos_user_username',
'allow_add' => true,
'allow_delete' => true
))
Then I added the adding-removing solution with JavaScript to dynamically add fields in the form (thanks to Thomas Potaire).
Related
I would like to use my UserType form class to register a user and to edit the user profile. As I want to the admins to register a user while setting up their roles, I don't want the user to modify that options. So I would like to use 2 different forms but for the same type User.
Can I create 2 different buildForm() functions in one FormType class ?
Or do I need to create another type ?
Here is my UserType class (main purpose was to register a user) :
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', TextType::class)
->add('lastname', TextType::class)
->add('birthdate', BirthdayType::class, array(
'placeholder' => '-'))
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'password'),
'second_options' => array('label' => 'repeat-password'),
))
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true, // render check-boxes
'choices' => [
'Administrateur' => 'ROLE_ADMIN',
'Direction' => 'ROLE_MANAGER',
'Comptabilite' => 'ROLE_ACCOUNTING',
'Commercial' => 'ROLE_MARKETING',
'Docteur' => 'ROLE_DOCTOR',
'Client' => 'ROLE_CLIENT',
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
}
You can use a variable to dynamically add the roles to the formType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->is_admin = $options['is_admin'];
// [...]
if ($this->is_admin)
$builder
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => [
'Administrateur' => 'ROLE_ADMIN',
'Direction' => 'ROLE_MANAGER',
'Comptabilite' => 'ROLE_ACCOUNTING',
'Commercial' => 'ROLE_MARKETING',
'Docteur' => 'ROLE_DOCTOR',
'Client' => 'ROLE_CLIENT',
],
])
// [...]
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// [...]
'is_admin' => false,
]);
}
Then pass the variable like
$form = $this->createForm(UserType::class, $user, array(
'is_admin' => $this->isGranted('ROLE_ADMIN'),
);
you can easily handle this using array $optionsin controller set choices of roles and add them to $optionand in userType do something like this:
->add('roles', ChoiceType::class, [
'multiple' => true,
'expanded' => true, // render check-boxes
'choices' => $options['choices'],
])
I have an embedded form (oneTo Many relationship).
1 - Entity request
2 - Entity products of the request
I am looking for adapt the form.
I want form can dynamic modify the value when i check the value of the price of this object.
The best is to check the value in the form and compare but I don't know which method can works for my issue.
I would like to do this check in the form "ProductRequestType".
Exemple of my form ProductRequestType :
<?php
namespace \testBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
class testRequestProductType extends AbstractType {
/** #var \Doctrine\ORM\EntityManager */
private $em;
/**
* Constructor
*
* #param Doctrine $doctrine
*/
public function __construct($em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$disabled_exchange = $options['attr']['disabled_exchange'];
$customer_id = $options['attr']['customer_id'];
$builder->add('id', 'hidden');
$builder->add('part_number');
$builder->add('_number');
$builder->add('source_quantity');
$builder->add('quantity');
$builder->add('serial');
$builder->add('buy_date', 'date', array('required' => false,'widget' => 'single_text', 'fotestt' =>'dd/MM/yy'));
$builder->add('return_date', 'date', array('required' => false,'widget' => 'single_text', 'fotestt' =>'dd/MM/yy'));
$builder->add('is_new', null, array('required' => false));
$builder->add('test_comment');
$builder->add('available_stock', 'integer', array('mapped' => false));
$builder->add('buying_price', 'integer', array('mapped' => false));
$builder->add('test_reason', 'entity',
array(
'class' => 'testBundle:testReason',
'property' => 'name',
'expanded' => false,
'multiple' => false
)
);
//I would like to get the price and if the price < 100 the variable $disabled_exchange = true
if($disabled_exchange){
$builder->add('test_action', 'entity',
array(
'class' => 'testBundle:testAction',
'property' => 'label',
'expanded' => false,
'multiple' => false,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.name != :action')
->setParameter('action', 'exchange');
}
)
);
}else{
$builder->add('test_action', 'entity',
array(
'class' => 'testBundle:testAction',
'property' => 'label',
'expanded' => false,
'multiple' => false
)
);
}
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => '\testBundle\Entity\testRequestProduct',
'disabled_exchange' => false
));
}
public function getName()
{
return 'test_request_product';
}
}
Thank you for your advices.
You can do this by using form event listeners
symfony event doc
Context is
I have a Symfony2 form field of type collection, where the collection items are of the entity type. I use Symfony 2.7.
Problem is
So far it works, but in this case, I have to apply a model data transformer to those collection items as described in the Symfony Cookbook. I use this code snippet:
<?php
$builder
->add(
$builder
->create('items', 'collection', array(
'type' => 'entity',
'options' => array(
'class' => 'AppBundle:Item',
'property' => 'name',
'label' => 'Item',
),
'label' => 'Items',
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'prototype' => true,
'required' => false,
))
// $options['em'] is the entity manager
->addModelTransformer(new ItemToNumberTransformer($options['em']))
)
;
Unfortunately, this applies the model transformer to the whole collection and not one Item item of it. As a workaround, I modified the transformer to also work with arrays of items/ids instead of only a single item/id, but this kinda looks like the wrong place to handle this. Seems to me as if this is more of a syntactical problem.
Question is
Does anybody know how to apply model transformers to each item of a collection? Or ca anybody confirm this is simply not possible due to a limitaion in the Symfony framework?
I'd say instead of creating a collection of entity types, you need to make your own type.
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityManager;
/* Other use statements */
class ItemEntityType extends AbstractType
{
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addModelTransformer(new ItemToNumberTransformer($this->em));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'class' => 'AppBundle:Item',
'property' => 'name',
'label' => 'Item',
));
}
public function getParent()
{
return 'entity';
}
public function getName()
{
return 'appbundle_item_entity';
}
}
Then define this as a service
app/config/services.yml
services:
form.type.model.item_entity:
class: AppBundle\Form\Type\ItemEntityType
arguments: ["#doctrine.orm.entity_manager"]
tags:
- {name: form.type, alias: appbundle_item_entity}
And now you can specify this as the type for your collection
$builder
->create('items', 'collection', array(
'type' => 'appbundle_item_entity'
'label' => 'Items',
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'prototype' => true,
'required' => false,
))
Disclosure: I haven't tested this but it should work.
You should create a Type for your Item entity, apply the transformer to it, and then use it as a type for your collection.
I want to add a validation so that the the keyword and the field form elements become compulsory. These are not attached to any kind of entity though. I'm just going to handle the request in my controller. I'm not interested in HTML5 validation which I can do it myself.
Rules: Keyword cannot be null and Field cannot have anything apart from firstname or middlename values.
FORM TYPE:
class SearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$field = array('' => '', 'firstname' => 'Firstname', 'middlename' => 'Middlename');
$builder
->setAction($options['action'])
->setMethod('POST')
->add('keyword', 'text', array('label' => 'Keyword', 'error_bubbling' => true))
->add('field', 'choice', array('label' => 'Field', 'choices' => $field, 'error_bubbling' => true))
->add('search', 'submit', array('label' => 'Search'));
}
public function getName()
{
return 'personsearch';
}
}
CONTROLLER:
class SearchController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new SearchType(), array(),
array('action' => $this->generateUrl('searchDo')));
return $this->render('SeHirBundle:Default:search.html.twig',
array('page' => 'Search', 'form' => $form->createView()));
}
public function searchAction(Request $request)
{
//I'll carry out searching process here
}
}
This page in the docs details how to do what you want: http://symfony.com/doc/current/book/validation.html
Specifically, with your implementation of forms you can do something like this:
use Symfony\Component\Validator\Constraints\NotBlank;
// ...
$builder
->setAction($options['action'])
->setMethod('POST')
->add('keyword', 'text', array(
'label' => 'Keyword',
'error_bubbling' => true,
'constraints' => array(
new NotBlank(),
)
))
->add('field', 'choice', array(
'label' => 'Field',
'choices' => $field,
'error_bubbling' => true
))
->add('search', 'submit', array(
'label' => 'Search'
))
;
More on this at: http://symfony.com/doc/current/book/forms.html#using-a-form-without-a-class
As for your field element, you may need to make a new constraint yourself for that.
You may also like to look at the "Validation and Forms" section. Personally I prefer using the annotations method when doing validation.
If you want to use that method then, on your model, add the following use statement:
use Symfony\Component\Validator\Constraints as Assert;
And then use an annotation above the property you are validating:
/**
* #Assert\NotBlank()
*/
public $name;
All of the constraints available may be found and described here: http://symfony.com/doc/current/book/validation.html#validation-constraints
From your code, it seems you're not checking if the form is valid, you can add this section, and then redirect to your search action when the model is deemed valid.
I'm creating a form type to update options for accounts the user have.
I have 3 entities User, UserHasAccount and Account.
Here are the relations between them:
User (OTM) <-> (MTO) UserHasAccount (MTO) <-> (OTM) Account
In UserHasAccount I have 2 options options1 and options2 (boolean)
An account can be link to multiple user. I have a page where I want to be able to change the options foreach Users linked to this Account (/account/{id}/manage-user)
Here is the first form type to map the fields from User
class AccounUserOptionFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', 'entity', array(
'class' => 'AcmeUserBundle:User',
'property' => 'fullname',
))
->add('option1', 'checkbox', array(
'label' => 'account.form.option.one',
'required' => false,
'error_bubbling' => true,
))
->add('option2', 'checkbox', array(
'label' => 'account.form.option.two',
'required' => false,
'error_bubbling' => true,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\UserBundle\Entity\UserHasAccount',
'translation_domain' => 'AcmeAccountBundle'
));
}
public function getName()
{
return 'acme_update_account_option_type';
}
}
And the AccountUserOption form type:
class AccountUserOptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Display the collection of users
->add('accountUser', 'collection', array(
'type' => new AccountUserOptionFormType($options),
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AccountBundle\Entity\Account',
'cascade_validation' => true,
'validation_groups' => array('AcmeUpdateOption'),
));
}
public function getName()
{
return 'acme_account_user_option_type';
}
}
The problem I have is with:
->add('user', 'entity', array(
'class' => 'AcmeUserBundle:User',
'property' => 'fullname',
))
I just want to display (label or similar) the name of the user. Using entity is displaying a dropdown :/ Is there any core field type I could use or do I have to create a custom one? I would have think that this could be core.
Cheers,
Maxime