Symfony 2 - 删除表单和CSRF令牌

时间:2013-04-19 13:45:56

标签: ajax symfony twig csrf

我有一个来自数据库的条目列表。我想在每一行的末尾都有一个“删除按钮”,这样用户就不必先进入编辑/显示页面来删除该条目。

我尝试使用csrf标记创建一个隐藏的输入字段,如下所示:

return $this->createFormBuilder()
   ->getForm()
;

这将输出:

<div id="form">
   <input type="hidden" id="form__token" name="form[_token]" value="6c98ebfa9df07.....">
</div>

表单的其余部分我在twig模板中放置,以便每个表单根据条目的id有自己的操作路径。

不幸的是,在树枝模板中只有第一个

{{ form_widget(delete_form) }}

将被渲染。

我如何更频繁地使用此隐藏字段? 或者有没有办法以不同的方式完成这一切?

感谢您的帮助

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()
    ;
}

4 个答案:

答案 0 :(得分:3)

您可以使用以下内容呈现单个令牌:

{{ form_widget(form._token) }}

或特别针对您的情况:

{{ form_widget(delete_form._token) }}

但是,我认为你可以更好地制作一系列表格并完全呈现每一种形式:

在您的控制器中:

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
}

在你的树枝上做一些事情:

{% for form in delete_forms %}{{form_widget(form)}}{% endfor %}

答案 1 :(得分:3)

@Lighthart的回答让我得到了正确答案:

在你的控制器中生成一个表单视图数组并将其交给视图:

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
    ));
}

现在您必须在视图中访问它。因此,您可以使用表单函数和特殊循环变量:

{% 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 %}

那就是它。

答案 2 :(得分:0)

我遇到了类似的情况,我想在使用csrf保护时删除产品。我还想使用 ajax 来发出DELETE请求。

针对Symfony 3.x

进行了测试

所以,要做到这一点,我的查看 index.html 就是这样:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let inner = UIView()
        inner.backgroundColor = .yellow
        inner.translatesAutoresizingMaskIntoConstraints = false

        let outer = UIView()
        outer.backgroundColor = .blue
        outer.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(outer)
        view.addSubview(inner)

        // get these from somewhere, e.g. outerWidth.frame.size
        let outerWidth = CGFloat(200)
        let outerHeight = CGFloat(400)
        let outerAnchor: NSLayoutDimension

        if outerWidth >= outerHeight {
            outerAnchor = outer.heightAnchor
        } else {
            outerAnchor = outer.widthAnchor
        }

        let imageScale = CGFloat(0.5)

        NSLayoutConstraint.activate([
            outer.widthAnchor.constraint(equalToConstant: outerWidth),
            outer.heightAnchor.constraint(equalToConstant: outerHeight),
            outer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            outer.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            inner.centerXAnchor.constraint(equalTo: outer.centerXAnchor),
            inner.centerYAnchor.constraint(equalTo: outer.centerYAnchor),
            inner.widthAnchor.constraint(equalTo: outerAnchor, multiplier: imageScale),
            inner.heightAnchor.constraint(equalTo: inner.widthAnchor)
        ])
    }
}

如上所示, // 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> 实际上是在树枝内部为您提供 csrf令牌。

我的控制器

{{ csrf_token('form') }}

这应该按预期删除所需的行!

答案 3 :(得分:0)

@Phidelux 的回答让我找到了以下解决方案:

在树枝中我创建了删除表单:

<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>

创建列表视图的代码是:

/**
     * @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
        ]);
    }

然后在我的列表视图中我添加了删除按钮:

{% for key, page in pages %}
...
    {{ delete_forms[key].content | raw }}
{% endfor %}

在我的编辑页面表单中,我可以使用:

{{ include('admin/sjabloon/_delete_form.html.twig') }}

解决了删除时不允许消息的方法