Лучшие практики – Удалить ссылки с Symfony 2

В Symfony 2, который является лучшим способом создать ссылку для удаления записи?

Я могу определить маршрут для /entity/delete принимающий только метод DELETE , но я не знаю, как создать ссылку DELETE из шаблона. То же самое касается создания ссылок PUT .

Ну так что ты делаешь? Принять GET петицию, чтобы удалить запись? Есть ли способ создать ссылку DELETE ?

Анкеры могут запускать только запросы GET. И поскольку запросы GET могут быть кэшированы, а в будущем (когда вы выполняете серьезное кэширование), возможно, совсем не достигнете вашего кода приложения, это плохая практика использования GET для изменения чего-либо, что изменяет состояние вашего приложения. Вот что я делаю в своих проектах. С этими двумя, вы сможете просто сделать следующее в ваших шаблонах:

 <a href="{{ path('my_entity_destroy') }}" {{ delete_link(myEntity) }}>Delete</a> 

Как это работает

Предполагая, что ваш путь удаления объекта: /my_entity , методы: [УДАЛИТЬ]

Принцип очень прост. delete_link расширения delete_link создаст data-attribute на якоре, поэтому скомпилированный якорь будет выглядеть так:

 <a href="/my_entity" data-delete-link="3964">Delete</a> 

Затем, когда кто-то нажимает на эту ссылку, javascript поймает этот клик, создаст форму и запустит запрос DELETE с идентификатором, указанным в атрибуте data-delete-link .

Что заставляет это работать

Вот что делает возможным расширение LinkHelper Twig:

 <?php namespace Me\MyBundle\Twig; class LinkHelperExtension extends \Twig_Extension { public function getFunctions() { return [ new \Twig_SimpleFunction('delete_link', [$this, 'fnDeleteLink'], ['is_safe' => ['all']]), ]; } public function fnDeleteLink($target) { if (is_object($target)) { $target = $target->getId(); } return "data-delete-link='$target'"; } public function getName() { return 'link_helper'; } } 

И я JavaScript в моем базовом шаблоне:

 $(function () { var createForm = function (action, data) { var $form = $('<form action="' + action + '" method="POST"></form>'); for (input in data) { if (data.hasOwnProperty(input)) { $form.append('<input name="' + input + '" value="' + data[input] + '">'); } } return $form; }; $(document).on('click', 'a[data-delete-link]', function (e) { e.preventDefault(); var $this = $(this); var $form = createForm($this.attr('href'), { id: $this.attr('data-delete-link'), _method: 'DELETE' }).hide(); $('body').append($form); // Firefox requires form to be on the page to allow submission $form.submit(); }); }); 

Ограничения

Он работает только для объектов с первичным ключом с именем id . Однако вы можете легко изменить это в соответствии с вашими потребностями и поддерживать составные первичные ключи и т. Д.

_method функций Symfony2 _method можно найти в _method Определение требований маршрута» . Ниже приводится решение, которое я использую.

Ссылка

 <a href="{{ path('my_delete_route_name', {'id': some_entity.id}) }}" class="as-form" data-method="delete" data-csrf="_token:{{ csrf }}" >{{ 'delete'|trans({}, 'button') }}</a> 

Переформатируйте вышеуказанную ссылку на встроенный после копирования-вставки

Прикрепить прослушиватель событий onClick:

JS

 $('.as-form').on('click',function(){ var $form = $('<form/>').hide(); //form options $form.attr({ 'action' : $(this).attr('href'), 'method':'post' }) //adding the _method hidden field $form.append($('<input/>',{ type:'hidden', name:'_method' }).val($(this).data('method'))); //adding a CSRF if needs if ($(this).data('csrf')) { var csrf = $(this).data('csrf').split(':'); $form.append($('<input/>',{ type:'hidden', name:csrf[0] }).val(csrf[1])); } //add form to parent node $(this).parent().append($form); $form.submit(); return false; }); , $('.as-form').on('click',function(){ var $form = $('<form/>').hide(); //form options $form.attr({ 'action' : $(this).attr('href'), 'method':'post' }) //adding the _method hidden field $form.append($('<input/>',{ type:'hidden', name:'_method' }).val($(this).data('method'))); //adding a CSRF if needs if ($(this).data('csrf')) { var csrf = $(this).data('csrf').split(':'); $form.append($('<input/>',{ type:'hidden', name:csrf[0] }).val(csrf[1])); } //add form to parent node $(this).parent().append($form); $form.submit(); return false; }); 

контроллер

 class MyCustomController extends Controller { /** * @Route("/delete/{id}",name="my_delete_route_name") * @Method("DELETE") * @ParamConverter("entity", class="MyEntityClass") * @CsrfProtector(intention="my_csrf_intention", name="_token") */ public function deleteAction(Request $request, $entity) { // do whatever you need } } 

Заметка

@CsrfProtector – это моя пользовательская аннотация, которая используется для проверки токена CSRF, переданного в запросе, перед запуском метода контроллера.

Эти решения не работают без включенного JavaScript. Если вам просто нужна кнопка «Удалить» в вашей форме редактирования, было бы возможно создать второй submit с именем delete и перенаправить в updateAction в вашем контроллере на deleteAction, если был нажат «delete-submit».

Основываясь на рекомендации Бернхарда Зюрна , посмотрите, как удалить ссылку без javascript :

1) В своем построителе форм добавьте вторую кнопку отправки.

 $builder // ... [other fields] ... ->add('save', SubmitType::class, array( 'attr' => array('class' => 'button-link save'), 'label' => 'Validate' )) ->add('delete', SubmitType::class, array( 'attr' => array('class' => 'button-link delete'), 'label' => 'Delete' )); 

2) В вашем шаблоне ветки, нажмите кнопку удаления, где хотите. В этом примере я использую ту же форму для добавления и редактирования пользователя. Итак, я поставил код в условие «если», чтобы обнаружить действие редактирования.

 {% if user_id is defined %} {{ form_widget(form.delete) }} {% endif %} 

3) В вашем контроллере используйте метод isClicked() кнопки для запроса, если была нажата кнопка «Удалить»:

  /** * @Route("/users/edit/{id}", name="user_edit") */ public function editAction($id, Request $request) { // get user from database $em = $this->getDoctrine()->getManager(); $user = $em->getRepository('AppBundle:User')->find($id); // user doesn't exist if (!$user) { throw $this->createNotFoundException('No user found for id '. $id); } // build the form with user data $form = $this->createForm(UserType::class, $user); // form POST $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { /*** SAVE ***/ if ($form->get('save')->isClicked()) { // ... perform some action /*** DELETE ***/ } elseif ($form->get('delete')->isClicked()) { $em->remove($user); $em->flush(); // message $this->addFlash('notice', "User has been deleted successfully !"); // show list return $this->redirectToRoute('user'); } } // show form return $this->render('users/form.html.twig', array( 'form' => $form->createView(), 'user_id' => $id )); } 

Чтобы добиться дальнейшего прогресса, вы можете добавить сообщение подтверждения. Are you sure you want to delete this user ? в javascript перед выполнением действия маршрута.