Symfony 2 - Delete Forms and CSRF Token

后端 未结 3 1193
栀梦
栀梦 2021-02-15 16:48

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

相关标签:
3条回答
  • 2021-02-15 17:41

    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 %}
    
    0 讨论(0)
  • 2021-02-15 17:54

    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.

    0 讨论(0)
  • 2021-02-15 17:54

    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.

    Tested for Symfony 3.x

    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!

    0 讨论(0)
提交回复
热议问题