I have a List of entries coming from a database. I would like to have a \"Delete-Button\" at the end of every row, so that the user won\'t have to first go to the edit/show page
You may render individual tokens with:
{{ form_widget(form._token) }}
or specifically for your case:
{{ form_widget(delete_form._token) }}
But, I think you are better served making an array of forms and fully rendering each one:
In your controller:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$rep= $em->getRepository('IntranetServicesBundle:Laender')
->createQueryBuilder('l');
var_dump($rep->getQuery()->getDql());
$entities=$rep->getQuery()->getResult();
$delete_forms = array_map(
function($element){
return $this->createDeleteForm($element->getId());}
,$entities->toArray()
);
return $this->render('IntranetServicesBundle:Laender:index.html.twig'
, array(
'entities' => $entities,
'delete_forms' => $delete_forms
));
}
private function createDeleteForms($id)
{
return $this->createFormBuilder(array('id' => $id)))
->add('id', 'hidden')
->getForm()
;
}
public function deleteAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('IntranetServicesBundle:Laender')
->find($id);
// this line might need to be changed to point to the proper repository
if (!$entity) {
throw $this->createNotFoundException('Unable to find Laender entity.');
}
$em->remove($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('laender_index'));
// this line might need to be changed to point to the proper
// post-delete route
}
In your twig do something along the lines of:
{% for form in delete_forms %}{{form_widget(form)}}{% endfor %}
The answer of @Lighthart led me to the correct answer:
In your controller generate an array of form views and had it over to the view:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('AppBundle:Entity')->findAll();
$delete_forms = array_map(
function ($element) {
return $this->createDeleteForm($element->getId())->createView();
}
, $entities
);
return $this->render('AppBundle:Entity:index.html.twig', array(
'entities' => $entities,
'delete_forms' => $delete_forms
));
}
Now you have to access this in your view. Therefore you can use the form functions and the special loop variables:
{% extends '::base.html.twig' %}
{% block body %}
{% for entity in entities %}
{# Access the delete form for this entity using the loop index ... #}
{% set delete_form = delete_forms[loop.index0] %}
{# ... and display the form using the form tags. #}
{{ form_start(delete_form) }}
<input type="submit" value="Delete" />
{{ form_end(delete_form) }}
{% endfor %}
{% endblock %}
That's it.
I faced a similar situation where I wanted to delete a product while using csrf protection. I also wanted to use ajax to make the DELETE request.
So, to do this, this is what my view, index.html, looked like:
// html for displaying a products table with delete btn for each row
// ...
// Retrieve csrf token and store it somewhere in DOM (preferably outside table),
// We do this so that we can send the token via ajax later
<span id="csrf_token" data-token="{{ csrf_token('form') }}"></span>
<script>
// Make an ajax request on the on-click handler of our delete btn
$.ajax({
url: localhost/admin/product/4545, // generated dynamically, the last segment being the ID of the item to be deleted.
type: 'POST',
data: {
"_method": "DELETE",
"form[_token]": $("#csrf_token").data("token") // passed csrf token here
},
success: function(result) {
// Do something with the result
}
});
</script>
As seen above, {{ csrf_token('form') }}
is what is actually giving you the csrf token inside twig.
My Controller:
/**
* Deletes a product entity.
* @Route("/{id}", name="admin_product_delete")
* @Method("DELETE")
*/
public function deleteAction(Request $request, product $product)
{
$form = $this->createDeleteForm($product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($product);
$em->flush($product);
// you can return a json response here to your ajax callback if you'd like.
return new JsonResponse(array('status' => 'deleted'));
}
// return new JsonResponse(array('status' => 'failed'));
}
/**
* Creates a form to delete a product entity.
* @param product $product The product entity
* @return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(product $product)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('admin/product/{id}', array('id' => $product->getId())))
->setMethod('DELETE')
->getForm()
;
}
And this should delete the required row as expected!