PHP项目Symfony form与数据持久化

wen PHP项目 2

本文目录导读:

PHP项目Symfony form与数据持久化

  1. Symfony Form基础
  2. 控制器中的表单处理
  3. Twig模板渲染
  4. 复杂数据类型处理
  5. 表单事件与动态处理
  6. 数据验证最佳实践
  7. 性能优化建议
  8. 数据持久化注意事项

我将详细介绍Symfony框架中Form组件和数据持久化的集成使用。

Symfony Form基础

创建表单类

// src/Form/ArticleType.php
namespace App\Form;
use App\Entity\Article;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ArticleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', TextType::class, [
                'label' => '文章标题',
                'attr' => [
                    'placeholder' => '请输入标题',
                    'class' => 'form-control'
                ]
            ])
            ->add('content', TextareaType::class, [
                'label' => '内容',
                'attr' => ['rows' => 10]
            ])
            ->add('createdAt', DateTimeType::class, [
                'label' => '创建时间',
                'widget' => 'single_text'
            ])
            ->add('save', SubmitType::class, [
                'label' => '保存',
                'attr' => ['class' => 'btn btn-primary']
            ]);
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Article::class,
            'csrf_protection' => true,
            'csrf_field_name' => '_token',
            'csrf_token_id'   => 'article_item'
        ]);
    }
}

实体类定义

// src/Entity/Article.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
#[ORM\Table(name: 'articles')]
class Article
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;
    #[ORM\Column(type: 'string', length: 255)]
    #[Assert\NotBlank(message: '标题不能为空')]
    #[Assert\Length(
        min: 2,
        max: 255,
        minMessage: '标题至少{{ limit }}个字符',
        maxMessage: '标题最多{{ limit }}个字符'
    )]
    private ?string $title = null;
    #[ORM\Column(type: 'text')]
    #[Assert\NotBlank(message: '内容不能为空')]
    private ?string $content = null;
    #[ORM\Column(type: 'datetime')]
    private ?\DateTimeInterface $createdAt = null;
    // Getters and Setters
    public function getId(): ?int { return $this->id; }
    public function getTitle(): ?string { return $this->title; }
    public function setTitle(string $title): self 
    {
        $this->title = $title;
        return $this;
    }
    public function getContent(): ?string { return $this->content; }
    public function setContent(string $content): self 
    {
        $this->content = $content;
        return $this;
    }
    public function getCreatedAt(): ?\DateTimeInterface { return $this->createdAt; }
    public function setCreatedAt(\DateTimeInterface $createdAt): self 
    {
        $this->createdAt = $createdAt;
        return $this;
    }
}

控制器中的表单处理

创建和保存数据

// src/Controller/ArticleController.php
namespace App\Controller;
use App\Entity\Article;
use App\Form\ArticleType;
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 ArticleController extends AbstractController
{
    #[Route('/article/new', name: 'article_new')]
    public function new(Request $request, EntityManagerInterface $entityManager): Response
    {
        // 创建新文章对象
        $article = new Article();
        $article->setCreatedAt(new \DateTime());
        // 创建表单
        $form = $this->createForm(ArticleType::class, $article);
        // 处理请求
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            // 表单验证通过,保存数据
            $entityManager->persist($article);
            $entityManager->flush();
            // 添加flash消息
            $this->addFlash('success', '文章保存成功!');
            // 重定向到文章列表
            return $this->redirectToRoute('article_list');
        }
        // 渲染表单页面
        return $this->render('article/new.html.twig', [
            'form' => $form->createView(),
            'form_title' => '创建新文章'
        ]);
    }
}

编辑和更新数据

#[Route('/article/edit/{id}', name: 'article_edit')]
public function edit(Request $request, Article $article, EntityManagerInterface $entityManager): Response
{
    $form = $this->createForm(ArticleType::class, $article);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        // 不需要调用persist(),因为实体已经被管理
        $entityManager->flush();
        $this->addFlash('success', '文章更新成功!');
        return $this->redirectToRoute('article_list');
    }
    return $this->render('article/edit.html.twig', [
        'form' => $form->createView(),
        'article' => $article,
        'form_title' => '编辑文章'
    ]);
}

删除数据

#[Route('/article/delete/{id}', name: 'article_delete', methods: ['POST'])]
public function delete(Request $request, Article $article, EntityManagerInterface $entityManager): Response
{
    if ($this->isCsrfTokenValid('delete' . $article->getId(), $request->request->get('_token'))) {
        $entityManager->remove($article);
        $entityManager->flush();
        $this->addFlash('success', '文章删除成功!');
    }
    return $this->redirectToRoute('article_list');
}

Twig模板渲染

创建表单模板

{# templates/article/new.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mt-4">
    <h1>{{ form_title }}</h1>
    {{ form_start(form) }}
        <div class="mb-3">
            {{ form_label(form.title, null, {'label_attr': {'class': 'form-label'}}) }}
            {{ form_widget(form.title, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.title) }}
        </div>
        <div class="mb-3">
            {{ form_label(form.content, null, {'label_attr': {'class': 'form-label'}}) }}
            {{ form_widget(form.content, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.content) }}
        </div>
        <div class="mb-3">
            {{ form_label(form.createdAt, null, {'label_attr': {'class': 'form-label'}}) }}
            {{ form_widget(form.createdAt, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.createdAt) }}
        </div>
        <button type="submit" class="btn btn-primary">保存</button>
        <a href="{{ path('article_list') }}" class="btn btn-secondary">取消</a>
    {{ form_end(form) }}
</div>
{% endblock %}

自定义表单渲染

{# 使用form_theme自定义渲染 #}
{% form_theme form with ['bootstrap_5_layout.html.twig'] %}
{% block body %}
    {{ form(form) }}
{% endblock %}
{# 或者完全手动渲染 #}
{% block form_row %}
    <div class="form-group {{ errors|length > 0 ? 'has-error' : '' }}">
        {{ form_label(form) }}
        {{ form_widget(form, {'attr': {'class': 'form-control'}}) }}
        {{ form_errors(form) }}
        {{ form_help(form) }}
    </div>
{% endblock %}

复杂数据类型处理

关联实体处理

// 文章分类实体
#[ORM\Entity]
class Category
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;
    #[ORM\Column(type: 'string', length: 100)]
    private ?string $name = null;
    // 双向关联
    #[ORM\OneToMany(targetEntity: Article::class, mappedBy: 'category')]
    private Collection $articles;
    public function __construct()
    {
        $this->articles = new ArrayCollection();
    }
}
// 在Article实体中添加关联
#[ORM\ManyToOne(targetEntity: Category::class, inversedBy: 'articles')]
#[ORM\JoinColumn(nullable: false)]
private $category;

表单中的关联处理

// ArticleType
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        // ... 其他字段
        ->add('category', EntityType::class, [
            'class' => Category::class,
            'choice_label' => 'name',
            'placeholder' => '选择分类',
            'query_builder' => function (CategoryRepository $er) {
                return $er->createQueryBuilder('c')
                    ->orderBy('c.name', 'ASC');
            },
        ]);
}

表单事件与动态处理

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $article = $event->getData();
            $form = $event->getForm();
            // 根据文章状态动态添加字段
            if ($article && $article->getStatus() === 'published') {
                $form->add('publishDate', DateTimeType::class);
            }
        })
        ->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
            $article = $event->getData();
            // 在提交时修改数据
            if (!$article->getCreatedAt()) {
                $article->setCreatedAt(new \DateTime());
            }
        });
}

数据验证最佳实践

// 创建自定义验证约束
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
#[Attribute]
class ContainsUniqueTitle extends Constraint
{
    public $message = '标题 "{{ value }}" 已经存在。';
}
class ContainsUniqueTitleValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        // 实现验证逻辑
        if (!$this->isUnique($value)) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ value }}', $value)
                ->addViolation();
        }
    }
}

性能优化建议

// 使用批量处理
public function batchProcess(array $articles)
{
    $batchSize = 20;
    $i = 0;
    foreach ($articles as $article) {
        // 修改文章
        $article->setUpdatedAt(new \DateTime());
        if (($i % $batchSize) === 0) {
            $this->entityManager->flush();
            $this->entityManager->clear(); // 清除实体管理器
        }
        $i++;
    }
    $this->entityManager->flush();
}

数据持久化注意事项

  1. 事务处理

    $this->entityManager->beginTransaction();
    try {
     // 执行数据库操作
     $this->entityManager->persist($entity);
     $this->entityManager->flush();
     $this->entityManager->commit();
    } catch (\Exception $e) {
     $this->entityManager->rollback();
     throw $e;
    }
  2. 级联操作

    // 在实体中配置级联
    #[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'article', cascade: ['persist', 'remove'])]
    private $comments;

Symfony Form与Doctrine数据持久化的结合提供了强大而灵活的数据处理能力,能够满足从简单到复杂的各种业务需求,通过合理使用这些功能,可以大大提升开发效率和代码质量。

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