I'm using entity choice list in my form. I want to use only specific entities (in example: only groups that user belongs to)
So, in controller, I'm getting these groups, and trying to pass them into formBuider.
Controller:
/.../
$groups = $em->getRepository('VendorMyBundle:Group')->getUserGroups($user);
$form = $this->createForm(new Message($groups), $message);
/.../
so, what now? how to use it in formBuilder?
how to change this line to use passed array of groups?
->add('group','entity',array('class' => 'Vendor\MyBundle\Entity\Group', 'label'=>'Group:'))
or in the other way:
class MessageType
{
/.../
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('group','entity',
array(
'class' => 'Vendor\MyBundle\Entity\Group',
'property' => 'name',
'query_builder' => function ($repository) {
$qb = $repository->createQueryBuilder('group');
$qb->add('where', 'group.administrator = :user');
$qb->setParameter('user', $user->getId());
return $qb;
},
'label' => 'Group'
)
)
// Continue adding fields
;
}
/.../
}
so how can i get object $user to use in form builder? ($user represent current logged user)
You can give the object you want to use in the __construct() method.
Eg :
$form = $this
->get('form.factory')
->create(new ApplyStepOneFormType($this->company, $this->ad), $applicant);
In your form type :
function __construct(\Your\Bundle\Entity\Company $company, \DYB\ConnectBundle\Entity\Ad $ad) {
$this->company = $company;
$this->ad = $ad;
}
And then in your form type in buildForm method :
$company = $this->company;
$builder->add('ad', 'entity', array(
'class' => '\Your\Bundle\Entity\Ad',
'query_builder' => function(\Your\Bundle\Repository\AdRepository $er) use ($company) {
return $er->getActiveAdsQueryBuilder($company);
},
));
//In controller pass the value which you want to use in builder form in array like
$object = new Question();
$form->create(new QuestionType() , $object , array('sqtname'=>2,'question_type'=>2));
//In Form type class
public function buildForm(FormBuilderInterface $builder , array $options)
{
//for setting data field dynamically
if (array_key_exists('question_type', $options) && $options['question_type'] != '') {
$data = $em->getReference("RecrutOnlineStandardBundle:StdQuestionType",$options['question_type']->getId());
} else {
$data = "";
}
$builder->add('StdQuestionType', 'entity', array(
'class' => 'TestStandardBundle:StdQuestionType',
'property' => 'name',
'empty_value' => 'Sélectionner un question type',
'required' => true,
'data' => $data,
'query_builder' => function(EntityRepository $er ) use ( $options ) {
if (isset($options['sqtname']) && $options['sqtname'] != '') {
return $er->createQueryBuilder('sqt')
->where("sqt.name!= ".$options['sqtname']);
} else{
return $er->createQueryBuilder('sqt');
}
}
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Test\QuestionBundle\Entity\Question',
'required' => false,
'sqtname' => '',
'question_type' =>''
));
}
Bacteries' solution IS NOT a good one. For example, if you declare your type as service, it is impossible to pass an object to constructor.
A perfect solution is options - just pass data as option to form builder.
If you want to use custom query, you have to set query_builder option as follows:
use Doctrine\ORM\EntityRepository;
...
$message = new Message();
$form = $this->createFormBuilder($message)
->add('group', 'entity', array(
'class' => 'Vendor\MyBundle\Entity\Group',
'label'=>'Group:',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('g')
->... // whatever you want to do
}
))
->getForm();
You can find more info about query builder in Doctrine manual and about options for entity in Symfony2 manual.
Bacteries' solution is a real good one. Just a note to save headache to other guy like me :)
In this part may I point out the use ($company) part.
It was hidden by the frame and of course nothing works properly without it.
$builder->add('ad', 'entity', array(
'class' =>
'\Your\Bundle\Entity\Ad',
'query_builder' =>
function(\Your\Bundle\Repository\AdRepository $er) use ($company) {
return $er->getActiveAdsQueryBuilder($company);
},
)
);
Best way (my opinion) is give to your form entityManager and select all you need in it. But don't forget to declare empty key in setDefaults() otherwise data won't pass to your builder.
Something like this one
public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['em']->getRepository(''); // select all you need
$builder->add('title', 'text')
->add('content', 'textarea');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Main\BlogBundle\Entity\Post',
'validation_groups' => array('post'),
'required' => false,
'em' => null // this var is for your entityManager
));
}
Apply EM as simple option...
Related
I'm using Symfony <4 and I have an issue with a form using an other form in a Many-To-Many relation with parameter.
You will find below my FeatureForm:
->add('tags',CollectionType::class,
array(
'entry_type' =>TagFeatureType::class,
'allow_add' => true,
'allow_delete' => true,
'data' => $datas,
'entry_options' => array(
'label' => false,
)
)
)
Now my TagFeatureType:
->add('tag', EntityType::class,
array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr){
return $tr->findObjectNotMine();
}
)
)
I would like to inject a parameter into findOBjectNotMine but I cannot pass parameter from controller because the TagFeatureType is created by FeatureForm. Inside the buildForm function I cannot pass any extra parameter.
I see 2 possibilites, 1st I modify default options to allow an extra option, but it's a bit disgusting. 2nd, I could use session parameter and inject session service inside the constructor... But it looks like more a workaround than a proper way...
Do you know an elegant way to inject parameter to a form from buildForm function inside FormType?
Best regards
In case you need to pass container-known parameters to custom form type, you can go the way you attempted above (obviously via parameter injection). However, if you want to pass data from controller down to form type, you can pass it via $options (last) argument (in buildForm):
FeatureForm
public function buildForm(FormBuilderInterface $builder, array $options)
// ....
$builder->add('tags', CollectionType::class, array (
'entry_type' => TagFeatureType::class,
'allow_add' => true,
'allow_delete' => true,
'data' => $datas,
'entry_options' => array(
'label' => false,
'some_custom_param' => $options['some_custom_param']
)
)
);
// ....
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Task::class
));
$resolver->setRequired(array(
'some_custom_param'
));
}
And then, in TagFeatureType should have configured options:
TagFeatureType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(array(
'some_custom_param'
));
}
And then finally, include it inside of buildForm:
public function buildForm(FormBuilderInterface $builder, array $options)
// ....
$someCustomParam = $options['some_custom_param'];
// ....
$builder->add('tag', EntityType::class, array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr) use ($someCustomParam)
{
return $tr->findObjectNotMine($someCustomParam);
}
);
// ....
}
Obvious downside of this would be a need for all the forms in path to have setRequired or setDefault.
Hope this helps...
In your TagFeatureType you can just pass in the argument via constructor injection:
class TagFeatureType extends AbstractType
{
private $tagRepository;
public function __construct(TagRepository $tagRepository)
{
$this->tagRepository = $tagRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$tr = $this->tagRepository;
// ...
->add('tag', EntityType::class,
array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr){
return $tr->findObjectNotMine();
}
)
)
}
}
There is a DependencyInjectionExtension form extension that will check if the form type is registered in the service container and then inject it, instead of creating a new instance. This should make sure that the tag repository exists.
I can not get value from EntityType. I have last version 3.3.6.
class BuildType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('title', TextType::class)
->add('save', SubmitType::class, array('label' => 'Create Post'))
->add('team', CollectionType::class, array(
// each entry in the array will be an "email" field
'entry_type' => TeamType::class,
// these options are passed to each "email" type
'entry_options' => array(
'attr' => array('class' => 'form-control'),
),
'label' => false,
'allow_add' => true,
'prototype' => true,
'mapped' => false
));
}
}
class TeamType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('name', EntityType::class, array(
'placeholder' => 'Choice a champ',
'required' => true,
'class' => 'AppBundle:Champions',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'id',
'attr' => array('class' => 'dropdown'),
));
}
I tried all but i cannot take value of 'name' of TeamType. After submission form, i do
foreach ($form["team"]->getData() as $value) {
'name' => $value['name']
but the value is empty. If i try dump request and the value is there. The other values i can get it and save in Database. Only EntityType i can not.
Someone know how do?
EntityType returns as the object. You should use model getter functions.
$form->get('name')->getData()->getId(); // getName() vs..
Here is a similar example.
symfony how get selected data from select
I suppose that you are using ManyToOne relationship.
With AJAX
if you are trying get data after submitted, You can do this first in your view:
$('#elementWhereYouAreTEAMType').find('input').each(function (i,v) {
var valTeam = $(this).val(); //take value
// adding data, create an associative array.
formData.append("team["+ i +"]", valTeam);
});
You must put formData like data argument to ajax JQuery
Now, server side:
public function createTeamNow(Request $request) {// ajax
$teams = $request->request->get('team'); // getting array
if(!is_null($teams)) {// if user added team
foreach ($teams as $team) {
// dump($team);
//create an instance for each element, it does not replace a data with the above
$teamType = new TeamType();
$teamName->setName($team);
$this->em->persist($teamType);
}
}
}
Without AJAX
/**
* #Route("/slim/1" , name="data_x")
*/
public function slimExampleAction(Request $request)
{
$form = $this->createForm(TeamType::class);
$form->handleRequest($request);
if ($form->isSubmitted() /*&& $form->isValid()*/) {
// When you've ManyToOne relationship, that field returns ArrayCollection class, it has several method to get data on differents ways, iterate with it, for example toArray is one
$data = $form->getData();
dump($data->getDescription()->toArray());die;
}
return $this->render('AppBundle:view.html.twig', array(
'form' => $form->createView(),
));
}
I have a form that collects data from two related entities 'Jobs' and 'Scope'. Jobs data adds successfully to the database, but i can't get the scope entity to add which also requires the unique ID that was created from the jobs entry. I'm using doctrine by the way.
Here is jobsType:
class jobsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('jobNumber', null, array('label' => 'Job Number','attr' => array('placeholder'=>'Code used for job')))
->add('description', null, array('label' => 'Job Description',))
;
$builder->add('scopes', new scopeType())
;
}
public function getName()
{
return 'jobs';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Baker\TimeSlipBundle\Entity\Jobs',
));
}
}
Here is scopeType
class scopeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', 'entity', array('label' => 'Scope', 'required' => false,
'class' => 'BakerTimeSlipBundle:Scope',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('s')
->orderBy('s.description', 'asc');
},
'property' => 'description',
'multiple' => false,
'expanded' => false,
'empty_value' => false,
'attr' => array('class' => 'form-inline'),
'label_attr' => array('class' => 'required')
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Baker\TimeSlipBundle\Entity\Scope',
));
}
public function getName()
{
return 'scope';
}
}
Here is my controller, i think the problem here is that i'm not passing the scope() instance to the form builder. Which i'm not sure how to do. The entity is mapped correctly.
class JobsController extends Controller
{
public function indexAction($limit, Request $request)
{
$jobs= new Jobs();
$scope = new Scope();
$fname=$limit;
$form = $this->createForm(new JobsType(),$jobs, array(
'method' => 'POST',
));
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($jobs);
$em->persist($scope);
$em->flush();
return $this->redirectToRoute('company',array ('limit' =>'Job Submitted Successfully'));
}
return $this->render(
'BakerTimeSlipBundle:Jobs:index.html.twig',
array('fname' => $fname,
'form' => $form->createView()
)
);
}
}
}
Any help here would be greatly appreciated. Below is listed the error message when I try to enter the scope:
An exception occurred while executing 'INSERT INTO Scope (description, jobsId, jobsid) VALUES (?, ?, ?)' with params [null, null, null]:
SQLSTATE[42000]: Syntax error or access violation: 1110 Column 'jobsId' specified twice
The problem here is the $jobs object you link to your form has no relation with the $scope object. So when you try to persist $jobs, Doctrine has no idea $scope it had to persist.
Just try this :
$jobs= new Jobs();
$scope = new Scope();
$jobs->setScope($scope);
Then, in your Jobs entity, on the annotation above $scopes, make sure you have cascade={"persist"} (but you must already have it because Doctrine tries to persist, and that's where the error is thrown), and you should be good to go.
Hope this helps
Hi i am tying pass array collection (method getProjects() returns it) to form (select input) and fail. This code returns exception - A "__toString()" method was not found on the objects of type "Tasker\WebBundle\Entity\Project" passed to the choice field.
Can anybody help? Is needed transformer? Or what is right way?
Controller:
/**
* #Route("/pridaj", name="web.task.add")
* #Template()
*/
public function addAction(Request $request)
{
$task = new Task;
/** #var User $loggedUser */
$loggedUser = $this->get('security.token_storage')->getToken()->getUser();
$form = $this->createForm(new AddTaskType(), $task, ['user' => $loggedUser]);
if ($form->handleRequest($request) && $form->isValid()) {
// some stuff
}
return [
'form' => $form->createView()
];
}
Form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
])
// ....
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array(
'user',
));
$resolver->setDefaults(array(
'user' => null,
));
}
just add __ToString() to your Project class
Tasker\WebBundle\Entity\Project
class Project
{
....
function __toString() {
return $this->getName(); //or whatever string you have
}
}
I wanted to add another answer, because you do not have to add __toString() to your Project class. The Symfony entity field type allows you to specify which property/field to use for displaying. So instead of __toString() you could specify the property in the form configuration like so:
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
'property' => 'name'
])
If you check this part of the Symfony documentation you will see that __toString() is automatically called only if you do not specify the property.
I have a entity
Product:
name # string
country # entity
categories #entity many-many
I have a form for that entity
ProductType:
name
categories
now i need filter categories by country but i dont wanna show a country parameter when i build the form I do
//...
$entity = new Entity\Product();
$entity->setCountry($this->getUser()->getProfile()->getCountry());
$form = $this->createForm(new Form\ProductType(), $entity);
return array('form' => $form->createView());
i want filter the categories by country in the ProductType class, how can achieve this?.
How i can pass $country value to query builder?
//...
->add('categories', 'entity', array(
'class' => 'MyBundle:Category',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBulder('c');
}
)
You are looking for the options array you can pass to a form class.
Add this to your FormType:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'country' => null
));
}
then call the FormType like this:
$this->createForm(new Form\ProductType(), $entity, array(
'country' => $country
));
And access the $country in your buildForm method like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$country = $options['country'];
From there on you could build you own queryBuilder to select only the products you need.
Edit:
For accessing the $country variable in the queryBuilder you should use the use statement. It would look like this:
->add('categories', 'entity', array(
'class' => 'MyBundle:Category',
'query_builder' => function (EntityRepository $er) use($country) {
// here you can use the $country variable in your anonymous function.
return $er->createQueryBuilder('c');
}
)
)