本文目录导读:

我来详细介绍 Symfony Form 和永久删除功能的实现。
Symfony Form 基础
创建表单类型
// 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;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
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' => 5]
])
->add('submit', SubmitType::class, [
'label' => '保存',
'attr' => ['class' => 'btn btn-primary']
]);
}
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 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('/product/new', name: 'product_new')]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($product);
$entityManager->flush();
$this->addFlash('success', '产品创建成功!');
return $this->redirectToRoute('product_list');
}
return $this->render('product/new.html.twig', [
'form' => $form->createView()
]);
}
#[Route('/product/edit/{id}', name: 'product_edit')]
public function edit(Request $request, Product $product, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
$this->addFlash('success', '产品更新成功!');
return $this->redirectToRoute('product_list');
}
return $this->render('product/edit.html.twig', [
'form' => $form->createView(),
'product' => $product
]);
}
}
永久删除功能实现
实体配置(软删除 vs 硬删除)
// src/Entity/Product.php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
// ... 其他字段
// 软删除字段
#[ORM\Column(type: 'datetime', nullable: true)]
private ?\DateTimeInterface $deletedAt = null;
// Getter 和 Setter
public function getDeletedAt(): ?\DateTimeInterface
{
return $this->deletedAt;
}
public function setDeletedAt(?\DateTimeInterface $deletedAt): self
{
$this->deletedAt = $deletedAt;
return $this;
}
// 检查是否已删除
public function isDeleted(): bool
{
return $this->deletedAt !== null;
}
// 执行软删除
public function softDelete(): self
{
$this->deletedAt = new \DateTime();
return $this;
}
// 恢复软删除
public function restore(): self
{
$this->deletedAt = null;
return $this;
}
}
软删除仓库方法
// src/Repository/ProductRepository.php
namespace App\Repository;
use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ProductRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
// 查询未删除的产品
public function findActive(): array
{
return $this->createQueryBuilder('p')
->where('p.deletedAt IS NULL')
->orderBy('p.id', 'DESC')
->getQuery()
->getResult();
}
// 查询已删除的产品(回收站)
public function findDeleted(): array
{
return $this->createQueryBuilder('p')
->where('p.deletedAt IS NOT NULL')
->orderBy('p.deletedAt', 'DESC')
->getQuery()
->getResult();
}
// 永久删除过期的软删除记录
public function permanentDeleteOldRecords(int $days = 30): int
{
$date = new \DateTime("-{$days} days");
return $this->createQueryBuilder('p')
->delete()
->where('p.deletedAt IS NOT NULL')
->andWhere('p.deletedAt < :date')
->setParameter('date', $date)
->getQuery()
->execute();
}
}
删除控制器方法
// src/Controller/ProductController.php(续)
class ProductController extends AbstractController
{
// 软删除
#[Route('/product/soft-delete/{id}', name: 'product_soft_delete', methods: ['POST'])]
public function softDelete(Request $request, Product $product, EntityManagerInterface $entityManager): Response
{
// CSRF 保护
if ($this->isCsrfTokenValid('delete'.$product->getId(), $request->request->get('_token'))) {
$product->softDelete();
$entityManager->flush();
$this->addFlash('success', '产品已移至回收站');
}
return $this->redirectToRoute('product_list');
}
// 永久删除
#[Route('/product/permanent-delete/{id}', name: 'product_permanent_delete', methods: ['POST'])]
public function permanentDelete(Request $request, Product $product, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('permanent_delete'.$product->getId(), $request->request->get('_token'))) {
$entityManager->remove($product);
$entityManager->flush();
$this->addFlash('success', '产品已永久删除');
}
return $this->redirectToRoute('product_list');
}
// 恢复软删除
#[Route('/product/restore/{id}', name: 'product_restore', methods: ['POST'])]
public function restore(Request $request, Product $product, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('restore'.$product->getId(), $request->request->get('_token'))) {
$product->restore();
$entityManager->flush();
$this->addFlash('success', '产品已恢复');
}
return $this->redirectToRoute('product_trash');
}
// 清空回收站
#[Route('/product/empty-trash', name: 'product_empty_trash', methods: ['POST'])]
public function emptyTrash(ProductRepository $productRepository, EntityManagerInterface $entityManager): Response
{
$deletedProducts = $productRepository->findDeleted();
foreach ($deletedProducts as $product) {
$entityManager->remove($product);
}
$entityManager->flush();
$this->addFlash('success', '回收站已清空');
return $this->redirectToRoute('product_list');
}
}
Twig 模板示例
{# templates/product/list.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mt-4">
<h1>产品列表</h1>
<table class="table">
<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>
<a href="{{ path('product_edit', {'id': product.id}) }}" class="btn btn-sm btn-warning">编辑</a>
{# 软删除按钮 #}
<form method="post" action="{{ path('product_soft_delete', {'id': product.id}) }}" style="display: inline-block;" onsubmit="return confirm('确定要删除此产品吗?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ product.id) }}">
<button class="btn btn-sm btn-danger">删除</button>
</form>
{# 永久删除按钮 #}
<form method="post" action="{{ path('product_permanent_delete', {'id': product.id}) }}" style="display: inline-block;" onsubmit="return confirm('确定要永久删除此产品吗?此操作不可恢复!');">
<input type="hidden" name="_token" value="{{ csrf_token('permanent_delete' ~ product.id) }}">
<button class="btn btn-sm btn-dark">永久删除</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
批量操作(回收站)
// src/Controller/ProductController.php(续)
#[Route('/product/trash', name: 'product_trash')]
public function trash(ProductRepository $productRepository): Response
{
$deletedProducts = $productRepository->findDeleted();
return $this->render('product/trash.html.twig', [
'products' => $deletedProducts
]);
}
#[Route('/product/batch-delete', name: 'product_batch_delete', methods: ['POST'])]
public function batchDelete(Request $request, EntityManagerInterface $entityManager): Response
{
$productIds = $request->request->get('product_ids', []);
if (!empty($productIds)) {
$products = $entityManager->getRepository(Product::class)->findBy(['id' => $productIds]);
foreach ($products as $product) {
$entityManager->remove($product);
}
$entityManager->flush();
$this->addFlash('success', '批量删除成功');
}
return $this->redirectToRoute('product_list');
}
安全考虑
// 添加 Voter 进行权限控制
// src/Security/ProductVoter.php
namespace App\Security;
use App\Entity\Product;
use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class ProductVoter extends Voter
{
const DELETE = 'delete';
const PERMANENT_DELETE = 'permanent_delete';
protected function supports(string $attribute, $subject): bool
{
if (!in_array($attribute, [self::DELETE, self::PERMANENT_DELETE])) {
return false;
}
if (!$subject instanceof Product) {
return false;
}
return true;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
/** @var Product $product */
$product = $subject;
return match($attribute) {
self::DELETE => $this->canDelete($product, $user),
self::PERMANENT_DELETE => $this->canPermanentDelete($product, $user),
default => false,
};
}
private function canDelete(Product $product, User $user): bool
{
// 只有管理员或创建者可以删除
return $user === $product->getCreatedBy() || in_array('ROLE_ADMIN', $user->getRoles());
}
private function canPermanentDelete(Product $product, User $user): bool
{
// 只有管理员可以永久删除
return in_array('ROLE_ADMIN', $user->getRoles());
}
}
这样实现后,你就拥有了完整的表单处理和删除功能,包括软删除、永久删除、恢复和批量操作,以及权限控制。