I have been experiencing problems with embedding a controller that creates a form where you can upload files. When the controller is rendered in certain parts of the twig file, I get this error:
An exception has been thrown during the rendering of a template ("Expected argument of type "Symfony\Component\HttpFoundation\File\UploadedFile", "string" given").
This is strange since in other parts of the same twig file, the expected argument is given without problems. The problem seems to be another form in the same twig file that doesn't play nice with my embedded controller form.
The part that seems to cause the problem:
<div id="payment_checkout_form">
{% if cId and shippingRegionId %}
{% set savedPath =path('cart_set_shipping', {'store_id': webstore.id, 'shippingRegion': shippingRegionId,'cId':cId}) %}
{{ form_start(form, {'attr': {'id': 'form_checkout','data-url':savedPath}}) }}
{% else %}
{{ form_start(form, {'attr': {'id': 'form_checkout'}}) }}
{% endif %}
{{ render(url('passport')) }}
Relevent part of my PassportType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', 'file', array('label' => false) , [
'multiple' => true,
'label' => '',
'attr' => [
'accept' => 'image/*',
'multiple' => 'multiple'
]
]
)
->add('confirm', 'submit');
}
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Passport',
));
}
Relevent part of my Passport entity:
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* Sets file.
*
* #param Symfony\Component\HttpFoundation\File\UploadedFile $file
*/
public function setFile(UploadedFile $file = null) {
$this->file = $file;
}
Relevent part of my Passport controller
/**
* #Route("/passport", name="passport")
*/
public function createPassportAction(Request $request)
{
$request = $this->get('request_stack')->getMasterRequest();
$passport = new Passport();
$passport->setName('default');
$form = $this->createForm(new PassportType(), $passport);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$files = $request->files->get('passportPhoto');
if (!empty($files)) {
$this->uploadFile($files);
}
}
return $this->render('passport.html.twig', [
'form' => $form->createView(),
'isFormSubmitted' => $form->isSubmitted(),
'passportImages' => $this->getDoctrine()->getRepository('AppBundle\Entity\Passport')->findAll(),
]);
}
{{ render(url('passport')) }} is the embedded controller that renders the file upload form. If I put the{{ render(url('passport')) }} above the form_start of the other form everything works.
Answering my own question:
embedding a form inside another form like I'm trying to do in the question by using render is not possible. I fixed my problem by first removing the render call of my embedded passport form and making my passport type a sub type of the type that is used in the checkout form like this:
public function buildForm(FormBuilderInterface $builder, array $options){
$builder
...
->add('passport', new PassportType(), array(
'required' => true
))
...
}
I still wanted to have the controller of the passport part of my form to be in it's own file. To achieve this I called my passport controller inside of the checkout controller using the forward method like this:
$files = $request->files->get('order')['passport_id'];
$store_id = $request->attributes->get('store_id');
$this->forward('AppBundle\Controller\PassportController::uploadFile',
[ 'files' => $files, 'store_id' => $store_id ]);
Related
I am trying to "Update field dynamicly based on the user choice in another field in Symfony 5" from "https://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data" this side. But i faced this exceptions/error "Argument 2 passed to App\Form\SportMeetupType::App\Form{closure}() must be an instance of App\Form\Sport or null, instance of App\Entity\Sport given, called in C:\Apache24\htdocs\dynamicform\src\Form\SportMeetupType.php on line 58" when ajax called.
My Code:
Form :
class SportMeetupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('sport', EntityType::class, [
'class' => 'App\Entity\Sport',
'placeholder' => '',
])
;
$formModifier = function (FormInterface $form, Sport $sport = null) {
$positions = null === $sport ? [] : $sport->getPositions();
$form->add('position', EntityType::class, [
'class' => 'App\Entity\Position',
'placeholder' => '',
'choices' => $positions,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
$formModifier($event->getForm(), $data->getSport());
}
);
$builder->get('sport')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$sport = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $sport); // Here line 58 and error show this line
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SportMeetup::class,
]);
}
}
Controller:
/**
* #Route("/new", name="sport_meetup_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$sportMeetup = new SportMeetup();
$form = $this->createForm(SportMeetupType::class, $sportMeetup);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($sportMeetup);
$entityManager->flush();
return $this->redirectToRoute('sport_meetup_index');
}
return $this->render('sport_meetup/new.html.twig', [
'sport_meetup' => $sportMeetup,
'form' => $form->createView(),
]);
}
Twig/View:
{% extends 'base.html.twig' %}
{% block title %}New SportMeetup{% endblock %}
{% block body %}
<h1>Create new SportMeetup</h1>
{{ form_start(form) }}
{{ form_row(form.name)}}
{{ form_row(form.sport) }} {# <select id="sport_meetup_sport" ... #}
{{ form_row(form.position) }} {# <select id="sport_meetup_position" ... #}
{# ... #}
{{ form_end(form) }}
{% endblock %}
JavaScript code:
var $sport = $('#sport_meetup_sport');
// 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: "POST",
data : data,
success: function(html) {
// Replace current position field ...
$('#sport_meetup_position').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#sport_meetup_position')
);
// Position field now displays the appropriate positions.
}
});
});
When changed Sport dropdown value then ajax called and show below exceptions/error
"Argument 2 passed to App\Form\SportMeetupType::App\Form{closure}() must be an instance of App\Form\Sport or null, instance of App\Entity\Sport given, called in C:\Apache24\htdocs\dynamicform\src\Form\SportMeetupType.php on line 58"
Please help me....
Make sure that in your SportMeetupType.php you have:
use App\Entity\Sport;
I think you are using wrong class.
How to set value in select on twig?
When I submit my form one of the value is null and when dump in the controller show me null.
$builder
->add('date', null, [
'widget' => 'single_text'
])
->add('hairstyle',EntityType::class,[
'class' => Hairstyle::class,
'mapped' => false,
])
;
I add in twig this:
{{ form_start(form) }}
{{ form_widget(form.hairstyle, {value: Setcountry ~ "" } ) }}
{{ form_errors(form) }}
{{ form_end(form) }}
This is my var_dump in controller:
private 'normData' => object(BeautySalonBundle\Entity\Reservation)[824]
private 'id' => null
private 'date' => object(DateTime)[1034]
public 'date' => string '2020-02-21 21:21:00.000000' (length=26)
public 'timezone_type' => int 3
private 'hairstyle' => null
private 'user' => null
private 'viewData' =>
You can leverage the empty_data option to provide a default entity for your EntityType select-field of type Hairstyle.
Inject the repository into your form type
Use the repository in a closure for the empty_data option
Fetch and return the default entity from the repository inside the closure
class YourFormType extends AbstractType
{
private const DEFAULT_HAIRSTYLE_ID = 1;
/** #var HairstyleRepository */
private $hairstyleRepository;
public function __construct(HairstyleRepository $hairstyleRepository)
{
$this->hairstyleRepository = $hairstyleRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// create an intermediate variable for use in the closure
$hairstyleRepository = $this->hairstyleRepository;
$builder
// ..
->add('hairstyle', EntityType::class, [
'class' => Hairstyle::class,
// return a default Hairstyle entity
'empty_data' => function(FormInterface $form) use ($hairstyleRepository) {
return $hairstyleRepository->getOneById(self::DEFAULT_HAIRSTYLE_ID);
},
])
}
I've worked through a few of the Forms-Tutorials on the Symfony-Page (especially How to Embed a Collection of Forms, How To use a Form without a Dataclass & CollectionType Field ).
I'm trying to show a form with multiple lead partners which can be edited and submitted back to the system.
But i get a Twig_Runtime_Error saying: ''Variable "lead_partners" does not exist''.
My Twig:
{% block content %}
<div>
{{ form_start(form) }}
{% for partner in lead_partners %}
{{ form_row(partner.name) }}
{% endfor %}
{{ form_end(form) }}
</div>
{% endblock content %}
My Controller Code:
public function overview(Request $request, \App\Utility\LeadPartnerLoader $LeadPartnerLoader)
{
$leadPartnerList = $LeadPartnerLoader->loadAll();
$form = $this->createFormBuilder($leadPartnerList)
->add('lead_partners', CollectionType::class, [
'entry_type' => LeadPartnerFormType::class,
])->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$data = $form->getData();
}
return $this->render(
'lead_partner_overview2.html.twig',
[
'form' => $form->createView()
]);
}
And the Form Type (LeadPartnerFormType):
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => LeadPartner::class,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', HiddenType::class)
->add('name', TextType::class);
}
$leadPartnerList is of type array.
What am i doing wrong/missing here?
Kind Regards
It seems your action overview soesn't return the lead_partners variable you use in your template.
You can try to do this
return $this->render(
'lead_partner_overview2.html.twig',
[
'form' => $form->createView(),
'lead_partners' => $leadPartnerList, // I gess that's the list you want to loop ?
]);
I have a variety of choice fields in a form I'm creating, but all of them are loaded from PHP arrays that are defined in code. createForm() is given an empty model. Each field, when rendered with form_row() in twig tacks on about 2 seconds to render, each, making the request take about 8 or 9 seconds, which is ridiculous. I've tried searching, reading, etc. and from what I can tell I am following best practices. There are no database queries being run. Please help me nail down this huge performance problem.
Let's start with the form type:
class SubscriptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('latitude', 'hidden')
->add('longitude', 'hidden')
->add('crop', 'choice', [
'choices' => Crop::getFormChoices(),
'required' => true,
])
->add('infliction', 'choice', [
'choices' => Infliction::getFormChoices(),
'required' => true,
])
->add('emergenceDate', 'date')
->add('threshold', 'choice', [
'choices' => Threshold::getFormChoices(),
'label' => 'Severity threshold',
'required' => true,
])
->add('save', 'submit', ['label' => 'Subscribe']);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => 'PlantPath\Bundle\VDIFNBundle\Entity\Subscription',
]);
}
public function getName()
{
return 'subscription';
}
}
Then, in my controller:
public function formAction(Request $request)
{
$subscription = new Subscription();
$form = $this->createForm(new SubscriptionType(), $subscription);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// ...
}
return $this->render('PlantPathVDIFNBundle:Subscription:form.html.twig', [
'form' => $form->createView(),
]);
}
Subscription/form.html.twig:
{{ form_start(form, {'attr': {'id': 'subscription-form'}}) }}
{{ form_row(form.crop) }}
{{ form_row(form.infliction) }}
{{ form_row(form.emergenceDate) }}
{{ form_row(form.threshold) }}
{{ form_end(form) }}
As I mentioned, each call to form_row() takes a good two seconds. If I take out all the form_row()s and the form_end(), the template is rendered (without a form) in milliseconds. I cannot fathom why Symfony needs over 8 seconds to render several lines of HTML for a blank form.
The blocks for rendering the choice fields in bootstrap_3_layout.html.twig contains '|trans' filter which tries to find translation for each option.
Override choice_widget_options block in your twig file, if you do not wish to translate. This should eliminate the warning messages caused due to translation.
i need your help please , I want to display my created form in Symfony2. I want to display my created form 92 times becouse i have 92 numbers in my database(every number is a form) , i didn't know how to do it here is my code:
controller:
class DefaultController extends Controller
{
public function QuestionsAction(Request $request)
{
$questions = $this->getDoctrine()->getEntityManager()
->getRepository('Tests\TestsPhpBundle\Entity\Question')
->findAll();
$task = new Question();
$forms = $this->createForm(new QuestionType(), $task);
if ($request->getMethod() == 'POST') {
$forms->bindRequest($request);
if ($forms->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($task);
$em->flush();
}
}
{
return $this->render('TestsTestsPhpBundle:Default:index.html.twig', array(
'questions' => $questions,
'forms' => $forms->createView()
));
}
}
}
my form file:
class QuestionType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('categories', null, array('required' => false,
))
->add('text', 'entity', array(
'class' => 'TestsTestsPhpBundle:Question',
'query_builder' => function($repository) {
return $repository->createQueryBuilder('p')->orderBy('p.id', 'ASC'); },
'property' => 'text'))
;
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Tests\TestsPhpBundle\Entity\Question',);
}
public function getName()
{
return 'question';
}
}
my twig file:
{% block content %}
<h2>Questions</h2>
{% for question in questions %}
<dl>
<dt>Number</dt>
<dd>{{ question.number }}<dd>
{% for form in forms %}
{{ form_row(forms.categories) }}
{{ form_row(forms.text) }}
</dl>
{% endfor %}
<hr />
{% endfor %}
{% endblock %}
I recommend to read capter: Embedding Controller
http://symfony.com/doc/2.0/book/templating.html
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
</div>
You can make a for loop within Twig Template and call an action (with parameter if needed) where you render the form. -> QuestionsAction in your case.