现在在学习Symfony,Doctrine作为官方重点推荐的数库库Bundle,必须要试试!
1、创建Symfony的Demo的Web应用
symfony new symfony_webapp --webapp
正确安装后,目录应该是这样的:
如果你的目录仅仅只有config和vendor目录,那就表明你没有安装完整。原因是symfony时,需要从https://packagist.org下载相应的包,包括Symfony新建应用的模板源码。由于 https://packagist.org限速或者封锁的原因,不能下载完整的包。这时候,你需要配置一下镜像:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
然后,再执行一遍symfony的创建新应用命令。
2、启动Symfony官方开发的Web Server,根据提示,访问新应用的页面
Web Server的启动命令:
cd symfony_webapp symfony server:start
下图红框标记部分,就是启动的URL地址:
3、创建Student Entity和数据表
- 需要在.env中配置数据库连接,我用的MariaDB:
DATABASE_URL="mysql://root:123456@127.0.0.1:3306/symfony_webapp?serverVersion=10.1.34-MariaDB&charset=utf8mb4"
- 在项目目录下,命令行窗口,运行命令:
php bin/console make:entity
按照提示,输入表名、字段属性(字段名、类型、长度、是否允许为null等)。完了之后,会在src\Entity\
目录下,创建实体类,比如Student、Teacher等。同时,还会在src\Repository\
目录下,创建实体对应的数据存取类,比如StudentRepository,该类除了构造器之外,还有两个默认被注释的方法,findByExampleField
、findOneBySomeField
。
- 在命令行运行生成迁移文件:
php bin/console make:migration
在migrations
目录下,会产生创建表的类文件类,文件名是Version+时间戳,比如Version20250911130132.php。
- 执行所有未应用的迁移,实际修改数据库结构
symfony console doctrine:migrations:migrate
执行完毕后,会在数据库中创建表。
4、实现新增学员
-
创建Student的Controller类
php bin/console make:controller StudentController
执行后,会创建src\Controller\StudentController.php
,或者手工创建也是可以的。 -
在StudentController类中,新增/student/create路由及方法
#[Route('/student/create', name: 'student_create')]
public function createStudent(EntityManagerInterface $entityManager,Request $request): Response
{$student = new Student();$form = $this->createForm(StudentType::class, $student);$form->handleRequest($request);if ($form->isSubmitted()) {if ($form->isValid()) {$entityManager->persist($student);$entityManager->flush();$this->addFlash('success', 'Student created successfully.');return $this->redirectToRoute('student_list');}}return $this->render('student/edit.html.twig', ['form' => $form,]);
}
- 创建StudentType,该类是一个form
class StudentType extends AbstractType
{public function buildForm(FormBuilderInterface $builder, array $options){$builder->add('first_name', TextType::class)->add('last_name', TextType::class)->add('birthdate', DateType::class)->add('description', TextareaType::class)->add('save', SubmitType::class, ['label' => 'Create']);}
}
- edit.html.twig的代码如下:
{% extends 'base.html.twig' %}{% block body %}{{ form_start(form) }}<div class="row"><div class="col-md-6">{{ form_row(form.firstName) }}</div><div class="col-md-6">{{ form_row(form.lastName) }}</div></div><div class="row"><div class="col-md-6">{{ form_row(form.birthdate) }}</div></div><div class="row"><div class="col-md-12">{{ form_row(form.description) }}</div></div>{{ form_end(form) }}
{% endblock %}
按照上面4步,就可以完成一个学生的新增了,具体功能包括:
- 新增
- 保存数据
- 保存时,验证数据的有效性验证
5、实现学员列表查询
- 在StudentController中,定义路由和列表查询的方法
#[Route('/students', name: 'student_list')]public function list(Request $request, EntityManagerInterface $em): Response{$initialData = ['firstName' => $request->query->get('firstName'),'lastName' => $request->query->get('lastName'),'birthdate' => $request->query->get('birthdate') ?\DateTime::createFromFormat('Y-m-d', $request->query->get('birthdate')) : null];// 1. 创建查询表单$form = $this->createForm(StudentSearchType::class,$initialData);$form->handleRequest($request);// 2. 初始化查询构建器$repository = $em->getRepository(Student::class);$queryBuilder = $repository->createQueryBuilder('s');$searchData = $form->isSubmitted() ? $form->getData() : $request->query->all();// 3. 处理表单提交并构建查询条件if ($form->isSubmitted() && $form->isValid()) {return $this->redirectToRoute('student_list', ['firstName' => $form->get('firstName')->getData(),'lastName' => $form->get('lastName')->getData(),'birthdate' => $form->get('birthdate')->getData()?->format('Y-m-d'),]);}if (!empty($searchData['firstName'])) {$queryBuilder->andWhere('s.firstName LIKE :firstName')->setParameter('firstName', '%' . $searchData['firstName'] . '%');}if (!empty($searchData['lastName'])) {$queryBuilder->andWhere('s.lastName LIKE :lastName')->setParameter('lastName', '%' . $searchData['lastName'] . '%');}if (!empty($searchData['birthdate'])) {$queryBuilder->andWhere('s.birthdate = :birthdate')->setParameter('birthdate', $searchData['birthdate']);}// 4. 执行查询$query = $queryBuilder->getQuery();$students = $query->getResult();// 5. 渲染模板return $this->render('student/list.html.twig', ['form' => $form->createView(),'students' => $students,]);}
2、定义StudentSearchType,这个是列表上方的查询条件
class StudentSearchType extends AbstractType
{public function buildForm(FormBuilderInterface $builder, array $options){$builder->add('firstName', TextType::class, ['label' => 'First Name','required' => false,])->add('lastName', TextType::class, ['label' => 'Last Name','required' => false,])->add('birthdate', DateType::class, ['label' => 'Birth Date','required' => false,'widget' => 'single_text',])->add('search', SubmitType::class, ['label' => 'Search','attr' => ['class' => 'btn btn-primary'],]);;}
}
- 实现list.html.twig,实现列表中,查询条件和数据行的展示:
{% extends 'base.html.twig' %}{% block title %}Student List{% endblock %}{% block body %}
<div class="container mt-4"><h1>Student Search</h1>{# 查询表单 #}
{# {{ form_start(form, { attr: { 'data-turbo': 'false' } }) }}#}{{ form_start(form) }}<div class="row"><div class="col-md-3">{{ form_row(form.firstName) }}</div><div class="col-md-3">{{ form_row(form.lastName) }}</div><div class="col-md-3">{{ form_row(form.birthdate) }}</div><div class="col-md-3 align-self-end">{{ form_row(form.search) }}<a href="{{ path('student_create') }}"class="btn btn-sm btn-outline-primary"><i class="fas fa-edit"></i> create</a></div></div>{{ form_end(form) }}{# 查询结果列表 #}<table class="table table-striped mt-4"><thead><tr><th>First Name</th><th>Last Name</th><th>Birth Date</th><th>Description</th></tr></thead><tbody>{% for student in students %}<tr><td>{{ student.firstName }}</td><td>{{ student.lastName }}</td><td>{{ student.birthdate ? student.birthdate|date('Y-m-d') : '' }}</td><td>{{ student.description }}</td><td>{# 编辑按钮 - 链接到编辑路由 #}<a href="{{ path('student_edit', {id: student.id}) }}"class="btn btn-sm btn-outline-primary"><i class="fas fa-edit"></i> Edit</a>{# 删除按钮 - 使用表单提交防止CSRF #}<form method="post"action="{{ path('student_delete', {id: student.id}) }}"style="display: inline-block;"onsubmit="return confirm('Are you sure to delete this student?');"><input type="hidden" name="_method" value="DELETE"><input type="hidden" name="_token" value="{{ csrf_token('delete' ~ student.id) }}"><button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i> Delete</button></form></td></tr>{% else %}<tr><td colspan="4">No records found</td></tr>{% endfor %}</tbody></table>
</div>
{% endblock %}
列表页面,实现的功能有:
- 按条件查询学员,并分行展示
- 新增学员、编辑学员、删除学员的入口
6、编辑学员
在列表中,定义了编辑学员的入口,现在只需要在StudentController中,定义路由并实现编辑的方法:
#[Route('/student/{id}', name: 'student_edit')]public function edit(EntityManagerInterface $entityManager, StudentRepository $repository,Request $request, Student $student): Response{if (!$student) {throw $this->createNotFoundException('No student found for id '.$student->getId());}$form = $this->createForm(StudentType::class, $student);$form->handleRequest($request);if ($form->isSubmitted()) {if ($form->isValid()) {$entityManager->persist($student);$entityManager->flush();$this->addFlash('success', 'Student created successfully.');return $this->redirectToRoute('student_list');}}return $this->render('student/edit.html.twig', ['form' => $form,]);}
由于编辑页面与新增页面一样,因此,继续复用edit.html.twig
7. 删除学员
在列表中,定义了编辑学员的入口,现在只需要在StudentController中,定义路由并实现删除的方法:
#[Route('/students/{id}', name: 'student_delete',methods: ['DELETE'])]public function delete(EntityManagerInterface $entityManager, StudentRepository $repository, Student $student): Response{if (!$student) {throw $this->createNotFoundException('No student found for id '.$student->getId());}$entityManager->remove($student);$entityManager->flush();return $this->redirectToRoute('student_list');}
这样,一个简单的学员管理的增、删、查、改就完成了。