我来为你详细介绍在PHP Symfony项目中使用表单和时间轴的实现方案。

Symfony Form 与时间轴集成
基本时间轴表单组件
// src/Form/Type/TimelineType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TimelineType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title', TextType::class, [
'label' => '时间轴标题',
'attr' => ['class' => 'form-control']
])
->add('events', CollectionType::class, [
'entry_type' => TimelineEventType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'prototype' => true,
'prototype_name' => '__event__',
'attr' => [
'class' => 'timeline-events-collection'
]
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Timeline::class,
]);
}
}
时间轴事件表单类型
// src/Form/Type/TimelineEventType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TimelineEventType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('date', DateType::class, [
'widget' => 'single_text',
'label' => '事件日期',
'attr' => ['class' => 'form-control datepicker']
])
->add('time', TimeType::class, [
'widget' => 'single_text',
'label' => '时间',
'required' => false,
'attr' => ['class' => 'form-control timepicker']
])
->add('title', TextType::class, [
'label' => '事件标题',
'attr' => ['class' => 'form-control']
])
->add('description', TextareaType::class, [
'label' => '事件描述',
'required' => false,
'attr' => [
'class' => 'form-control',
'rows' => 3
]
])
->add('type', ChoiceType::class, [
'label' => '事件类型',
'choices' => [
'里程碑' => 'milestone',
'任务' => 'task',
'提醒' => 'reminder',
'通知' => 'notification'
],
'attr' => ['class' => 'form-control']
])
->add('status', ChoiceType::class, [
'label' => '状态',
'choices' => [
'进行中' => 'in_progress',
'已完成' => 'completed',
'已取消' => 'cancelled',
'待开始' => 'pending'
],
'attr' => ['class' => 'form-control']
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => TimelineEvent::class,
]);
}
}
实体类定义
// src/Entity/Timeline.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
#[ORM\Entity(repositoryClass: TimelineRepository::class)]
class Timeline
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(type: 'datetime')]
private ?\DateTimeInterface $createdAt = null;
#[ORM\OneToMany(mappedBy: 'timeline', targetEntity: TimelineEvent::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
private Collection $events;
public function __construct()
{
$this->events = new ArrayCollection();
$this->createdAt = new \DateTime();
}
// Getters and Setters...
public function getEvents(): Collection
{
return $this->events;
}
public function addEvent(TimelineEvent $event): self
{
if (!$this->events->contains($event)) {
$this->events->add($event);
$event->setTimeline($this);
}
return $this;
}
public function removeEvent(TimelineEvent $event): self
{
if ($this->events->removeElement($event)) {
if ($event->getTimeline() === $this) {
$event->setTimeline(null);
}
}
return $this;
}
}
控制器实现
// src/Controller/TimelineController.php
namespace App\Controller;
use App\Entity\Timeline;
use App\Form\Type\TimelineType;
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 TimelineController extends AbstractController
{
#[Route('/timeline/new', name: 'timeline_new')]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$timeline = new Timeline();
$form = $this->createForm(TimelineType::class, $timeline);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($timeline);
$entityManager->flush();
$this->addFlash('success', '时间轴已创建');
return $this->redirectToRoute('timeline_show', ['id' => $timeline->getId()]);
}
return $this->render('timeline/new.html.twig', [
'form' => $form->createView(),
]);
}
#[Route('/timeline/{id}/edit', name: 'timeline_edit')]
public function edit(Request $request, Timeline $timeline, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(TimelineType::class, $timeline);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
$this->addFlash('success', '时间轴已更新');
return $this->redirectToRoute('timeline_show', ['id' => $timeline->getId()]);
}
return $this->render('timeline/edit.html.twig', [
'form' => $form->createView(),
'timeline' => $timeline
]);
}
}
Twig模板(时间轴显示)
{# templates/timeline/show.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mt-4">
<h1>{{ timeline.title }}</h1>
{# 时间轴组件 #}
<div class="timeline">
{% for event in timeline.events %}
<div class="timeline-item">
<div class="timeline-badge {{ event.type }}">
<i class="fas fa-{{ event.type|timeline_icon }}"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">{{ event.title }}</h4>
<p>
<small class="text-muted">
<i class="fas fa-calendar"></i>
{{ event.date|date('Y-m-d') }}
{% if event.time %}
<i class="fas fa-clock"></i>
{{ event.time|date('H:i') }}
{% endif %}
</small>
<span class="label label-{{ event.status|timeline_status_color }}">
{{ event.status|trans }}
</span>
</p>
</div>
{% if event.description %}
<div class="timeline-body">
<p>{{ event.description }}</p>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
JavaScript增强(动态添加事件)
// assets/js/timeline-form.js
class TimelineForm {
constructor() {
this.collectionHolder = document.querySelector('.timeline-events-collection');
this.addButton = document.createElement('button');
this.setupCollection();
}
setupCollection() {
this.addButton.innerText = '添加事件';
this.addButton.className = 'btn btn-primary mt-2';
this.collectionHolder.parentNode.appendChild(this.addButton);
this.addButton.addEventListener('click', (e) => {
this.addEvent();
});
// 添加删除按钮
this.collectionHolder.querySelectorAll('.timeline-event-item').forEach((item) => {
this.addDeleteButton(item);
});
}
addEvent() {
const prototype = this.collectionHolder.dataset.prototype;
const index = this.collectionHolder.children.length;
const newForm = prototype.replace(/__event__/g, index);
const container = document.createElement('div');
container.className = 'timeline-event-item';
container.innerHTML = newForm;
this.addDeleteButton(container);
this.collectionHolder.appendChild(container);
}
addDeleteButton(item) {
const deleteButton = document.createElement('button');
deleteButton.innerText = '删除';
deleteButton.className = 'btn btn-danger btn-sm mt-2';
deleteButton.addEventListener('click', () => {
item.remove();
});
item.appendChild(deleteButton);
}
}
// 初始化
document.addEventListener('DOMContentLoaded', () => {
new TimelineForm();
});
CSS样式
/* assets/css/timeline.css */
.timeline {
position: relative;
padding: 20px 0;
}
.timeline::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 50px;
width: 2px;
background: #e9ecef;
}
.timeline-item {
position: relative;
margin-bottom: 30px;
padding-left: 100px;
}
.timeline-badge {
position: absolute;
left: 35px;
top: 0;
width: 30px;
height: 30px;
border-radius: 50%;
text-align: center;
line-height: 30px;
color: white;
z-index: 1;
}
.timeline-badge.milestone { background: #007bff; }
.timeline-badge.task { background: #28a745; }
.timeline-badge.reminder { background: #ffc107; }
.timeline-badge.notification { background: #17a2b8; }
.timeline-panel {
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.timeline-panel::before {
content: '';
position: absolute;
left: 90px;
top: 10px;
border: 10px solid transparent;
border-right-color: #dee2e6;
}
/* 表单样式 */
.timeline-event-item {
padding: 15px;
margin-bottom: 10px;
border: 1px solid #dee2e6;
border-radius: 5px;
background: #f8f9fa;
}
这个实现提供了完整的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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播