本文目录导读:

我来为你详细介绍Symfony Form与Bootstrap模态框的结合使用。
基本结构
Twig模板中的模态框
{# templates/demo/index.html.twig #}
<!-- 触发按钮 -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">
新增记录
</button>
<!-- 模态框 -->
<div class="modal fade" id="createModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新增记录</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
{{ form_start(form) }}
<div class="modal-body">
{{ form_widget(form) }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
创建Symfony表单
表单类型
// src/Form/ProductType.php
namespace App\Form;
use App\Entity\Product;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, [
'label' => '产品名称',
'attr' => ['class' => 'form-control']
])
->add('price', NumberType::class, [
'label' => '价格',
'attr' => ['class' => 'form-control']
])
->add('description', TextareaType::class, [
'label' => '描述',
'required' => false,
'attr' => ['class' => 'form-control', 'rows' => 3]
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
}
控制器处理
创建和编辑
// src/Controller/ProductController.php
namespace App\Controller;
use App\Entity\Product;
use App\Form\ProductType;
use App\Repository\ProductRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ProductController extends AbstractController
{
#[Route('/products', name: 'product_list')]
public function index(ProductRepository $repository, Request $request, EntityManagerInterface $em): Response
{
// 创建表单
$product = new Product();
$createForm = $this->createForm(ProductType::class, $product, [
'action' => $this->generateUrl('product_create'),
'method' => 'POST',
]);
// 获取所有产品
$products = $repository->findAll();
return $this->render('product/index.html.twig', [
'products' => $products,
'form' => $createForm->createView(),
]);
}
#[Route('/product/create', name: 'product_create')]
public function create(Request $request, EntityManagerInterface $em): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($product);
$em->flush();
$this->addFlash('success', '产品创建成功!');
if ($request->isXmlHttpRequest()) {
return $this->json([
'success' => true,
'product' => [
'id' => $product->getId(),
'name' => $product->getName(),
'price' => $product->getPrice(),
]
]);
}
return $this->redirectToRoute('product_list');
}
return $this->json([
'success' => false,
'errors' => $this->getFormErrors($form)
], 400);
}
private function getFormErrors($form): array
{
$errors = [];
foreach ($form->getErrors(true) as $error) {
$errors[] = $error->getMessage();
}
return $errors;
}
}
编辑模态框
Twig模板 - 编辑模态框
{# templates/product/index.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mt-4">
<h1>产品列表</h1>
<!-- 新增按钮 -->
<button type="button" class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#createModal">
新增产品
</button>
<!-- 产品列表 -->
<table class="table" id="productTable">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>
<button class="btn btn-sm btn-warning edit-btn"
data-id="{{ product.id }}"
data-bs-toggle="modal"
data-bs-target="#editModal">
编辑
</button>
<button class="btn btn-sm btn-danger delete-btn"
data-id="{{ product.id }}">
删除
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 创建模态框 -->
{% include 'product/_create_modal.html.twig' with {'form': form} %}
<!-- 编辑模态框 -->
{% include 'product/_edit_modal.html.twig' %}
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('js/product.js') }}"></script>
{% endblock %}
创建模态框组件
{# templates/product/_create_modal.html.twig #}
<div class="modal fade" id="createModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新增产品</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
{{ form_start(form, {'attr': {'id': 'createForm', 'class': 'needs-validation'}}) }}
<div class="modal-body">
{{ form_widget(form) }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
编辑模态框组件
{# templates/product/_edit_modal.html.twig #}
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑产品</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="editForm" method="POST" action="">
<div class="modal-body">
<div class="mb-3">
<label for="edit_name" class="form-label">产品名称</label>
<input type="text" id="edit_name" name="product[name]"
class="form-control" required>
</div>
<div class="mb-3">
<label for="edit_price" class="form-label">价格</label>
<input type="number" id="edit_price" name="product[price]"
class="form-control" step="0.01" required>
</div>
<div class="mb-3">
<label for="edit_description" class="form-label">描述</label>
<textarea id="edit_description" name="product[description]"
class="form-control" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">更新</button>
</div>
</form>
</div>
</div>
</div>
JavaScript处理
// public/js/product.js
document.addEventListener('DOMContentLoaded', function() {
// 编辑按钮点击事件
document.querySelectorAll('.edit-btn').forEach(button => {
button.addEventListener('click', function() {
const productId = this.dataset.id;
loadProductData(productId);
});
});
// 编辑表单提交
document.getElementById('editForm').addEventListener('submit', function(e) {
e.preventDefault();
updateProduct(this);
});
// 删除按钮点击事件
document.querySelectorAll('.delete-btn').forEach(button => {
button.addEventListener('click', function() {
if (confirm('确定要删除这个产品吗?')) {
deleteProduct(this.dataset.id);
}
});
});
});
// 加载产品数据到编辑模态框
function loadProductData(id) {
fetch(`/product/${id}/edit`)
.then(response => response.json())
.then(data => {
document.getElementById('edit_name').value = data.name;
document.getElementById('edit_price').value = data.price;
document.getElementById('edit_description').value = data.description;
// 设置表单action
document.getElementById('editForm').action = `/product/${id}/update`;
})
.catch(error => console.error('Error:', error));
}
// 更新产品
function updateProduct(form) {
const formData = new FormData(form);
fetch(form.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
modal.hide();
// 刷新页面或更新表格
location.reload();
} else {
alert('更新失败: ' + data.errors.join(', '));
}
})
.catch(error => console.error('Error:', error));
}
// 删除产品
function deleteProduct(id) {
fetch(`/product/${id}/delete`, {
method: 'DELETE',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('删除失败');
}
})
.catch(error => console.error('Error:', error));
}
// AJAX创建产品
document.getElementById('createForm')?.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch(this.action, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const modal = bootstrap.Modal.getInstance(document.getElementById('createModal'));
modal.hide();
location.reload();
} else {
alert('创建失败: ' + data.errors.join(', '));
}
})
.catch(error => console.error('Error:', error));
});
编辑控制器方法
// 添加到 ProductController.php
#[Route('/product/{id}/edit', name: 'product_edit_data')]
public function editData(Product $product): Response
{
return $this->json([
'id' => $product->getId(),
'name' => $product->getName(),
'price' => $product->getPrice(),
'description' => $product->getDescription(),
]);
}
#[Route('/product/{id}/update', name: 'product_update', methods: ['POST'])]
public function update(Request $request, Product $product, EntityManagerInterface $em): Response
{
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->flush();
if ($request->isXmlHttpRequest()) {
return $this->json([
'success' => true,
'product' => [
'id' => $product->getId(),
'name' => $product->getName(),
'price' => $product->getPrice(),
]
]);
}
return $this->redirectToRoute('product_list');
}
return $this->json([
'success' => false,
'errors' => $this->getFormErrors($form)
], 400);
}
#[Route('/product/{id}/delete', name: 'product_delete', methods: ['DELETE'])]
public function delete(Product $product, EntityManagerInterface $em): Response
{
$em->remove($product);
$em->flush();
return $this->json(['success' => true]);
}
最佳实践建议
表单验证
// 在实体中添加验证
use Symfony\Component\Validator\Constraints as Assert;
class Product
{
/**
* @Assert\NotBlank(message="产品名称不能为空")
* @Assert\Length(min=2, max=50)
*/
private $name;
/**
* @Assert\NotBlank
* @Assert\Positive
*/
private $price;
}
CSRF保护
{# 在模态框表单中添加CSRF token #}
<input type="hidden" name="_token" value="{{ csrf_token('product-form') }}">
这样你就拥有了一个完整的Symfony表单与模态框的结合解决方案,包括创建、编辑和删除功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播