Symfony 3 - Edit form - populate fields with db data (array) - php

I needed a form with 3 dynamic select boxes (choicetypes), which, after form submission, are saved as a serialized array in the database.
I have managed to make it work in the end but now I am struggling to populate the dropdowns with the database values when editing a project.
Projects Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="projects")
*/
class Projects
{
/**
* #ORM\Column(type="string", length=255)
*/
protected $projectName;
/**
* #ORM\Column(type="array")
*/
protected $frequency;
/**
* No database fields for these.
* They are used just to populate the form fileds
* and the serialized values are stored in the frequency field
*/
protected $update;
protected $every;
protected $on;
/*
* Project Name
*/
public function getProjectName()
{
return $this->projectName;
}
public function setProjectName($projectName)
{
$this->projectName = $projectName;
}
/*
* Frequency
*/
public function getFrequency()
{
return $this->frequency;
}
public function setFrequency($frequency)
{
$this->frequency = $frequency;
}
/*
* Update
*/
public function getUpdate()
{
return $this->update;
}
public function setUpdate($update)
{
$this->update = $update;
}
/*
* Every
*/
public function getEvery()
{
return $this->every;
}
public function setEvery($every)
{
$this->every = $every;
}
/*
* On
*/
public function getOn()
{
return $this->on;
}
public function setOn($on)
{
$this->on = $on;
}
}
Projects Controller
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Projects;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* Projects controller.
*/
class ProjectsController extends Controller
{
/**
* #Route("/projects/new", name="projects_new")
*/
public function newAction(Request $request)
{
$project = new Projects();
$form = $this->createForm('AppBundle\Form\ProjectsType', $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
// This is where the data get's saved in the frequency field
$frequency = array(
"update" => $project->getUpdate(),
"every" => $project->getEvery(),
"on" => $project->getOn()
);
$project->setFrequency($frequency);
$em->persist($project);
$em->flush($project);
return $this->redirectToRoute('projects_show', array('id' => $project->getId()));
}
return $this->render('projects/new.html.twig', array(
'project' => $project,
'form' => $form->createView(),
));
}
/**
* #Route("/projects/edit/{id}", name="projects_edit", requirements={"id": "\d+"})
* #ParamConverter("id", class="AppBundle:Projects")
*/
public function editAction(Request $request, Projects $project)
{
$editForm = $this->createForm('AppBundle\Form\ProjectsType', $project);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
// This is where the data get's saved in the frequency field
$frequency = array(
"update" => $project->getUpdate(),
"every" => $project->getEvery(),
"on" => $project->getOn()
);
$project->setFrequency($frequency);
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('projects_edit', array('id' => $project->getId()));
}
return $this->render('projects/edit.html.twig', array(
'project' => $project,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
}
ProjectsType Form
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use AppBundle\Entity\Projects;
class ProjectsType extends AbstractType
{
private function setUpdateChoice(Projects $project)
{
return $project->getFrequency()['update'];
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('projectName', null, array());
$builder->add('update', ChoiceType::class, array(
'label' => 'Update',
'attr' => array(
'class' => 'update_selector',
),
'choices' => array(
'Daily' => 'Daily',
'Weekly' => 'Weekly',
'Monthly' => 'Monthly'
),
'data' => $this->setUpdateChoice($builder->getData())
)
);
$addFrequencyEveryField = function (FormInterface $form, $update_val) {
$choices = array();
switch ($update_val) {
case 'Daily':
$choices = array('1' => '1', '2' => '2');
break;
case 'Weekly':
$choices = array('Week' => 'Week', '2 Weeks' => '2 Weeks');
break;
case 'Monthly':
$choices = array('Month' => 'Month', '2 Months' => '2 Months');
break;
default:
$choices = array();
break;
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($addFrequencyEveryField) {
$update = $event->getData()->getUpdate();
$update_val = $update ? $update->getUpdate() : null;
$addFrequencyEveryField($event->getForm(), $update_val);
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addFrequencyEveryField) {
$data = $event->getData();
$update_val = array_key_exists('update', $data) ? $data['update'] : null;
$addFrequencyEveryField($event->getForm(), $update_val);
}
);
$addFrequencyOnField = function (FormInterface $form, $every_val) {
$choicesOn = array();
switch ($every_val) {
case 'Week':
$choicesOn = array(
'Monday' => 'Monday',
'Tuesday' => 'Tuesday',
'Wednesday' => 'Wednesday',
'Thursday' => 'Thursday',
'Friday' => 'Friday',
'Saturday' => 'Saturday',
'Sunday' => 'Sunday',
);
break;
case 'Month':
$choicesOn = array(
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
);
break;
default:
$choicesOn = array();
break;
}
$form->add('on', ChoiceType::class, array(
'choices' => $choicesOn,
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($addFrequencyOnField) {
$every = $event->getData()->getEvery();
$every_val = $every ? $every->getEvery() : null;
$addFrequencyOnField($event->getForm(), $every_val);
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addFrequencyOnField) {
$data = $event->getData();
$every_val = array_key_exists('every', $data) ? $data['every'] : null;
$addFrequencyOnField($event->getForm(), $every_val);
}
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Projects'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_projects';
}
}
and finally, the Edit Template containing ajax
{% extends 'base.html.twig' %}
{% block body %}
{% block content %}
<h1>Edit Project</h1>
{{ form_start(edit_form) }}
{{ form_widget(edit_form) }}
<input type="submit" value="Edit" />
{{ form_end(edit_form) }}
{% endblock content %}
{% block javascripts %}
<script>
var $update = $('#appbundle_projects_update');
var $every = $('#appbundle_projects_every');
var $on_selector = $('#appbundle_projects_on');
// When update gets selected ...
$update.change(function() {
$every.html('');
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected update value.
var data = {};
data[$update.attr('name')] = $update.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
var options = $(html).find('#appbundle_projects_every option');
for (var i=0, total = options.length; i < total; i++) {
$every.append(options[i]);
}
}
});
});
// // When every gets selected ...
$every.change(function() {
$on_selector.html('');
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected every value.
var data = {};
data[$every.attr('name')] = $every.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
var options = $(html).find('#appbundle_projects_on option');
for (var i=0, total = options.length; i < total; i++) {
$on_selector.append(options[i]);
}
}
});
});
</script>
{% endblock javascripts %}
{% endblock %}
I was able to set the selected value in the update field using setUpdateChoice() and assigning the return value to the ChoiceType::class data in ProjectsType. However, this doesn't have any effect on the every select or the other one, the PRE_SET_DATA listener returning null.
I've tried the same as above with the other two fields as well, but having the fields rendered in the listener resulted in an error no such field (or something similar)
I've also tried to get the value from the frequency in the controller and using setUpdate(), setEvery() and setOn() before rendering the form, resulting in You can't modify a form that has already been submitted.
This is how the form currently renders when editing a project:
And I would need it populated like this:
Any idea/advice/example of how I can tackle this would be greatly appreciated.
Thank you
PS. If I've omitted something, please let me know and I'll update my question.
Update
Thanks to Constantin's answer, I've managed to set the fields data in the controller doing the following:
public function editAction(Request $request, Projects $project)
{
$project->setUpdate($project->getFrequency()['update']);
$project->setEvery($project->getFrequency()['every']);
$project->setOn($project->getFrequency()['on']);
$editForm = $this->createForm('AppBundle\Form\ProjectsType', $project);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$frequency = array(
"update" => $project->getUpdate(),
"every" => $project->getEvery(),
"on" => $project->getOn()
);
$project->setFrequency($frequency);
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('projects_edit', array('id' => $project->getId()));
}
return $this->render('projects/edit.html.twig', array(
'project' => $project,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}

Not sure that this will help you as I have not enough time to read all your post.
First of all (and just for your information), you can use in your form for your 3 fields (update, every and on) the option 'mapped' => false and remove from your entity these 3 field (this is a mapping with your entity, not DB).
You can then access them in your controller with
$editForm->get('update')->getData(); //or setData( $data );
But of course you can keep these attributes if you want and if it is more convenient for you.
But if you do keep them, please make sure that they will always reflect the data in your frequency attribute.
Next, I see in your edit controller that before creating your form, you don't set the values of your 3 fields.
As these fields are not mapped (with the database this time), they will be null when you fetch your entity (this is done with your paramConverter in your edit controller).
So before passing your $project as a parameter in your creating form, you have to set them (unserialize or whatever).
Hope that this will help you!

Related

is it possible to assign a value to a field added with EventListener symfony in $builder symfony?

I would like to know if it is possible to automatically assign values ​​to added fields of type:
datetime
entity
Thanks for your help
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $options['user']; // entity User
$player = $options['player']; // entity Player
$today = new DateTime('now');
$builder
->add('fieldA')
->add('fieldB')
->add('fieldC');
$builder
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($user, $player, $today) {
$form = $event->getForm();
$datas = $event->getData();
$form->add('today');
$form->add('user');
$form->add('player');
//dd($form); ok = 3 fields added
$datas['dateDuJour'] = $today;
$datas['user'] = $user;
$datas['player'] = $player;
//dd($datas); ok = 3 assigned values
$form->setData($datas);
question 1 : how to insert the data in the form
question 2 : pb from entity (object) to string
//dd($form, $datas);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Evaluation::class,
'user' => null,
'player' => null
]);
}
}
I thought about inserting the 3 fields with the type = hidden and using Data Transformer
I do not know what is the best practice?
if you have a concrete example
From what i can see, you have some form and you want to plug 3 data to the form on submit.
Depending on your database configuration, you can do 3 different way:
The best one is to use the mapping
Your evaluation have those 3 fields:
date
user
player
Then just add them to the original builder as hidden field whith default value what you have:
$builder->add('token', HiddenType::class, [
'data' => $today,
])->add('user', HiddenType::class, [
'data' => $user,
])->add('player', HiddenType::class, [
'data' => $player,
]);
As they are hidden, the security check will not autorise users to change those value plus thoise fields will be hiddent
It require those three fields exist in your entity
Second one is to use unmapped hidden field. Same a previous, but add 'mapped'` => false . Then you in your controller, you will have the value and use them as needed.
The third one is to not use them in your form (my favorite) but in your controller
public function addEvaluation(Request $request, EvaluationManager $evaluationManager): Response
{
$evaluation = new Evaluation();
$form = $this->createForm(EvaluationType::class, $evaluation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$evaluation->setTime(new DateTime('now'))
->setUser($user)
->setPlayer($player);
$evaluationManager->save($evaluation);
return $this->redirectToRoute('evaluation_add');
}
return $this->render('/evaluation_add.twig', [
'form' => $form,
]);
}

Symfony, how to use form event to validate dynamic client-side form

I'm using the select2 plugin with ajax to have a dynamic field on my form, but when i submit the it return me an error "This value is not valid", which is normal cause i use the ChoiceType with an empty array() in the choices options on creation. According to this part of the symfony doc, the form event is my savior, so trying to use it but it look like something wrong with my code and can't really see what.
So My Question Is :
HOW to pass the choices possibility to the field, for the form to be valid.
My form Type
class ArticleType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//My other field
//My functions to add the field with the possible choices
$formModifier = function (FormInterface $form, $imageValue) use ($options) {
if ($imageValue !== null) {
$listImages = $this->getChoiceValue($imageValue, $options);
if (!$listImages) {
$form->get('image')->addError(new FormError(
'Nous n\'avons pas pu trouver l\'image, veuiller choisir une autre'
));
}
} else {
$listImages = array();
}
//die(var_dump($listImages)); //Array of Image
$form->add('image', ChoiceType::class, array(
'attr' => array(
'id' => 'image'),
'expanded' => false,
'multiple' => false,
'choices' => $listImages));
};
$formModifierSubmit = function (FormInterface $form, $imageValue) use ($options) {
if ($imageValue !== null) {
$listImages = $this->getChoiceValue($imageValue, $options);
if (!$listImages) {
$form->get('image')->addError(new FormError(
'Nous n\'avons pas pu trouver l\'image, veuiller choisir une autre'
));
}
} else {
$form->get('image')->addError(new FormError(
'Veuillez choisir une image s.v.p.'
));
}
//die(var_dump($listImages)); //Array of Image object
$config = $form->get('image')->getConfig();
$opts = $config->getOptions();
$chcs = array('choices' => $listImages);
//die(var_dump($chcs)); //output an array with a 'choices' keys with array value
array_replace($opts, $chcs); //not work
//array_merge($opts, $chcs); //not work
//die(var_dump($opts)); //replacements/merge are not made
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be the entity Article
$data = $event->getData();
$formModifier($event->getForm(), $data->getImage());
}
);
//$builder->get('image')->addEventListener( //give error cause the field image don't exist
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($formModifierSubmit) {
$imageVal = $event->getData();
//die(var_dump($imageVal)); //return all the submitted data field in an array
//But when change this event to Submit it return the Article model populated by the submitted data, EXCEPT the image field which have null as value
$formModifierSubmit($event->getForm(), $imageVal['image']);
}
);
}
public function getChoiceValue($imageValue, $options)
{
$listImages = $options['em']->getRepository('AlmotivAppBundle:Image')->findBy(array(
'id' => $imageValue
));
return $listImages; //array of Image object
}
[...]
}
For Info
My image field is not depending on any other field like the doc example, so i need to populate the choices options on PRE_SUBMIT event to give the possible choice.
And also image have a ManyToOne relation in my Article entity
class Article implements HighlightableModelInterface
{
//some properties
/**
* #ORM\ManyToOne(targetEntity="Image\Entity\Path", cascade={"persist"})
* #Assert\Valid()
*/
private $image;
}
If i'm in the bad way let me know cause i'm out of idea now, i try much thing, like
array_replace with the options in the configuration of the field but didn't wrong.
make an ajax request to the url of the form action url : $form.attr('action'), i think it will load the choices option with the possible of <option> but my select is still returned with none <option>.
and much more (can't remmenber).
And also i'm using the v3.1 of the framework with the v4.0.3 of the select2 plugin, if need more info just ask and thx for reading and trying help.
Edit
Just add some info to be more clear
You making things way too complicated. In your documentation example they add eventListener for already existing form field ('sport') and you are adding it to only later added field which does not exist (your 'image' field and 'position' field from the documentation example).
You should use EntityType and if you need (which I'm not if sure you are) filter your images using query_builder option, for validation add constraints (example with controller).
class ArticleType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder
// My other field
$imageFieldFunction = $this->getImageFieldFunction();
$builder->addEventListener(FormEvents::PRE_SET_DATA, $imageFieldFunction);
$builder->addEventListener(FormEvents::PRE_SUBMIT, $imageFieldFunction);
}
private function getImageFieldFunction()
{
return function(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
//when your data_class is Article
$image = $data->getImage();//depending on your Article class
/*if you are using data_class => null
$image = $data['image'];
*/
$imageId = $image ? $image->getId() : 0;
$builder->add('image', EntityType::class , array(
'class' => 'AlmotivAppBundle:Image',
'attr' => array(
'id' => 'image'
) ,
'expanded' => false,
'multiple' => false,
'constraints' => new NotBlank(),
'query_builder' => function (EntityRepository $er) use ($imageId) {
return $er->createQueryBuilder('i')
->where('i.id = :image_id')
->setParameter('image_id', $imageId);
}
));
}
}
}

Symfony form listener

I have form with choice field customer type entity and I want when select some customer in another place(input or label, whatever) change data, I use example from cook book but not work
my form
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customer', 'entity', array(
'class' => Customer::class,
'property' => 'name',
'empty_value' => 'Choice Customer',
'query_builder' => function ($repository) {
/** #var CustomerRepository $repository */
return $repository->getAllQuery();
},
'required' => true
));
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
/** #var Customer $customer */
$customer = $data->getCustomer(); //always have null, why ?
$positions = null === $customer ? '-' : 'its_work';
$form
->add('invoicing_address', 'text', [
'mapped' => false,
'data' => $positions
]);
}
);
/**
* #return string
*/
public function getName()
{
return 'out_bound_invoice';
}
my js ?
var $sport = $('#out_bound_invoice_customer');
// When sport gets selected ...
$sport.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$sport.attr('name')] = $sport.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) { // in html always have empty, like ''
// Replace current position field ...
$('#out_bound_invoice_address').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#out_bound_invoice_address')
);
// Position field now displays the appropriate positions.
}
});
});
when I change customer in js in data have name form and customer name key and data id and then in action in request I see this data
when using event form FormEvents::POST_SUBMIT in $customer I have customer entity
$builder->get('customer')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$customer = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $customer);
}
);
and I want understand why needed PRE_SET_DATA, POST_SET_DATA for this event I have null
my twig
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_label(form.customer, 'customer')}}
{{ form_widget(form.customer, {'attr': {'class': 'select2'}}) }}
{{ form_label(form.invoicing_address)}}
{{ form_widget(form.invoicing_address) }}
{{ form_end(form) }}
why all time I have null in $customer in form listener and how change another field (label or default data, whatever in another field)?

Cannot both disable entity field and retain its value

In a form event, setting a field 'attr' => array('readonly' => 'readonly') is rendered as "disabled" = "1". This is not the desired effect. A disabled select field persists a null value on submit. A readonly field should retain and persist the displayed value. Or so I thought. So how to get the value to remain unchanged and unchangeable?
Edit;
A hidden field does not do the trick. choice_attr does not help either.
I'm voting to close this question. I've not discovered any method for displaying a disabled entity field and also retain the value. If you've got any idea on how that's done...
An example (in Symfony 2.8.3):
The Household entity has six attributes, each of which is an entity in a OneToMany relationship to Household. (The application has other entities which have similar attributes.) The Housing entity/attribute of Household has two properties: housing and enabled. The application's client can set a property to enabled = no if they no longer intend to track that property.
If a property is set to enabled = no its availability in a new or edit Household form is readily eliminated by including a where clause in the entity field's query builder, e.g., ->where("h.enabled=1"). However, doing so causes the disabled property to be set to null. Thus the need for retaining the value somehow.
The ideal solution would be a service for these attribute entity fields that would both display values and retain if enabled is no.
I have tried using an event listener, a hidden field, choice_attr, modifying the form template and the form theme all to no avail. For example, a hidden field is text when an entity field is required. This doesn't mean it can't be done, only that I haven't stumbled on the proper method.
The eventual solution: a service using Doctrine metadata to get disabled entity fields, form class modifications, and, for ManyToMany relationships, some jquery and invisible template entry.
Service functions:
/**
* Create array of disabled fields of an entity object
*
* #param type $object
* #return array
*/
public function getDisabledOptions($object) {
$values = [];
$className = get_class($object);
$metaData = $this->em->getClassMetadata($className);
foreach ($metaData->associationMappings as $field => $mapping) {
if (8 > $mapping['type']) {
$fieldName = ucfirst($field);
$method = 'get' . $fieldName;
if (method_exists($object->$method(), 'getEnabled') && false === $object->$method()->getEnabled()) {
$values[] = $fieldName;
}
}
}
$manyToMany = json_decode($this->getMetaData($object), true);
foreach(array_keys($manyToMany) as $key) {
$values[] = $key;
}
return $values;
}
/**
* Get array of disabled ManyToMany options
*
* #param Object $object
* #return array
*/
public function getMetaData($object) {
$data = array();
$className = get_class($object);
$metaData = $this->em->getClassMetadata($className);
foreach ($metaData->associationMappings as $field => $mapping) {
if (8 === $mapping['type']) {
$data[$field] = $this->extractOptions($object, $field);
}
}
return json_encode($data);
}
Controller use of service:
$searches = $this->get('mana.searches');
$disabledOptions = $searches->getDisabledOptions($household);
$metadata = $searches->getMetadata($household);
...
$form = $this->createForm(HouseholdType::class, $household, $formOptions);
...
return $this->render('Household/edit.html.twig',
array(
'form' => $form->createView(),
....
'metadata' => $metadata,
));
Example of form class field:
->add('housing', EntityType::class,
array(
'class' => 'TruckeeProjectmanaBundle:Housing',
'choice_label' => 'housing',
'placeholder' => '',
'attr' => (in_array('Housing', $options['disabledOptions']) ? ['disabled' => 'disabled'] : []),
'label' => 'Housing: ',
'query_builder' => function (EntityRepository $er) use ($options) {
if (false === in_array('Housing', $options['disabledOptions'])) {
return $er->createQueryBuilder('alias')
->orderBy('alias.housing', 'ASC')
->where('alias.enabled=1');
} else {
return $er->createQueryBuilder('alias')
->orderBy('alias.housing', 'ASC');
}
},
))
...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Truckee\ProjectmanaBundle\Entity\Household',
'required' => false,
'disabledOptions' => [],
));
}
Jquery to remove disabled attribute:
$("input[type=Submit]").click(function () {
$("input").removeAttr("disabled");
$("select").removeAttr("disabled");
});
Example of template & jquery for ManyToMany relationship:
<div id="household_options" style="display:none;">{{ metadata }}</div>
jquery:
if (0 < $("#household_options").length) {
var house_options = JSON.parse($("#household_options").text());
$.each(house_options, function (index, item) {
$.each(item, function (k, v) {
var formAttr = 'household_' + index + '_' + v.id;
$("#" + formAttr).attr('disabled', 'disabled');
});
});
}

Symfony Dynamic Forms with ajax

I am trying to do a form in which I will be able to add as many product options as I want (so collection with prototype). For this I need two fields, one Option Definition and second with Option values.
I have got entities like: Product, Options, Options Definitions and Product Options Join (I decided to use two ManyToOne instead of one ManyToMany, so in Product Definitions Join I got ManyToMany to Options and ManyToMany to Products).
When I am adding option to product, I want first to choose Option Definition and then update select box with Options. This is everything working alright, but the problem is that when I want to edit product then it is showing me default option definitions. For example, when I was creating product I added product with option definition size and the value of size was 40. When I going to edit product I am getting option definition color and size 40.
Here is on pictures what I mean:
And in second row there should be a Size instead of Colour.
Here you can also see how I got connected tables:
ProductOptionsType Class
public function buildForm(FormBuilderInterface $builder, array $options)
{
$factory = $builder->getFormFactory();
$builder
->add('optiondefinitions', 'entity', array(
'mapped' => false,
'class' => 'ePOS\ProductsBundle\Entity\OptionsDefinitions',
'query_builder' => function ($repository) { return $repository->createQueryBuilder('p')->orderBy('p.name', 'ASC'); },
'property' => 'name',
'attr' => array('class' => 'option-definitions')))
;
$refreshOptions = function ($form, $option) use ($factory) {
$form->add($factory->createNamed('options', 'entity', null, array(
'class' => 'ePOS\ProductsBundle\Entity\Options',
'property' => 'value',
'label' => false,
'auto_initialize' => false,
'query_builder' => function ($repository) use ($option) {
$qb = $repository->createQueryBuilder('options')
->innerJoin('options.optionDefinition', 'optionDefinitions');
if($option instanceof OptionsDefinitions) {
$qb = $qb->where('options.optionDefinition = :optiondefinitions')
->setParameter('optiondefinitions', $option);
} elseif(is_numeric($option)) {
$qb = $qb->where('options.id = :option_id')
->setParameter('option_id', $option);
} else {
$qb = $qb->where('options.optionDefinition = 1');
}
return $qb;
}
)));
};
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) use ($refreshOptions, $factory) {
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return ;
}
$refreshOptions($form, $data->getOptions()->getOptionDefinition());
});
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($refreshOptions) {
$form = $event->getForm();
$data = $event->getData();
if($data == null)
$refreshOptions($form, null); //As of beta2, when a form is created setData(null) is called first
if($data instanceof Location) {
$refreshOptions($form, $data->getOptions()->getOptionDefinition());
}
});
$builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshOptions) {
$form = $event->getForm();
$data = $event->getData();
if(array_key_exists('optiondefinitions', $data)) {
$refreshOptions($form, $data['optiondefinitions']);
}
});
}

Categories