PHP项目Symfony form与弹窗确认

wen PHP项目 2

本文目录导读:

PHP项目Symfony form与弹窗确认

  1. 基础实现方式
  2. 高级确认弹窗(使用Bootstrap Modal)
  3. Symfony表单集成示例
  4. 使用JavaScript库的封装
  5. 最佳实践建议

在Symfony项目中,将表单与弹窗确认结合是一个常见的需求,主要用于删除操作或重要数据提交时的二次确认,以下是几种实现方式的详细说明:

基础实现方式

使用HTML5的confirm()弹窗

Twig模板:

<form action="{{ path('delete_item', {'id': item.id}) }}" method="POST" 
      onsubmit="return confirm('确定要删除这个项目吗?');">
    <input type="hidden" name="_method" value="DELETE">
    {{ form_row(form._token) }}
    <button type="submit" class="btn btn-danger">删除</button>
</form>

使用Symfony表单的attr属性

在Controller中:

use Symfony\Component\Form\Extension\Core\Type\SubmitType;
public function deleteItemForm($itemId): Response
{
    $form = $this->createFormBuilder()
        ->add('delete', SubmitType::class, [
            'label' => '删除',
            'attr' => [
                'onclick' => 'return confirm("确定要删除吗?");',
                'class' => 'btn btn-danger'
            ]
        ])
        ->getForm();
    // ... 处理表单
}

高级确认弹窗(使用Bootstrap Modal)

纯JavaScript + Bootstrap Modal

Twig模板:

{# 删除按钮 #}
<button type="button" class="btn btn-danger" data-toggle="modal" 
        data-target="#confirmModal-{{ item.id }}">
    删除
</button>
{# 确认弹窗 #}
<div class="modal fade" id="confirmModal-{{ item.id }}" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">确认删除</h5>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
            <div class="modal-body">
                <p>确定要删除项目"{{ item.name }}"吗?此操作不可撤销。</p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
                <form action="{{ path('delete_item', {'id': item.id}) }}" method="POST" style="display: inline;">
                    <input type="hidden" name="_method" value="DELETE">
                    {{ form_row(form._token) }}
                    <button type="submit" class="btn btn-danger">确认删除</button>
                </form>
            </div>
        </div>
    </div>
</div>

使用SweetAlert2(推荐)

安装:

composer require symfony/webpack-encore-bundle
# 或直接引入CDN

Twig模板(使用CDN):

{% block javascripts %}
    {{ parent() }}
    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
{% endblock %}
{# 删除按钮与确认 #}
<button type="button" class="btn btn-danger delete-btn" 
        data-url="{{ path('delete_item', {'id': item.id}) }}"
        data-name="{{ item.name }}">
    删除
</button>
<script>
document.querySelectorAll('.delete-btn').forEach(button => {
    button.addEventListener('click', function(e) {
        const url = this.dataset.url;
        const itemName = this.dataset.name;
        Swal.fire({
            title: '确认删除',
            text: `确定要删除 "${itemName}" 吗?此操作不可撤销!`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6',
            confirmButtonText: '确认删除',
            cancelButtonText: '取消'
        }).then((result) => {
            if (result.isConfirmed) {
                // 创建并提交删除表单
                const form = document.createElement('form');
                form.method = 'POST';
                form.action = url;
                const methodInput = document.createElement('input');
                methodInput.type = 'hidden';
                methodInput.name = '_method';
                methodInput.value = 'DELETE';
                form.appendChild(methodInput);
                const tokenInput = document.createElement('input');
                tokenInput.type = 'hidden';
                tokenInput.name = '{{ csrf_token("delete") }}';
                tokenInput.value = '{{ csrf_token("delete") }}';
                form.appendChild(tokenInput);
                document.body.appendChild(form);
                form.submit();
            }
        });
    });
});
</script>

Symfony表单集成示例

完整示例:批量操作确认

Controller:

// src/Controller/ItemController.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class ItemController extends AbstractController
{
    #[Route('/items/{id}/delete', name: 'delete_item', methods: ['POST'])]
    public function deleteItem(Item $item, Request $request, CsrfTokenManagerInterface $csrfTokenManager): Response
    {
        // 验证CSRF token
        $submittedToken = $request->request->get('_token');
        if (!$csrfTokenManager->isTokenValid(new CsrfToken('delete_item', $submittedToken))) {
            $this->addFlash('error', '无效的CSRF令牌');
            return $this->redirectToRoute('items_list');
        }
        // 删除逻辑
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->remove($item);
        $entityManager->flush();
        $this->addFlash('success', '项目已成功删除');
        return $this->redirectToRoute('items_list');
    }
    #[Route('/items/{id}/delete-form', name: 'delete_item_form')]
    public function deleteForm(Item $item): Response
    {
        // 创建专用删除表单(可选)
        $form = $this->createFormBuilder()
            ->setAction($this->generateUrl('delete_item', ['id' => $item->getId()]))
            ->setMethod('POST')
            ->add('submit', SubmitType::class, [
                'label' => '确认删除',
                'attr' => ['class' => 'btn btn-danger']
            ])
            ->getForm();
        return $this->render('item/delete_confirm.html.twig', [
            'item' => $item,
            'form' => $form->createView()
        ]);
    }
}

Twig模板(delete_confirm.html.twig):

{% extends 'base.html.twig' %}
{% block body %}
<div class="container mt-4">
    <div class="card">
        <div class="card-header bg-danger text-white">
            <h3>确认删除操作</h3>
        </div>
        <div class="card-body">
            <p class="mb-4">你确定要删除以下项目吗?</p>
            <div class="alert alert-warning">
                <strong>项目名称:</strong> {{ item.name }}<br>
                <strong>创建时间:</strong> {{ item.createdAt|date('Y-m-d H:i:s') }}
            </div>
            {{ form_start(form) }}
                {{ form_row(form.submit, {
                    'attr': {
                        'class': 'btn btn-danger',
                        'onclick': 'return confirmDelete();'
                    }
                }) }}
                <a href="{{ path('items_list') }}" class="btn btn-secondary ml-2">取消</a>
            {{ form_end(form) }}
        </div>
    </div>
</div>
<script>
function confirmDelete() {
    return confirm('此操作将永久删除项目"{{ item.name }}",是否继续?');
}
</script>
{% endblock %}

使用JavaScript库的封装

创建可复用的确认组件(Symfony UX + Stimulus)

安装Symfony UX:

composer require symfony/stimulus-bundle

创建Stimulus控制器:

// assets/controllers/confirm_submit_controller.js
import { Controller } from '@hotwired/stimulus';
import Swal from 'sweetalert2';
export default class extends Controller {
    static values = {
        message: String,
        title: String,
        confirmText: { type: String, default: '确认' },
        cancelText: { type: String, default: '取消' },
        icon: { type: String, default: 'warning' }
    }
    connect() {
        this.element.addEventListener('click', this.confirm.bind(this));
    }
    async confirm(event) {
        event.preventDefault();
        const result = await Swal.fire({
            title: this.titleValue || '确认操作',
            text: this.messageValue || '确定要执行此操作吗?',
            icon: this.iconValue,
            showCancelButton: true,
            confirmButtonText: this.confirmTextValue,
            cancelButtonText: this.cancelTextValue,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6'
        });
        if (result.isConfirmed) {
            // 提交表单
            if (this.element.tagName === 'FORM') {
                this.element.submit();
            } else if (this.element.tagName === 'BUTTON' && this.element.form) {
                this.element.form.submit();
            }
        }
    }
}

在Twig中使用:

<form action="{{ path('delete_item', {'id': item.id}) }}" method="POST"
      data-controller="confirm-submit"
      data-confirm-submit-message-value="确定要删除项目"{{ item.name }}吗?"
      data-confirm-submit-title-value="删除确认"
      data-confirm-submit-icon-value="warning">
    <input type="hidden" name="_method" value="DELETE">
    {{ form_row(form._token) }}
    <button type="submit" class="btn btn-danger">删除</button>
</form>

最佳实践建议

安全性考虑:

  1. 始终使用CSRF保护
  2. 验证请求方法(POST、DELETE等)
  3. 验证用户权限(如isGranted()

用户体验建议:

  1. 提供清晰的确认信息
  2. 允许取消操作
  3. 显示操作结果反馈(flash消息)
  4. 对不可逆操作(如删除)使用醒目的警告样式

代码结构建议:

// 可以将删除逻辑封装到Voter或Service中
class ItemVoter extends Voter
{
    protected function supports($attribute, $subject): bool
    {
        return $attribute === 'DELETE' && $subject instanceof Item;
    }
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
    {
        // 检查用户是否有删除权限
    }
}
// 在Controller中使用
$this->denyAccessUnlessGranted('DELETE', $item);

通过以上方法,你可以根据项目需求选择合适的确认弹窗实现方式,从简单的confirm()到复杂的SweetAlert2集成,都能很好地配合Symfony表单使用。

抱歉,评论功能暂时关闭!