I was posting a Symfony form via AJAX post and after I made a field edit on my "Siparis" entity and related form field in "SiparisType" I keep getting 419 on post.
Symfony version: 6.0.2 - PHP version:8.1.2 Database: 10.4.22-MariaDB
I tried to disable csrf protection and still getting the same status 419 and nothing else.
Symfony route to handle AJAX post(I reverted my latest changes after editing the field below version was working fine earlier):
#[Route('/save/', name: 'save', methods: ['POST'])]
public function save_siparis(Request $request, EntityManagerInterface $entityManager): JsonResponse
{
$siparis = new Siparis();
try {
$form = $this->createForm(SiparisType::class, $siparis);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($siparis);
$entityManager->flush();
return new JsonResponse(['result' => 'OK', 'siparis_id' => $siparis->getId()]);
}
}catch(\Exception $e){
error_log($e->getMessage());
return new JsonResponse(array('result' => $e->getMessage()), 419);
}
}
Render Form route on Controller:
Only change is: 'firma' => $id option for form in this section
#[Route('/new/{id}', name: 'new', methods: ['GET'])]
public function new($id, Request $request, EntityManagerInterface $entityManager, SiparisRepository $siparisRepository,FirmaRepository $firmaRepository, IlgiliRepository $ilgiliRepository): Response
{
$siparis = new Siparis();
$latest_siparis = $siparisRepository->findBy(['Musteri' => $id], ['id' => 'desc', ]);
$siparis_id = 1;
if(!is_null($latest_siparis))
$siparis_id = count($latest_siparis) + 1;
$musteri = $firmaRepository->find($id);
$siparisNo = 'TST-'.str_replace(' ', '-',$musteri->getKod()).'-'.str_pad($siparis_id, 3, '0', STR_PAD_LEFT);
$form = $this->createForm(SiparisType::class, $siparis, ['firma' => $id,'attr' => ['id' => 'siparis_form']]);
$firmalar = $firmaRepository->findBy(['Type' => 0]);
$maliyet = new Maliyetler();
$maliyet_form = $this->createForm(MaliyetlerType::class, $maliyet, ['attr' => ['id' => 'maliyet_form']]);
return $this->renderForm('siparis/new.html.twig', [
'siparisNo' => $siparisNo,
'sipari' => $siparis,
'form' => $form,
'maliyet_form' => $maliyet_form,
'satici_firmalar' => $firmalar,
'musteri' => $musteri,
'mode' =>'NEW'
]);
}
My FormType:
The field: 'siparis_veren' was a TextType and i have changed this one as a EntityType and fetched the related entities via query builder
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$firma = $options['firma'];
$SIPARIS_CHOICES = ['Devam Ediyor' => '1','Tamamlandı' => '2','İptal' => '3'];
$builder
->add('teslim_tarihi', DateType::class, [
'widget' => 'single_text',
'label' => 'Teslim Tarihi', 'attr' =>['class' => 'form-control', 'placeholder' => 'Teslim Tarihi']
])
->add('siparis_tarihi', DateType::class, [
'widget' => 'single_text',
'label' => 'Sipariş Tarihi', 'attr' =>['class' => 'form-control', 'placeholder' => 'Sipariş Tarihi']
])
->add('siparis_veren', EntityType::class,['class' => Ilgili::class,
'choice_label' => 'full_name',
'query_builder' => function (EntityRepository $er) use ($firma) {
return $er->createQueryBuilder('ilgili')
->andWhere('ilgili.firma = :firma')
->setParameter('firma', $firma);
},
'label' => 'Siparişi Veren', 'attr' =>['class' => 'form-control', 'placeholder' => 'Siparişi Veren']])
->add('siparis_durum', ChoiceType::class,['choices' => $SIPARIS_CHOICES,'attr' =>['class' => 'form-control', 'placeholder' => 'Siparişi Alan'] ])
->add('siparis_turu', HiddenType::class)
->add('siparis_genel_aciklama', TextType::class,['label' => 'Sipariş Genel Açıklaması', 'attr' =>['class' => 'form-control', 'placeholder' => 'Sipariş Genel Açıklaması']])
->add('siparis_satir_aciklama', HiddenType::class)
->add('siparis_miktari', HiddenType::class)
->add('siparis_fiyati', HiddenType::class)
->add('fatura_durumu', HiddenType::class)
->add('siparisNo', HiddenType::class)
->add('siparis_kdv_orani', HiddenType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Siparis::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
]);
$resolver->setRequired(['firma']);
}
My AJAX form post on client side(No change here):
$('form[name="siparis"]').submit(function(e) {
e.preventDefault();
var url = "{{ path('siparis_save') }}";
var formSerialize = $(this).serialize();
$.post(url, formSerialize, function(response) {
if(response.result === "OK")
{
$( 'form[name="maliyetler"]' ).submit();
}
else
{
console.log(response.result);
}
}, 'JSON');
});
In my Siparis Entity siparis_veren was a text field and i added a ManyToOne relation with Ilgili class to it and after my changes post keep giving me 419 error.
#[ORM\ManyToOne(targetEntity: Ilgili::class, inversedBy: 'siparisler')]
#[ORM\JoinColumn(nullable: false)]
private $siparis_veren;
I tried clearing cache, removed related siparis_veren field from form type and controller but result is same unless I revert siparis_veren to TextType as it was before.
Sorry if my explanation is not enough, I couldn't find any results regarding this issue and SO is my only choice, I am a newbie in Symfony.
Thank you.
Okay found the issue, I didn't notice that I set status to 419 on Try Catch catch block so thought it was about Symfony or client side. After doing some debugging I get "An error has occurred resolving the options of the form "App\Form\SiparisType": The required option "firma" is missing." error which is caused by
SiparisConroller.php
$form = $this->createForm(SiparisType::class, $siparis);
This line in save_siparis() function. In SiparisType I have set 'firma' field as required
SiparisType.php
$resolver->setRequired(['firma']);
Therefore I got an error in Try Catch block.
Thanks to #jean-max for his advice to check Try Catch.
Related
for a few days I am blocked, if someone can help me with the good practice, I explain:
I want to retrieve data from the database: entity (expression_besoin) contains the information of a purchase with ref , requester ... and the entity besoin_article contains the items products in the previous entity, there is a OneToMany relationship between the two entities.
the objective is to retrieve the several expression needs of articles in a form in order to modify whether the quantities are necessary or to delete them, then to be able to generate a PDF file which contains all the needs and the articles which correspond to them.
Controller :
/**
* #Route("/extraire_commandes", name="export_commandes", methods={"GET","POST"})
*/
public function exportCommandes(ExBesoinRepository $exBesoinRepository, Request $request): Response
{
$datas = new FiltreBesoin();
$form = $this->createForm(FiltreBesoinType::class, $datas);
$form->handleRequest($request);
$exBesoin = $exBesoinRepository->exportCommande($datas);
$valueFournisseur = $form['fournisseur']->getData();
$formExport = $this->createFormBuilder(array('besoin' => $exBesoin));
$formExport->add('besoin', CollectionType::class, array(
'entry_type' => ExBesoinToCommandeType::class,
));
$formExport = $formExport->getForm();
$formExport->handleRequest($request);
$formValue = $formExport->getData();
if ($formExport->isSubmitted() && $formExport->isValid()) {
$html = $this->renderView('admin/besoins/epicerie/exportPdf.html.twig', [
'besoins' => $formValue,
]);
$html .= '<link rel="stylesheet" href="/build/css/app.css"> ';
$html .= '<script src="/build/vendors~js/app.js"></script><script src="/build/runtime.js"></script><script src="/build/vendors-node_modules_popperjs_core_lib_index_js-node_modules_symfony_stimulus-bridge_dist_ind-f4bfca.js"></script>';
$name = 'test';
$options = new Options();
$options->set('isHtml5ParserEnabled', true);
$options->set('isRemoteEnabled', true);
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html);
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'portrait');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
$dompdf->stream($name, array('Attachment' => 0));
return new Response('', 200, [
'Content-Type' => 'application/pdf',
]);
}
return $this->render('admin/besoins/epicerie/export.html.twig', [
'besoins' => $exBesoin,
'form' => $form->createView(),
'valueFournisseur' => $valueFournisseur,
//'idBesoin' => $idBesoin,
'formExport' => $formExport->createView(),
'valu'=> $formValue
]);
}
FormType
class ExBesoinToCommandeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('besoinArs', CollectionType::class, [
'entry_type' => BesoinArToCommandeType::class,
'allow_delete' => true,
//'allow_add' => false,
'prototype' => true,
'by_reference' => false,
'delete_empty' => true,
'entry_options' => [
'label' => true,
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ExBesoin::class,
]);
}
public function getBlockPrefix()
{
return 'ExBesoinToCommandeType';
}
}
class BesoinArToCommandeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('article', EntityType::class, [
'class' => Article::class,
'label' => false,
'attr' =>[
'class' => 'arSelect form-control-sm',
'data-live-search' => 'true',
],
'placeholder' => 'Selectionner un article ',
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('a')
->innerJoin('a.category', 'c')
->where('c.is_legume = 0')
;
return $qb;
},
'group_by' => function (Article $article) {
return $article->getCategory()->getLibele();
}
])
->add('quantity', TextType::class, [
'label' => ' ',
'attr' =>[
'class' => 'quantity form-control-sm'
]
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => BesoinAr::class,
]);
}
public function getBlockPrefix()
{
return 'BesoinArToCommandeType';
}
}
Until I get my data back without problem, the getData() of the form contains all my data before the submit, but when I validate to generate my pdf file, it does not contain all the data. I use a filter system that allows me to select the expressions of need between two dates, by requester or by supplier, and the problem is linked to this filter because when I generate the pdf of the data without filter it works but when I filter my data this is where my data changes and I don't recover the filtered data, I will be grateful if someone unblocks me or tell me where the problem comes from.
In my form user must choose an option to then select users based on it.
I'm getting an error every time im trying to submit my form.
The CSRF token is invalid. Please try to resubmit the form.
I tried to user {{ form_row(form._token) }}, but it doesnt work.
Symfony tell me that the value of the csrf token is empty.
My view :
<div class="card">
<div class="card-body">
{{ form_start(form) }}
{{ form_rest(form) }}
<button class="btn btn-info">Envoyer</button>
{{ form_end(form) }}
</div>
</div>
<script>
$(document).on('change', '#bsv_send_cultures', function () {
let $field = $(this)
let $form = $field.closest('form')
let data = {}
data[$field.attr('name')] = $field.val()
$.post($form.attr('action'), data).then(function (data) {
let $input = $(data).find('#bsv_send_user')
$('#bsv_send_user').replaceWith( $input )
$('#bsv_send_user').append( "<input id=\"selectAll\" type=\"checkbox\"><label for='selectAll'>Sélectioner tous</label>" )
$("#selectAll").click(function(){
$("input[type=checkbox]").prop('checked', $(this).prop('checked'));
});
})
})
</script>
My form builder
class BsvSendType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('cultures', EntityType::class, [
'class' => IndexCultures::class,
'choice_label' => 'name',
'mapped' => false,
'required' => false,
'placeholder' => 'Sélectionnez une culture',
'attr' => [
'class' => 'select2'
]
])
->add('display_at', DateType::class, [
'widget' => 'single_text',
'html5' => false,
'mapped' => false,
'required' => false,
'attr' => [
'class' => 'js-datepicker',
'autocomplete' => 'off'
],
'label' => 'Date d\'envoi',
'help' => 'Remplir uniquement en cas d\'envoi différé.'
])
;
$builder->get( 'cultures')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) {
$form = $event->getForm();
$this->addUserField( $form->getParent(), $form->getData());
}
);
$builder->addEventListener(
FormEvents::POST_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
$this->addUserField( $form, null );
}
);
}
/**
* #param FormInterface $form
* #param IndexCultures|null $indexCultures
*/
private function addUserField(FormInterface $form, ?IndexCultures $indexCultures)
{
if (is_null($indexCultures)) {
$form->add('user', EntityType::class, [
'class' => Users::class,
'mapped' => false,
'choices' => [],
'required' => false,
'placeholder' => 'Selectionner une culture avant de choisir un utilisateur'
]);
} else {
$form->add('user', EntityType::class, [
'class' => Users::class,
'choice_label' => function(Users $user) {
return $user->getIdentity();
},
'query_builder' => function (UsersRepository $er) use ( $indexCultures ) {
return $er->createQueryBuilder('u')
->leftJoin( Exploitation::class, 'e', 'WITH', 'u.id = e.users')
->leftJoin(Ilots::class, 'i', 'WITH', 'e.id = i.exploitation')
->leftJoin(Cultures::class, 'c', 'WITH', 'i.id = c.ilot')
->leftJoin(IndexCultures::class, 'ic', 'WITH','c.name = ic.id')
->andWhere('ic.id = :indexC')
->setParameter('indexC', $indexCultures->getId());
},
'mapped' => false,
'expanded' => true,
'multiple' => true
]);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => BsvUsers::class,
'translation_domain' => 'forms'
]);
}
My Controller
/**
* #Route("/admin/bsv/send/{id}", name="admin.bsv.send", methods="GET|POST")
* #param Bsv $bsv
* #param Request $request
* #return Response
* #throws \Exception
*/
public function send(Bsv $bsv, Request $request): Response
{
$bsvUsers = new BsvUsers();
$form = $this->createForm(BsvSendType::class, $bsvUsers);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->all();
$customers = $data['user']->getData();
$displayAt = $data['display_at']->getData();
//-- Init
$datetime = New \DateTime();
//-- Update BSV info
$bsv->setSendDate( $datetime );
//-- Create relation
foreach ($customers as $customer) {
$relation = new BsvUsers();
$this->em->persist($relation);
$relation->setBsv($bsv);
$relation->setCustomers($customer);
$relation->setChecked(0);
if ( $displayAt !== null ) {
$displayAt->setTime(8,00);
$relation->setDisplayAt($displayAt);
} else {
$relation->setDisplayAt($datetime);
}
}
$this->em->flush();
$this->addFlash('success', 'BSV envoyé avec succès');
return $this->redirectToRoute('admin.bsv.index');
}
return $this->render('admin/bsv/send.html.twig', [
'bsv' => $bsv,
'form' => $form->createView()
]);
}
It looks like you are sending your form without the _token field, look at your JS:
...
let data = {}
data[$field.attr('name')] = $field.val()
$.post($form.attr('action'), data).then(...)
...
Your data object contains only #bsv_send_cultures input value before being sent to the server, just add the #bsv_send__token input value to this object before sending it from your script.
I create 2 forms not linked to any entity in the same controller.
Each form have it owns submitted button.
I never goes to the submitted function of the second form.
I think it is because the 2 forms have same default name 'form'.
The problem is how to change the form name?
Below what I did
public function index(Request $request)
{
$form1 = $this->createFormBuilder()
->add('sn', TextType::class, [
'required' => false,
])
->add('search', SubmitType::class, ['label' => 'Search'])
->getform();
$form1->handleRequest($request);
if ($form1->isSubmitted() && $form1->isValid()) {
//Do something
}
$form2 = $this->createFormBuilder();
$form2->add('Agree', CheckboxType::class, [
'label' => 'Agree',
'required' => false,
]);
$form2->add('detail', SubmitType::class, ['label' => 'Detail']);
$form2 = $form2->getForm();
$form2->handleRequest($request);
if ($form2->isSubmitted() && $form2->isValid()) {
//Do something else
}
return $this->render('search/index.html.twig', [
'form1' => $form1->createView(),
'form2' => $form2->createView(),
]);
}
If you want to modify the form name, use the createNamed() method:
$form1 = $this
->get('form.factory')
->createNamed('my_name', TextType::class, $task);
You can even suppress the name completely by setting it to an empty string.
i need some help.
I have a Form where i would like to either choose an existing Entity or submit a new one. So i have a Class Dolmetscher (Interpreter for languages) with title, name, surname and language. To create the Form i have a Class InterpreterType with the function
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('anrede', ChoiceType::class, array(
'choices' => array(
'Herr' => 'Herr',
'Frau' => 'Frau'
)
))
->add('vorname')
->add('nachname')
->add('sprache')
->add('dolmetscher', EntityType::class, array(
'class' => 'AppBundle:Dolmetscher',
'placeholder' => 'Dolmetscher wählen',
'label' => 'Dolmetscher',
'choice_value' => 'id',
'choice_label' => function ($dolmetscher) {
return $dolmetscher->getAnrede() . ' ' .
$dolmetscher->getVorname() . ' ' .
$dolmetscher->getNachname();
},
'mapped' => false,
))
->add('select', SubmitType::class, array(
'label' => 'Übernehmen',
'attr' => array(
'class' => 'btn btn-default',
'formnovalidate' => 'formnovalidate'
)
))
->add('save', SubmitType::class, array(
'label' => 'OK',
'attr' => array(
'style' => 'float: right',
'class' => 'btn btn-default'
)
))
->add('reset', SubmitType::class, array(
'label' => 'Zurücksetzen',
'attr' => array(
'style' => 'float: right; margin-right: 10px',
'class' => 'btn btn-warning',
'formnovalidate' => 'formnovalidate'
)
));
}
So i have a selection with Entities, which is working, with a 'select' Button and Form fields for a new Dolmetscher with a 'save' Button. Also a 'reset' Button
My Controller Class looks like
/**
* #Route("/u01/5", name="u1_5")
*/
public function dolmetscherAction(Request $request) {
$session = $this->get("session");
var_dump($session->get("foo"));
if (!$session->get("dolmetscher")) {
$dolmetscher = new Dolmetscher();
} else {
$dolmetscher = $session->get("dolmetscher");
}
$dolmetscherForm = $this->createForm(DolmetscherType::class, $dolmetscher);
$dolmetscherForm->handleRequest($request);
if ($dolmetscherForm->get('select')->isClicked()) {
$dolmetscher = $dolmetscherForm->get('dolmetscher');
$session->set("dolmetscher", $dolmetscher);
return $this->redirectToRoute('u1_5');
}
if ($dolmetscherForm->get('reset')->isClicked()) {
$dolmetscher = new Dolmetscher();
$session->set("dolmetscher", $dolmetscher);
return $this->redirectToRoute('u1_5');
}
if ($dolmetscherForm->get('save')->isClicked() && $dolmetscherForm->isSubmitted() && $dolmetscherForm->isValid()) {
$dolmetscher = $dolmetscherForm->getData();
$session->set("dolmetscher", $dolmetscher);
return $this->redirectToRoute('homepage');
}
return $this->render('urkunden/u01/5.html.twig', [
'form' => $dolmetscherForm->createView(),
'page_title' => 'U01'
]);
}
I want to put the Dolmetscher from the selection into $_SET for later use ,e.g. persist in DB, which works fine for a new Dolmetscher but not for my selection. I get an Exception
Serialization of 'Closure' is not allowed
I'm not sure if I'm doing this right at all (I have some OneToMany Relations and wanted to have a view for each Entity/Form and persist everything at once at the end so that i don't have only a Dolmetscher in my DB when the user quits in mid process)
I also thought it might be possible to populate the Form fields from the selection which I couldn't get to work. Can someone please help me, i would appreciate it.
This part of code is probably the origin of your problems :
if ($dolmetscherForm->get('select')->isClicked()) {
$dolmetscher = $dolmetscherForm->get('dolmetscher'); <------ this one
$session->set("dolmetscher", $dolmetscher);
return $this->redirectToRoute('u1_5');
}
you are trying to serialize a form object which contains a closure. Closure can not be serialized ( visit this link for more insights Exception: Serialization of 'Closure' is not allowed )
If you dump $dolmetscher variable you will probably get a form object not the entity you want. try to replace the line :
$dolmetscher = $dolmetscherForm->get('dolmetscher');
with :
$dolmetscher = $dolmetscherForm->get('dolmetscher')->getData();
I have a symfony2 main form, with a field what is a collection type of an other field, like a Product entity with multiple Tag, but these tag has a unique hash.
There is an eventListener attached to the main form. If I send a data to the form and I send a Tag along with this unique has, the unique constraint on the class will say, that that field has to be unique. This work good, but in this EventListener I'm search in the DB for this unique field and if it's the right one, I do an assign, I replace the post content with an entity from the DB.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('eventDate', 'datetime',array(
'widget' => 'single_text',
'format' => 'yyyy-MM-dd HH:mm',
'invalid_message' => 'Wrong datetime format. Please use like this: 2015-09-20 14:45:12',
))
->add('eventEnds', 'datetime', array(
'widget' => 'single_text',
'format' => 'yyyy-MM-dd HH:mm',
'invalid_message' => 'Wrong datetime format. Please use like this: 2015-09-20 14:45:12',
))
->add('tag','collection', array(
'type' => new TagType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'prototype' => true,
'property_path' => 'professional',
'label' => false,
'options' => array(
'professional' => true,
),
))
->addEventListener(FormEvents::SUBMIT, [$this, 'submit'])
;
}
public function preSubmit(FormEvent $event)
{
/** #var MainForm $data */
$data = $event->getData();
foreach ($data->getTags() as $tag) {
if ($tag->getId() == null && $tag->getHash()){
$tagDB = $this->entityManager
->getRepository('ApplicationBundle:Tag')
->findOneBy([
'hash' => $professional->getHash(),
]);
if ($tagDB) {
$data->removeTag($tag);
$data->addTag($tagDB);
}
}
}
$event->setData($data);
}
If I dump the $data value after the setData, I see there the entities from the DB, but I still got a Unique validation error and I check in the validator, symfony pass in the original POST content.
Why is it like that and how can I solve this problem?
Should be
->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'submit'])
(PRE_SUBMIT instead of SUBMIT)
Your code should be something like:
public function preSubmit(FormEvent $event)
{
/** #var MainForm $data */
$data = $event->getData();
$num = count($data['tag']);
for ($i=0;$i<$num;$i++) {
$tag = $data['tag'][$i];
if (!isset($tag['id']) && isset($tag['hash'])){
$tagDB = $this->entityManager
->getRepository('ApplicationBundle:Tag')
->findOneBy([
'hash' => $tag['hash'],
]);
if ($tagDB) {
unset($data['tag'][$i]);
$data['tag'][$i] = array (
'id' => $tagDB->getId();
'hash' => $tagDB->getHash();
);
}
}
}
$event->setData($data);
}
Not sure the code is 100% correct as I have not been able to test it, but you get the idea.