Dynamic display checkout Stripe : difficulty to retrieve data on Symfony - javascript

I'm trying to implement Stripe on my Symony web app. The checkout page is displayed fine, but I can't make the price and title displayed be those of the product concerned.
Page code above:
booking.html.twig
{% extends "base.html.twig" %}
{% block title %}Réservation
{% endblock %}
{% block body %}
<h1>Bienvenue sur la page de réservation</h1>
<div class="card">
<div class="card-header">
Réserver et payer son activité
</div>
<div class="card-body">
<h5 class="card-title">{{product.title}}</h5>
<p class="card-text">{{product.price}}</p>
<button id="checkout-button">Payer {{product.price / 100}} €</button>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script type="text/javascript">
var stripe = Stripe("pk_test_51LkttkIFe6WM20DzgFS9hQa8fx9jydub6UygVtVg0VBj1xTFt8g04EjfbYjdQkrQOpjiptaesRjTCpxUt0H9W4Qy00vl8ZEgpN");
var checkoutButton = document.getElementById("checkout-button");
checkoutButton.addEventListener('click', function () {
fetch('/create-checkout-session', {
method : 'POST',
})
.then(function(response) {
return response.json()
})
.then(function(session) {
return stripe.redirectToCheckout({ sessionId : session.id});
})
.then(function(result) {
if (result.error) {
alert(result.error.message);
}
})
.catch(function(error) {
console.error('Error', error);
});
});
</script>
{% endblock %}
Here is the controller linked to the reservation page (page where the checkout button is) and to the creation of the checkout session :
BookingController.php :
class BookingController extends AbstractController
{
#[Route('/reservation', name: 'booking')]
public function index(ProductRepository $product)
{
$event = $product->findOneBy(['id' => $_GET['id']]);
dump($event);
// App\Entity\Product {#627 ▼
// -id: 14
// -title: "Cours de cuisine"
// -photo: "30-ecole-de-cuisine-4-scaled-1662990399.jpg"
// -description: "Cours de cuisine"
// -start_date: DateTime #1663056000 {#585 ▶}
// -end_date: DateTime #1663070400 {#632 ▶}
// -place: "Restaurant Place St Jean - 77000 MELUN"
// -duration: DateTime #14400 {#577 ▶}
// -price: "15000.00"
// -creator: App\Entity\User {#830 ▶}
//
}//
return $this->render('booking/booking.html.twig', [
'product' => $event
]);
}
#[Route('/create-checkout-session', name: 'checkout')]
public function checkout()
{
\Stripe\Stripe::setApiKey('*confidential*');
$session = \Stripe\Checkout\Session::create([
"locale" => "fr",
'line_items' => [[
'price_data' => [
'currency' => 'eur',
'product_data' => [
'name' => '**HERE PUT THE NAME OF THE PRODUCT CONCERNED** ',
],
'unit_amount' => **HERE PUT THE PRICE OF THE PRODUCT CONCERNED**,
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $this->generateUrl('success', [], UrlGeneratorInterface::ABSOLUTE_URL),
'cancel_url' => $this->generateUrl('error', [], UrlGeneratorInterface::ABSOLUTE_URL),
]);
return new JsonResponse(['id' => $session->id]);
}
The goal is to put a variable to retrieve the name of the product and its price and integrate it into the checkout session to have a dynamic payment page. I have not found any information that works in this case.

You can try to send the product ID in your FETCH
checkoutButton.addEventListener('click', function () {
fetch('/create-checkout-session?productId=2', {
method : 'POST',
})
Now in your controller
#[Route('/create-checkout-session', name: 'checkout')]
public function checkout(Request $request, ProductRepository $product)
{
\Stripe\Stripe::setApiKey('*confidential*');
$product = $repository->findOneBye(['id' => $request->query->get('productId')]);
//If product not found
if(!$product){
return new JsonResponse(['message' => 'Product not found'], 404);
}
$session = \Stripe\Checkout\Session::create([
"locale" => "fr",
'line_items' => [[
'price_data' => [
'currency' => 'eur',
'product_data' => [
'name' => $product->getTitle(),
],
'unit_amount' => $product->getPrice(),
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $this->generateUrl('success', [], UrlGeneratorInterface::ABSOLUTE_URL),
'cancel_url' => $this->generateUrl('error', [], UrlGeneratorInterface::ABSOLUTE_URL),
]);
return new JsonResponse(['id' => $session->id]);
}
By passing in your controller, for the route ... Inject the Class Request and use this to get the product ID. Your method is not recommended because it is risky
#[Route('/reservation', name: 'booking')]
public function index(ProductRepository $product, Request $request)
{
$event = $product->findOneBy(['id' => $request->query->get('id')]);

Related

How can increment a new dynamics feild in a form load with a fixture (data-prototype, data-index)?

I want to increment a data-prototype in my template when i add a new feild in my form, so my form is increment with my fixture when a load my page and this work.
But when i add a new feild to my Form, the data-prototype from the new feild start to 0 instead 4 and add new div not suppose to be here.
i use this documentation for help me, but i don't understand my error:
https://symfony.com/doc/current/form/form_collections.html
In this image, you can see my data-prototype load with data who comes from fixtures with index equal to 3 :
But now, when i add a new feild to my form, the new feild take index 0 and i have a feildset with a div inside my "li" but it's not suppose to be here :( :
This is my template code
<div class="col-md-6">
<div class="custom_title"><h5>Objectif de la formation</h5></div>
<ul class="objectifFormations"
data-index="{{form.objectifFormations|length < 0 ? form.objectifFormations|last.vars.name + 1 : 0 }}"
data-prototype="{{ form_widget(form.objectifFormations.vars.prototype) |e('html_attr') }}">
{% for objectifFormation in form.objectifFormations %}
<li> {{ form_widget(objectifFormation.name) }}</li>
{% endfor %}
</ul>
<button type="button" id="add_objectif" class="btn btn-info" data-collection-holder-class="objectifFormations"> ajouter un objectif de formation </button>
This is my formType :
class FormationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('Date') //Date de la formation
->add('price')
->add('category', EntityType::class,[
'class' => Category::class,
'choice_label' => 'title'
])
->add('programmeFormations', CollectionType::class,[
'entry_type' => ProgrammeType::class,
'entry_options'=> ['label' => false],
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
])
->add('objectifFormations', CollectionType::class,[
'entry_type' => ObjectifType::class,
'entry_options'=> ['label' => false],
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
])
->add('duration') //Durée
->add('forWho')
->add('prerequisite')
->add('location')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Formations::class,
]);
}
}
This is my javascript
const addFormToCollection = (e) => {
const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('li');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index++;
addTagFormDeleteLink(item);
};
const addTagFormDeleteLink =(item) => {
const removeFormButton = document.createElement('button');
removeFormButton.classList.add("btn", "btn-danger");
removeFormButton.innerText = "Supprimé";
item.append(removeFormButton);
removeFormButton.addEventListener('click', (e) => {
e.preventDefault();
// remove the li for the tag form
item.remove();
});
}
document
.querySelectorAll('#add_objectif')
.forEach(btn => {
btn.addEventListener("click", addFormToCollection)
});
How can i solve this ?
Thanks !

Stripe redirectToCheckout with Laravel 6/JavaScript

I'm trying stripe.redirectToCheckout in Laravel 6. And it's been days now, I can't find anything helpful on internet. I used strip documentation for creating a user session and redirectToCheckout and implemented stripe session in web/routes and redirectToCheckout in JavaScript.
The following is the button for pay with stripe
<button id="checkout-button"
data="{{$id}}" href="https://checkout.stripe.com/checkout.js"
role="link" style="width: 100%"
class="btn btn-dark">Pay with stripe
</button>
The following is the JavaScript function for redirectToCheckout
<script>
(function () {
var stripe = window.Stripe('pk_test_ZuGUA3XxCsWvZVbqnOkFMQnM00PV2c0Acm');
var checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect
// them to Checkout.
console.log('button clicked');
var product_name = document.getElementById('#p_name');
var product_price = document.getElementById('#p_price');
var name, price;
// var checkout_btn = document.getElementById('#checkout-button-plan_GI1dhi4ljEzk0R');
btn = $(this).attr('data');
console.log(btn);
stripe.redirectToCheckout({
items: [
// Replace with the ID of your SKU
{sku: 'sku****', quantity: 1}
],
successUrl: 'http://4c655de9.ngrok.io/success',
cancelUrl: 'http://4c655de9.ngrok.io/canceled',
clientReferenceId: btn
}).then(function (result) {
console.log('result');
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
console.log(result.error.message);
});
});
})();
</script>
The following is the web/route.php function for \Stripe\Checkout\Session
Route::get('/', function () {
\Stripe\Stripe::setApiKey('sk****');
$session = \Stripe\Checkout\Session::create([
'success_url' => 'http://4c655de9.ngrok.io/success',
'cancel_url' => 'http://4c655de9.ngrok.io/canceled',
'payment_method_types' => ['card'],
'line_items' => [
[
'name' => 'T-shirt',
'description' => 'Comfortable cotton t-shirt',
'amount' => 1500,
'currency' => 'usd',
'quantity' => 2,
],
],
]);
$id = $session->id;
return view('home')->with('id', $id);

Symfony2 - Reconstructing dynamic form on validation failure

Symfomaniacs! :)
I have a pretty complex form formed by forms embedded on other forms, embedded on other forms and so on 4 layers deep. It all works great using prototypes and a bit of javaScript (variation from the examples in the cookbook) , yet I have a big problem:
When I submit the form, and it fails validation, it reloads the page, and it only renders the first (outer) layer form.
Is there a simple (or any other) way to reconstruct everything to the state before submit? Some directions would be really appreciated.
Thanks for your attention.
---UPDATE:
Relevants part of the code
FormTypes: Classroom and Activity. A Classroom contains Activities
class ClassroomType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$entity = $builder->getData();
$builder->add('name', 'text', array(
"label" => "Nombre",
"required" => true,
"trim" => true,
))
//Bunch of other fields
->add("image", "file", array(
"label" => "Imagen"
))
//First level of embedding
->add("activities", 'collection',array(
'type' => new ActivityType(),
'allow_add' => true,
'prototype_name' => '__activity_prototype_placeholder__'
))
->add('save', 'submit');
}
}
class ActivityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('kindActivity', 'entity', array(
"class" => "EducaAppBundle:ActivityKind",
"multiple" => true,
'error_bubbling' => true,
"expanded" => true));
$builder->add('name', 'text', array(
'label' => 'Nombre del la actividad',
))
->add('sessions', 'collection',array(
'label' => 'Sesiones',
'type' => new SessionType(),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
))
->add('save', 'submit', array('label' => 'Guardar'));
//It has a save button so it can be standalone.
//In the twig template macro, if embedded I set "rendered"= true
}
}
ClassroomController
class ClassroomController extends Controller
{
public function createAction(Request $request)
{
//initialization and authoritation stuff
$newClassroom = new Classroom();
$form = $this->createForm(new ClassroomType($choices), $newClassroom);
$form->handleRequest($request);
if ($form->isSubmitted()) {
$ClassroomData = $form->getData();
$validator = $this->get('validator');
$errorsClassroom = $validator->validate($ClassroomData);
if (count($errorsClassroom) == 0) {
//stuff for Doctrine persistence
} else {
\Doctrine\Common\Util\Debug::dump("Not Valid");
}
} else {
\Doctrine\Common\Util\Debug::dump("Not Submitted");
}
$formview = $form->createView();
return $this->render("ClassroomBundle:Default:createClassroomIndex.html.twig", array(
'platforms' => $platforms,
'classroomForm' => $formview,
'errors' => $form->getErrors()));
}
createClassroomIndex.html.twig
{% extends '::base.html.twig' %}
{% block body %}
{% form_theme classroomForm 'ClassroomBundle:Form:createClassroomBaseForm.html.twig' %}
<div id="looper-background" class="col-md-12">
<div class="looper-outer-container col-sm-10 col-md-offset-1">
{{ form(classroomForm) }}
</div>
</div>
//navigation stuff
{% endblock body %}
{% block javascripts %}
function addActivityForm(collectionHolder, newLinkLi) {
// Get the data-prototype explained earlier
var thumbPrototype = getThumbnailPrototype(collectionHolder.data('index'));
var formPrototype = $(".activity-prototype-holder").data("prototype");
// get the new index
var index = collectionHolder.data('index');
// Replace '__activity_prototype_placeholder__' in the prototype's HTML to
// instead be a number based on how many items we have
var newThumb = thumbPrototype.replace(/__activity_prototype_placeholder__/g, index);
var newForm = formPrototype.replace(/__activity_prototype_placeholder__/g, index);
var newLooper = newForm;
// increase the index with one for the next item
collectionHolder.data('index', index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
var newThumbObject = $(newThumb);
newLinkLi.before(newThumbObject);
addThumbDeleteButton(newThumbObject);
// Add new "loop" (page) to looper
var loopContainer = jQuery("#classroomLooper").find(".looper-inner");
loopContainer.append(newLooper);
// show new loop
var numItems = loopContainer.find(".item").length;
looperGoTo(numItems);
//Order thumbnails
animateThumbnailPosition(collectionHolder);
}
{% endblock javascripts %}
createClassroomBaseForm.html.twig
{% block form -%}
{{ form_start(form, {attr: {novalidate: 'novalidate'}}) }}
{{- block('form_nav') -}}
{{- block('form_looper') -}}
{{- block('activity_prototype') -}}
{{ block('save_btn') }}
{{ form_end(form) }}
<span class="help-block pad15 bg-danger educaErrorBox">
{{ form_errors(form) }}
</span>
{{ tinymce_init() }}
{{ block('javascripts') }}
{%- endblock form %}
{% block activity_prototype %}
{% import "ClassroomBundle:Form:createActivityFormMacro.html.twig" as activityForms %}
{% do form.activities.setRendered %}
<ul id="activities_hidden_field" display="none" class="activities activity-prototype-holder"
data-prototype="{{ activityForms.formularioLooper(form.activities.vars.prototype, false) |e }}"></ul>
{% endblock activity_prototype %}
If I missed something in the code, just let me know, I didn't want to paste too much irrelevant code. :)

Symfony2 collection field for simple list of emails to be sent

I'm trying to accomplish a very basic task: I'd like to have a form with one field (an email address), and an "add" button so that you can add other email addresses. I'll later send emails to each of the addresses passed.
I came across this, which in theory is exactly what I need. I'm fine creating the form inside the controller directly, as I'll use it only here. Here is the function inside the controller:
public function createHRAction(Request $request)
{
$data = array();
$form = $this->createFormBuilder($data)
->add('emails', 'collection', array(
// each item in the array will be an "email" field
'type' => 'email',
// these options are passed to each "email" type
'options' => array(
'required' => false,
'attr' => array('class' => 'email-box'),
'prototype' => true,
'allow_add' => true,
),
))->getForm();
$form->handleRequest($request);
if ($form->isValid())
{
//$form->bind($request);
// $data is a simply array with your form fields
$data = $form->getData();
return $this->render('HR/invitationSent.html.twig', array('data' => $data,));
}
return $this->render('HR/createForm.html.twig', array('form' => $form->createView(),));
}
And here is the twig:
{% extends "internal.html.twig" %}
{% block content %}
{{ form_start(form) }}
{#
{# store the prototype on the data-prototype attribute #}
<ul id="email-fields-list" data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
Add another email
#}
{{ form_end(form) }}
{% endblock content %}
{% block javascripts %}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
// keep track of how many email fields have been rendered
var emailCount = '{{ form.emails|length }}';
jQuery(document).ready(function() {
jQuery('#add-another-email').click(function(e) {
e.preventDefault();
var emailList = jQuery('#email-fields-list');
// grab the prototype template
var newWidget = emailList.attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
newWidget = newWidget.replace(/__name__/g, emailCount);
emailCount++;
// create a new list element and add it to the list
var newLi = jQuery('<li></li>').html(newWidget);
newLi.appendTo(emailList);
});
})
</script>
{% endblock %}
I get the following error:
Key "prototype" for array with keys "value, attr, form, id, name,
full_name, disabled, label, label_format, multipart, block_prefixes,
unique_block_prefix, translation_domain, cache_key, read_only, errors,
valid, data, required, max_length, pattern, size, label_attr,
compound, method, action, submitted, allow_add, allow_delete" does not
exist in HR/createForm.html.twig at line 8
I thought I solved it by adding the lines:
'prototype' => true,
'allow_add' => true,
but in effect it did not change anything :(
Tips?
The 'prototype' and 'allow_add' are their own options. The 'options' is the array passed to the form type you specify in 'type' (so what each 'email' option would have, in this case). See the documentation here: http://symfony.com/doc/current/reference/forms/types/collection.html
Your form creation should look like this:
$this->createFormBuilder($data)
->add('emails', 'collection', array(
'type' => 'email',
'prototype' => true,
'allow_add' => true,
'options' => array(
'required' => false,
'attr' => array('class' => 'email-box'),
)
)
)->getForm();

Populating entity field type in dynamic form Symfony2

I have an entity Lexeme, which contains Language and Phoneme entities.
In my Form Type, I want the Phoneme choices to be different depending on the Language entity chosen for the Lexeme.
The problem is when I dynamically update the form, the correct Phonemes are displayed, but they are not populated from the database: i.e. those Phonemes which belong to the Lexeme in the database do not appear as checked in the dynamically loaded form.
The Lexeme Controller:
public function editAction(Request $request, $id)
{
$lexeme = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->find($id);
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->where('l.id > :thisId')
->setParameter('thisId', $id)
->setMaxResults(1)
->getQuery();
$nextLexeme = $query->getResult();
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->where('l.id < :thisId')
->setParameter('thisId', $id)
->setMaxResults(1)
->getQuery();
$previousLexeme = $query->getResult();
if ( count($nextLexeme) < 1 ) {
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->orderBy('l.id', 'asc')
->setMaxResults(1)
->getQuery();
$nextLexeme = $query->getResult();
}
if ( count($previousLexeme) < 1 ) {
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->orderBy('l.id', 'desc')
->setMaxResults(1)
->getQuery();
$previousLexeme = $query->getResult();
}
if (!$lexeme) {
$lexeme = new Lexeme();
}
$form = $this->createForm(new EditLexemeType(), $lexeme);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($lexeme);
$em->flush();
return $this->redirect($this->generateUrl('aleph2_omega_transcription_lexemes_edit', array ('id'=>$lexeme->getId())));
}
return $this->render('Aleph2OmegaTranscriptionBundle:Lexeme:edit.html.twig', array(
'form' => $form->createView(),
'previousId' => $previousLexeme[0]->getId(),
'nextId' => $nextLexeme[0]->getId()
));
}
The Lexeme Form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formModifier = function (FormInterface $form, $language) {
$form
->remove('phonemes')
->add('phonemes', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Phoneme',
'property' => 'latin',
'expanded' => true,
'multiple' => true,
'by_reference' => false,
'query_builder' => function(EntityRepository $er) use ($language) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', $language);
},
));
};
$builder
->add('language', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Language',
'property' => 'name',
'expanded' => false,
'multiple' => false,
))
->add('defaultGreek')
->add('defaultHebrew')
->add('save', 'submit')
//->add('id', 'hidden')
->add('words', 'collection', array(
'type' => new WordLimitedProtoType(),
'allow_add' => true,
'allow_delete' => true,
'options' => array('data_class' => 'Aleph2Omega\TranscriptionBundle\Entity\Word'),
'prototype' => true,
'by_reference' => false,
))
->add('phonemes', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Phoneme',
'property' => 'latin',
'expanded' => true,
'multiple' => true,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', '1');
},
))
->add('save', 'submit')
->setMethod('POST')
->addEventListener(
FormEvents::POST_SUBMIT,
function ($event) {
$event->stopPropagation();
},
900)
->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
if ($data instanceof Word) {
$formModifier($event->getForm(), $data->getLanguage());
}
}
)
->get('language')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$language = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $language);
});
}
The Lexeme Twig template:
{{ include('Aleph2OmegaTranscriptionBundle::menu.html.twig') }}
{{ form_start(form) }}
{{ form_row(form._token) }}
<h1>< Edit Lexeme ></h1>
{{ form_errors(form) }}
<table>
<tr><th>Language</th><td>{{ form_widget(form.language) }}</td></tr>
<tr><th>Default Greek</th><td>{{ form_widget(form.defaultGreek) }}</td></tr>
<tr><th>Default Hebrew</th><td>{{ form_widget(form.defaultHebrew) }}</td></tr>
</table>
<table>
<tr><th>Phonemes</th></tr>
<tr><td>{{ form_widget(form.phonemes) }}</td></tr>
</table>
<h3>{{ form_widget(form.save) }}</h3>
<table class="words" data-prototype="{% filter escape %}{% include 'Aleph2OmegaTranscriptionBundle:Word:limited_prototype.html.twig' with { 'item': form.words.vars.prototype } %}{% endfilter %}">
{# iterate over each existing word and render its fields #}
<tr><th>transcription</th><th>normalised</th><th>Hebrew</th><th>comment</th></tr>
{% for word in form.words %}
<tr>
<td>{{ form_widget(word.transcription) }}</td>
<td>{{ form_widget(word.normalisedTranscription) }}</td>
<td>{{ form_widget(word.hebrewTranscription) }}</td>
<td>{{ form_widget(word.comment) }}</td>
</tr>
{{ form_rest(word) }}
{% endfor %}
</table>
{{ form_rest(form) }}
{{ form_end(form) }}
The javascript for AJAX:
jQuery(document).ready(function() {
$("[id$=language]").change(function() {
languageChange();
});
function languageChange() {
var $languages = $("[id$=language]");
$($languages).each(function() {
var $form = $(this).closest('form');
var data = {};
data[$(this).attr('name')] = $(this).val();
$.ajax({
url : $form.attr('action'), //Routing.generate('route_id', /* your params */), //
type: $form.attr('method'),
data: data,
success: function(html) {
$phonemes = $("[id$=phonemes]");
$($phonemes).each(function() {
$(this).replaceWith(
$(html).find('#'+this.id)
);
});
}
});
});
}
languageChange();
});
UPDATE: Some screenshots
In private correspondence, some people wanted clarification as to the behaviour I'm looking for.
Here's a page from a different entity (word), which doesn't make use of AJAX to populate the phonemes field. This exhibits the sort of behaviour I would like:
The Lexeme entity (which the code above is for), which is updated via AJAX, correctly displays the right phonemes for each language, it's just that the phonemes recorded in the database as "connected" to the Lexeme are not checked.
E.g., here are the correctly displayed phonemes when "Hebrew" is selected:
And here are the correctly displayed phonemes when "Greek" is selected:
Finally, as far as debugging goes, I've rooted around using Chrome console and network display, and this hints that the problem may be due to CSRF with AJAX. However, what I don't understand is—if CSRF really is the problem, why is the page returned from the server at all?
UPDATE 2: A partial work-around
Ok, so a partial workaround to at least get the initial page-load displaying correctly is to avoid the AJAX call. This doesn't solve the problem of ensuring that AJAX updates display everything correctly, but at least improves functionality (and is, I think, a better-practice way of pre-populating the form anyway than using AJAX).
In the buildForm function, the data to populate the form can be obtained by using:
$lexemeEntity = $builder->getData();
This can then be fed into the initial query populating the phonemes entity type (note, don't forget to "use" this in the declaration of the defined function):
'query_builder' => function(EntityRepository $er) use ($lexemeEntity) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', $lexemeEntity->getLanguage());
},
Finally, the default AJAX call on the initial load of every page needs to be turned off in the javascript. I did this by just commenting:
//languageChange();
Now initial page loads are all populated correctly—but further updates to the forms using AJAX are still not loaded with all the data, any help with this still very much welcome.

Categories