I want to translate a form created with symfony's formbuilder. As i don't want one big translation file it is splitted up into "domains".
Now i have to specify the translation_domain for each form-field, otherwise symfony will look in the wrong file. This option has to be added to every field and i'm wondering if there is a way to set this option to a whole form?
Sample code i'm not happy with:
$builder->add(
'author_name',
'text',
array('label' => 'Comment.author_name', 'translation_domain' => 'comment')
)->add(
'email',
'email',
array('label' => 'Comment.email', 'translation_domain' => 'comment')
)->add(
'content',
'textarea',
array('label' => 'Comment.content', 'translation_domain' => 'comment')
);
You've then to set it as a default option of your form, add this:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'translation_domain' => 'comment'
));
}
to your setDefaultOptions method, in your form.
Update: It is deprecated. Use configureOptions method instead (thanks #Sudhakar Krishnan)
The method name in Ahmed's answer is now deprecated (since Symfony 2.7), the 2.7+ way of doing it is:
/**
* Configures the options for this type.
*
* #param OptionsResolver $resolver The resolver for the options.
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('translation_domain', 'messages');
}
in the same way you set your data_class settings, etc.
To do this using just the form builder, there is an options argument on the form builder. From the controller, for example:
$form = $this->createFormBuilder($entity, ['translation_domain' => 'messages'])->add(..)->getForm();
If you're using the FormFactory service, this would be
$formFactory->createBuilder('form', $entity, ['translation_domain' => 'messages']);
Symfony 3
/**
* Configures the options for this type.
*
* #param OptionsResolver $resolver The resolver for the options.
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'translation_domain' => 'forms',
// Add more defaults if needed
));
}
Or in case you use the Factory's namedBuilder that would be:
$formBuilder = $this->get('form.factory')->createNamedBuilder('myForm', 'form', $data, array(
'translation_domain' => 'forms',
));
Related
My scenario is,
I need to create a entity(module) with display order and the module is under a topic entity and association made correctly. On form load the topic dropdown and display order dropdown will be blank along the module name. When selecting topic the display order will fill with options via ajax/js. Display order will be 1 to a number that will be the total modules under the specific topic+1 . The upcoming display order will be selected automatically. And that's working perfectly. But my issue is about the display order validation after submit. Its saying 'This value is not valid'. I understands this is due to not giving 'choices' as array in form type, but this case i cant give as static in form type. Please help anyone knows a solution.
class ModuleType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('topic', EntityType::class, [
'class' => 'AppBundle:Topic',
'choice_label' => 'name',
'placeholder' => 'Choose a Topic'
])
->add('name')
->add('description', TextareaType::class)
->add('displayOrder', ChoiceType::class)
->add('save', SubmitType::class, [
'attr' => ['class' => 'form-control button btn-sm nomargin']
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Module'
));
}
}
Try to use the Event Listener.
In your case for exemple:
// TopicType.php
private function addEventListener(FormBuilderInterface $builder)
{
// this function permit to valid values of topics
$annonymFunction = function(FormInterface $form, $diplayOrder) {
$entities = $this->container->get('doctrine.orm.default_entity_manager')
->getRepository('YourBundle:Topic')
->findAll();
if ($entities) {
$topics = array();
foreach($topics as $topic) {
$topics[$topic->getName()] = $topic->getName();
}
}
else $topics = null;
$form->add('topic', EntityType::class, array(
'attr' => array('class' => 'topic'),
'choices' => $topics));
};
$builder
->get('displayOrder')
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) use ($annonymFunction) {
$annonymFunction($event->getForm()->getParent(), $event->getForm()->getData());
});
}
Hope to help.
I have a nested form
demand
home
child
godfather
demand is a parent and embed home which embed child and father (2 last forms are on the same level)
In DemandeType I have:
$builder
->add('date', 'datetype')
->add('name', 'text')
//...
->add('home', 'home', array(
'mapped' => false,
'data_class' => 'AppBundle\Entity\Home',
'inherit_data' => true
))
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Demand',
));
}
In HomeType :
$builder
->add('address', 'textarea')
//...
->add('child', 'child', array(
'mapped' => false,
'data_class' => 'AppBundle\Entity\Child',
'inherit_data' => true
))
->add('godfather', 'godfather', array(
'mapped' => false,
'data_class' => 'AppBundle\Entity\Godfather',
'inherit_data' => true
))
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Home',
));
}
And in ChildType and GodfatherType I have only text fields for firstname, lastname with their right data_class.
But when I submit the form (DemandType, wichh embed all subforms) I got this error :
Neither the property "address" nor one of the methods "getAddress()", "address()", "isAddress()", "hasAddress()", "__get()" exist and have public access in class "AppBundle\Entity\Demand".
And these methods don't belong to Demand entity but Home entity. I've put the inherit_data, what I'm missing ?
Thanks
This happens because you're using inherit_data. This property makes the form pass the entire submitted data to its child instead of a single property (or anything from getter function) which happens by default.
You do this for both demand and home so that's why home form type receives an instance Demand entity. So I guess you want to remove inherit_data from home and use just:
->add('home', 'home', array(
'mapped' => false,
'data_class' => 'AppBundle\Entity\Home',
))
In this case home will receive data from $demand->getHome() which should be a Hone entity.
I'm not sure you really need to use inherit_data at all but depends on your use case. Usually, you don't need it because you have structure of entities such as:
/** #ORM\Entity() */
class Demand {
/** #ORM\OneToWhatever() */
private $home;
public function getHome() {
return $this->home;
}
}
/** #ORM\Entity() */
class Home {
/** #ORM\OneToWhatever() */
private $child;
public function getChild() {
return $this->child;
}
}
/** #ORM\Entity() */
class Child { ... }
But I don't know what exactly your data structure is so it's hard to help.
Also, you're using mapped => false which I'm not sure is what you want because it prevent Symfony from updating entities with form data.
See:
http://symfony.com/doc/current/reference/forms/types/form.html#mapped
http://symfony.com/doc/current/reference/forms/types/form.html#inherit-data
I am working with two ManyToMany related entities, namely category and tag.
The entity Tag(relevant details):
/**
*
* #var string
*
* #ORM\Column(name="tagname", type="string")
*/
protected $tagname;
/**
* #ORM\ManyToMany(targetEntity="Category", mappedBy="tags")
*/
protected $categories;
The entity Category(relevant details):
/**
*
* #var string
*
* #ORM\Column(name="CategoryName", type="string",length=200)
*/
protected $categoryname;
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="categories")
*/
protected $tags;
I have a form with a select-input(CategoryType) and a multiple select-input(TagType) fields. Both the fields are EntityType fields. The TagType is embedded inside the CatgoryType.
For this I am not able to utilise the cascade=persist functionality and am adding the submitted tags manually inside my controller. On submission the form data gets persisted in the database without any issues.
The problem is, after submission, when I fetch the submitted category(and the associated tags) in my controller, and pass it to the form, I get this error - Unable to transform value for property path "tagname": Expected a Doctrine\Common\Collections\Collection object.
The var_dump result of the fetched category object(var_dump($category->getTags()->getValues());) gives me an array of the associated Tag objects, with the property protected 'tagname' => string 'tag1'.
From what I understand Interface Collection is quite similar to a php array and My guess is that the tagname field expects all the tagnames in an ArrayCollection or Collection object format. I am not sure whether what is the specific difference.
However I am still clueless how do I pass the already persisted category object in my form.
Here are the categoryname and tags field in the CategoryType:
$builder->add('categoryname', EntityType::class, array(
'class' => 'AppBundle:Category',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.id', 'ASC');
},
'choice_label' => 'categoryname',
'expanded' => false,
'multiple' => false,
'label' => 'Choose Category',
));
$builder->add('tags', CollectionType::class, array(
'entry_type' => TagType::class,
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
));
Here is the embedded tagname field in the TagType:
$builder->add('tagname', EntityType::class, array(
'class' => 'AppBundle:Tag',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('t')
->orderBy('t.id', 'ASC');
},
'choice_label' => 'tagname',
'expanded' => false,
'multiple' => true,
'label' => 'Choose Tags',
));
Any ideas?
Try to rid off 'multiple' => true in embedded form. This worked for me.
I've had a similar problem where my EntityType form element gave me this error message:
Unable to reverse value for property path 'my_property' Expected an array.
Setting multiple to false would make the implementation useless, so a better option for me was to set the empty_data to an empty array:
"empty_data" => [],
In your case you might be able to solve the problem by setting the empty_data to a Collection, something like this will probably work:
"empty_data" => new ArrayCollection,
Here is how you do it (from source: https://www.youtube.com/watch?v=NNCpj4otnrc):
***And here is a text version of this image #1, the reason I did not add text because it was hard to read when the format is not proper!
<?php
namespace App\Form;
use App\Entity\Post;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'attr' => [
'placeholder' => 'Etner the title here',
'class' => 'custom_class'
]
])
->add('description', TextareaType::class, [
'attr' => [
'placeholder' => 'Enter teh description here',
]
])
->add('category', EntityType::class, [
'class' => 'App\Entity\Category'
])
->add('save', SubmitType::class, [
'attr' => [
'class' => 'btn btn-success'
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Post::class,
]);
}
}
***And here is a text version of this image #2, the reason I did not add text because it was hard to read when the format is not proper!
class FormController extends AbstractController
{
/**
* #Route("/form", name="form")
* #param Request $request
* #return Response
*/
public function index(Request $request)
{
$post = new Post(); // exit('this');
/*$post->setTitle('welcome');
$post->setDescription('description here');*/
$form = $this->createForm(PostType::class, $post, [
'action' => $this->generateUrl('form'),
'method' => 'POST'
]);
// handle the request
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
// var_dump($post); die();
// save to database
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
}
return $this->render('form/index.html.twig', [
'postaform' => $form->createView()
]);
}
My assumption was wrong, I need to either make tagname field an array or create a ManyToOne or ManyToMany relationship with any other entity so it can be an arraycollection. Only then it is possible to use tagname as a multiple select field.
/**
*
* #var array
*
* #ORM\Column(name="tagname", type="array")
*/
protected $tagname;
or
/**
* #ORM\ManyToMany(targetEntity="SOME_ENTITY", mappedBy="SOME_PROPERTY")
*/
protected $tagname;
or
/**
* #ORM\ManyToOne(targetEntity="SOME_ENTITY", mappedBy="SOME_PROPERTY")
*/
protected $tagname;
The exception thrown is pretty explicit, and the problem is probably here:
$builder->add('tagname', EntityType::class, array()
According to your Tag entity, Tag::$tagname is not an entity collection, it's a string. You should add the property with
$builder->add('tagname', TextType::class, array()
shouldn't you ?
Running into a minor problem on Symfony 2.8. I have a couple of db fields, one is an integer and one is decimal. When I build my form, these fields are dropdowns so I'm using ChoiceType instead of IntegerType or NumberType.
The form actually works fine, the difference internally between the two apparently doesn't cause an issue, and I can pick a value and it properly saves to the db.
The issue now is in a Listener. When certain fields are changed, I need to kick off an extra process, so I have an event listener and am using the getEntityChangeSet() command.
What I'm noticing is that it's reporting back these fields as changed, because it's recognizing a difference between 1000 and "1000" which I can see on a Vardump output:
"baths" => array:2 [▼
0 => 1.5
1 => "1.5"
]
This is causing the listener to always trigger my hook even when the value really hasn't changed. If I change the form type to Integer, that's just a text entry and I lose my dropdown. How do you force a dropdown ChoiceType to treat a number as a number?
In my entity, this is properly defined:
/**
* #var float
*
* #ORM\Column(name="baths", type="decimal", precision=10, scale=1, nullable=true)
*/
private $baths;
In my regular form:
->add('baths', BathsType::class)
which pulls in:
class BathsType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => array_combine(range(1,10,0.5),range(1,10,0.5)),
'label' => 'Bathrooms:',
'required' => false,
'placeholder' => 'N/A',
'choices_as_values' => true,
]);
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
}
}
You should only pass values to your choices option, they will be indexed by numeric keys used as strings for "hidden" html input values which will do the mapping behind the scene.
Then use choice_label to set the labels (visible values) as the choices casted to string :
class BathsType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => range(1,10,0.5),
'label' => 'Bathrooms:',
'required' => false,
'placeholder' => 'N/A',
'choices_as_values' => true,
'choice_label' => function ($choice) {
return $choice;
},
]);
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
}
}
In symfony 4 choices_as_values does not exist, so the solution would be the same as Heah answer but without that option:
class BathsType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => range(1,10,0.5),
'label' => 'Bathrooms:',
'required' => false,
'placeholder' => 'N/A',
'choice_label' => function ($choice) {
return $choice;
},
]);
}
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
}
}
I have a form that works well, there is just one issue with it and I'm hoping that I'll get an answer on how to do what I need to do.
<?php
namespace ADS\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\SecurityContext;
class UserType extends AbstractType {
private $type;
public function __construct($type) {
$this->type = $type;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstName', 'text', array('required' => true))
->add('lastName', 'text', array('required' => true));
$builder->add('email', 'email', array('required' => true));
$builder->add('parentCompany', 'entity', array(
'class' => 'ADSUserBundle:Company',
'expanded' => false,
'empty_value' => 'CHOOSE ONE',
'required' => false,
'property' => 'companyName'
))
->add('enabled', 'choice', array('choices' => array('1' => 'Enabled', '0' => 'Disabled')))
->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class' => 'ADS\UserBundle\Entity\User'));
}
public function getName() { return 'ads_userbundle_user'; }
}
I have this form, the portion I am looking at is the 'roles' portion... Right now it created a multiple select box ( as I expect it to ), though the value is sequentially ie: 0,1,2,3,4...
What I really need is to figure out how to take this entity, and make the property to be the displayName ( as it is now ) and get the value to be the corresponding internalName This way it'll give me an array like:
array('ROLE_EXAMPLE' => 'EXAMPLE', 'ROLE_EXAMPLE1' => 'EXAMPLE1')
Any ideas how to accomplish this is greatly appreciated.
Kamil Adryjanek is correct, it is going to be much easier if you change it from an entity to a choice field. I've done some testing, both with FOSUserBundle and without the bundle - in both cases I hit some interesting road blocks.
First, I tried to run it through QueryBuilder in a repository, that didn't work out as it should have. The reason being, the fact that you wanted to be returning an array instead of a ORM object causes an error.
So next, I started looking at creating the choice field. All the guides, say to use the fieldname role instead of roles so I tried that, but I then had to duplicate the UserInterface from FOSUserBundle - I didn't want to do that -- so here I am stressed, and trying to figure it out.
Here is what I ended up doing, which works well.
private $normalRoles = array();
then in the __construct I add: $this->normalRoles = $roles;
Here is the builder:
$builder
->add('roles', 'choice', array(
'multiple' => true,
'choices' => $this->normalRoles
))
;
Originally, I left the multiple part out, figuring that it'd at least let me see an option box. I ended up getting an Array to String conversion error. So, adding the 'multiple' => true in, fixes that error.
Then, in my repository I created a function called normalizeRoles
public function normalizeRoles() {
$data = array();
$qb = $this->getEntityManager();
$query = $qb->createQuery(
"SELECT r.internalName, r.displayName FROM AcmeUserBundle:Roles r"
)->getArrayResult();
foreach ($query as $k => $v) {
$data[$v['internalName']] = $v['displayName'];
}
return $data;
}
From here, we just have to make some small edits in the DefaultController of the UserBundle in the newAction and editAction ( both are the same changes )
So, first off is to put into your Controller use Acme/UserBundle/Entity/Roles in order to avoid any errors and be able to get that repository.
Next, right before you create the form you run the normalizeRoles() function
$roles = $em->getRepository('AcmeUserBundle:Roles')->normalizeRoles()
Then, you pass it through the construct via: new UserType($roles)
full line for that would look like this:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.edit', array(
'id' => $id)
)
));
or for new:
$form = $this->createForm(new UserType($roles), $entity, array(
'action' => $this->generateUrl('acmd.user.new')
)
));
At this point -- You'll have a working system that will allow you to dynamically add roles into a database table, and then associate those with a new or current user.
You can try do it via query_builder attribute:
$builder->add('roles', 'entity', array(
'class' => 'ADSUserBundle:Roles',
'required' => true,
'property' => 'displayName',
'query_builder' => function (RolesRepository $queryBuilder) {
return $queryBuilder->someMethod() // some method in this repository that return correct query to db.
},
));
In this case it would be better to use choice field Type (http://symfony.com/doc/current/reference/forms/types/choice.html) instead of entity and pass some role choices as option to form because entity field Type get entity id as key for choices:
public function buildForm(FormBuilderInterface $builder, array $options) {
...
$builder->add('roles', 'choice', array(
'choices' => $options['role_choices']
));
...
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ADS\UserBundle\Entity\User',
'role_choices' => array()
));
}
Notice: it's recommended to pass variables to form through options parameter, not in constructor.
if I understand your question correctly, you need a data transformers. They help you to show data in form as you want.
Documentation: http://symfony.com/doc/current/cookbook/form/data_transformers.html