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 to delete the entry.
I tried creating a hidden input field with the csrf token like so:
return $this->createFormBuilder()
->getForm()
;
this will output:
<div id="form">
<input type="hidden" id="form__token" name="form[_token]" value="6c98ebfa9df07.....">
</div>
The rest of the Form i put around in the twig template so that every form has its own action path according the the id of the entry.
unfortunately in the twig template only the first
{{ form_widget(delete_form) }}
will get rendered.
How can i use this hidden field more often? OR is there any way to do this whole thing differently?
Thanks for any help
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$deleteForm = $this->createDeleteForms();
$entities = $em->getRepository('IntranetServicesBundle:Laender')->findAll();
return $this->render('IntranetServicesBundle:Laender:index.html.twig', array(
'entities' => $entities,
'delete_form' => $deleteForm->createView(),
));
}
private function createDeleteForms()
{
return $this->createFormBuilder()
->add('id', 'hidden')
->getForm()
;
}
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!
The answer of @Phidelux led me to the following solution:
In twig I created delete form:
<form method="post" action="{{ path('page_delete', {'id': page.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ page.id) }}">
<button type="submit" class="btn btn-icon">
<i class="far fa-trash-alt"></i>
</button>
</form>
The code for creating a list view is:
/**
* @Route("/", name="page-list", methods={"GET"})
*/
public function index(PageRepository $pageRepository): Response
{
$pages = $pageRepository->findAll();
$delete_forms = array_map(
function ($element) {
return $this->render('admin/page/_delete_form.html.twig', [
'page' => $element,
]);
}
, $pages
);
return $this->render('admin/page/index.html.twig', [
'pages' => $pages,
'delete_forms' => $delete_forms
]);
}
Then in my list view I added the delete button:
{% for key, page in pages %}
...
{{ delete_forms[key].content | raw }}
{% endfor %}
And in my edit page form I can use:
{{ include('admin/sjabloon/_delete_form.html.twig') }}
This solved the method not allowed message on delete
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.