PHP项目Symfony form与永久删除

wen PHP项目 2

本文目录导读:

PHP项目Symfony form与永久删除

  1. Symfony Form 基础
  2. 永久删除功能实现

我来详细介绍 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());
    }
}

这样实现后,你就拥有了完整的表单处理和删除功能,包括软删除、永久删除、恢复和批量操作,以及权限控制。

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