I'm trying to create an object of type, say, Slot related to an object of other type, Customer. Here is how the post request body looks like.
{
"name: "foo
"customer": {id: 21}
}
here is how I get the data and build the form:
$data = json_decode($request->getContent(), true);
$entity = new Slot();
$entityManager = $this->getDoctrine()->getManager();
$form = $this->createForm(SlotType::class, $entity);
$form->submit($data, true);
zer
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('customer', EntityType::class, [
"class" => Customer::class
]);
The form I use to submit this data uses an EntityType form type, but it seems it can't recognize the data posted for the customer field and tells me that the value is not valid for this field. Do I need to preprocess the data in the controller to populate my entity with associated entities? Or is there a way to attach dataTransformers to the SlotType to populate the form with valid data ?
You can use a DTO with properties (slotTypeName and customerId), the form will be easier and after the validation you use this DTO to build your entities
Related
I have an email object which contains a collection textareas and has a property template. The collection textareas contains the textareas in template, which are set in the controller.
public function editAction($id, Request $request)
{
// Get the email
$email = EmailQuery::create()->findPk($id);
if (!$email)
{
throw $this->createNotFoundException('Unknown email ID.');
}
// If a new template file is selected, the client refreshes the form with ajax
// So let's get the new template value
if (isset($request->request->get('email')['template']))
{
if (isset($request->request->get('email')['update_with_ajax']))
$email->setTemplate($request->request->get('email')['template']);
}
// Get textareas in the email's template
$email->setTextareas($this->get('email.analyzer')->getTextAreas($email->getTemplate()));
// Create form
$form = $this->createForm('email', $email);
// Handle form
$form->handleRequest($request);
if ($form->isValid() && $form->get('save')->isClicked())
{
$form->save();
}
}
This is my email type (which is a service):
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class EmailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Read template files in dir and store names in $templates
// $templates = array();
$builder->add('template', 'choice', array('choices' => $templates);
$builder->add('textareas', 'collection');
$builder->add('save', 'submit');
$builder->add('update_with_ajax', 'submit');
}
}
The problem here is that I want to update the textareas collection via AJAX when the user chooses another template. As you can see I add the collection of templates to the email object before creating the form, I think that's where it all goes wrong and triggers the This form should not contain extra fields error. But what is the right approach?
I'm working on a form extension that can disable some fields in form types when the underlying data is not new.I do this so I don't have to create seperate forms or event listeners for update/creation forms.i.e: I create the entity creation form and the extension disables the fields signified by an option if the underlying entity is not new.I bound an event listener to the PRE_SET_DATA event as such:
class RemoveFieldsExtension extends AbstractTypeExtension{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (!$options['disable_on_update']) {
return;
}
$isNewCallback = $options['disable_on_update_is_new'];
$em = $this->em;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($em, $isNewCallback, $builder){
if(is_bool($isNewCallback))
return $isNewCallback;
if(is_callable($isNewCallback)){
$isNew = call_user_func($isNewCallback, $event->getData(), $em);
if(!$isNew){//the check for resolving if this is an update form or creation form
// $builder->setDisabled(true);
$form = $event->getForm();
$form->getConfig()->setDisabled(true);
}
}
},
-200
);
}
}
the extension works fine the problem is that the above code will produce 'FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'
it also didn't work when I tried $builder->setDisabled(true);.what can I do to accomplish this?
I have a form with a status select. If a certain status is selected and the form is submitted it should reload and require an additional field.
I have read Dynamic generation for submitted Forms and almost every other post on the internet and about this topic and tried different event combinations (and got different errors) but I still struggle to make this to work correctly.
This is what I have so far:
FormType
private function addProcessAfterField(FormInterface $form)
{
$form->add('processAfterDate', 'date', array('required' => true));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('status', 'entity', array(
'class' => 'Acme\Bundle\ApplicationBundle\Entity\LeadStatusCode',
'choices' => $this->allowedTypes
));
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
$form = $event->getForm();
$data = $event->getData();
if ($data->getStatus()->getId() == LeadStatusCode::INTERESTED_LATER) {
$this->addProcessAfterField($form);
}
});
$builder->get('status')->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event){
$data = $event->getData();
if ($data == LeadStatusCode::INTERESTED_LATER && !$event->getForm()->getParent()->getData()->getProcessAfterDate()) {
$this->addProcessAfterField($event->getForm()->getParent());
}
});
$builder->add('comment', 'textarea', array('mapped' => false));
$builder->add('Update', 'submit');
}
Error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Proxies\__CG__\Acme\Bundle\ApplicationBundle\Entity\Lead::setProcessAfterDate() must be an instance of DateTime, null given, called in /var/www/application.dev/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 360 and defined in /var/www/application.dev/app/cache/dev/doctrine/orm/Proxies/__CG__AcmeBundleApplicationBundleEntityLead.php line 447
As already mentioned I tried different event combinations, one was almost working but then the date was never persisted to the entity so I added the \DateTime type-hint to the setProcessAfterDate() method. I am not sure if I don`t understand the event system correctly or if the error lies somewhere else.
Well, it might not be the best way to solve it, but to make long story short:
$form->handleRequest($request);
if($form->isValid()) // check if the basic version of the form is ok
{
$form = $this->createForm(new XXXXForm(), $form->getData()); // you recreate the form with the data that was submitted, so you rebuild the form with new data
if($form->isValid())
{
// ok
}
// not ok
}
Then inside buildForm function, you base the "required" attribute value of fields based on what you want:
'required' => $this->getCheckRequired($options)
private function getCheckRequired($options) // checks whether field should be required based on data bound to the form
{
if($options && isset($options['data'])
{
switch $options['data']->getStatus():
// whatever
;
}
return false;
}
As I said, this is not the best solution, and it doesn't fix your approach, but rather proposes a different one, but it does the job
I have a form with one default value:
class GearType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('options')
->add('model', 'choice', array('choices' => $this->getModelChoices(), 'data' => 2));
}
one of the requirements is form can be pre-populated by re-sellers by passing parameters in URL. It is also nice feature for potential customers to copy and paste link to email, communicators, etc.
I did it this way:
/**
* #Route("/car/gear")
* #Template()
*/
public function gearAction(Request $request)
{
$form = $this->createForm(new GearType());
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
return 'is valid';
}
} else {
$get = $this->getRequest()->query->all();
if (!empty($get)) {
$normalizer = new GetSetMethodNormalizer();
$form->setData($normalizer->denormalize($get, new Gear())); # look here
}
}
return array('form' => $form->createView());
}
unfortunately field 'options' has always default value, instead value passed as a parameter.
I have tried to change line # look here into
$gear = $normalizer->denormalize($get, new Gear());
$form = $this->createForm(new GearType(), $gear);
but no result.
It seems that solution is passing additional parameter to GearType object. I do not like this solution. Does anyone know better way?
Add this snippet, and modifiy between the [ ] as appropriate
$form->bind($request);
if ( [ passed parameters from querystring ] ){ //// New Code
$form->getData()->setOptions( [ processed parameter ]); //// New Code
} //// New Code
if ($form->isValid()) {
return 'is valid';
}
The reason for the field options always having default value may be the actual query. Instead of denormalizing and setting the data directly, modify else fragment to:
} else {
$form = $this->createForm(new GearType(), new Gear(), array(
'validation_groups' => array('not-validating')
));
$form->bind($request);
}
The form will validate only against validations associated with the not-validating group, which will avoid showing the common required alerts if the form is built form GET.
Docs about 'validations-groups': http://symfony.com/doc/current/book/forms.html#validation-groups
The question is similar to: Entity form field and validation in Symfony2?
I have a form for my entity called Book and I have a type to display a form in my view. In this type I have some fields that are mapped to properties in my entity.
Now I want to add another field which is not mapped in my entity and supply some initial data for that field during form creation.
My Type looks like this
// BookBundle\Type\Book
public function buildForm(FormBuilderInterface $builder, array $options = null)
{
$builder->add('title');
$builder->add('another_field', null, array(
'mapped' => false
));
}
The form is created like this
$book = $repository->find(1);
$form = $this->createForm(new BookType(), $book);
How can I supply some initial data now during form creation? Or how do I have to change that creation of the form to add initial data to the another_field field?
I also have a form that has fields that mostly match a previously defined entity, but one of the form fields has mapped set to false.
To get around this in the controller, you can give it some initial data pretty easily like this:
$product = new Product(); // or load with Doctrine/Propel
$initialData = "John Doe, this field is not actually mapped to Product";
$form = $this->createForm(new ProductType(), $product);
$form->get('nonMappedField')->setData($initialData);
simple as that. Then when you're processing the form data to get ready to save it, you can access the non-mapped data with:
$form->get('nonMappedField')->getData();
One suggestion might be to add a constructor argument (or setter) on your BookType that includes the "another_field" data, and in the add arguments, set the 'data' parameter:
class BookType
{
private $anotherFieldValue;
public function __construct($anotherFieldValue)
{
$this->anotherFieldValue = $anotherFieldValue;
}
public function buildForm(FormBuilderInterface $builder, array $options = null)
{
$builder->add('another_field', 'hidden', array(
'property_path' => false,
'data' => $this->anotherFieldValue
));
}
}
Then construct:
$this->createForm(new BookType('blahblah'), $book);
You can change the request parameters like this to support the form with additional data:
$type = new BookType();
$data = $this->getRequest()->request->get($type->getName());
$data = array_merge($data, array(
'additional_field' => 'value'
));
$this->getRequest()->request->set($type->getName(), $data);
This way your form will fill in the correct values for your field at rendering. If you want to supply many fields this may be an option.