Symfony

技术栈
后端框架
phpenterprisecomponentstwigdoctrinebundles

概览

Symfony\n\nSymfony 是 PHP 生态中最成熟的企业级框架,由 SensioLabs 于 2005 年创建。它以可复用的 PHP 组件为核心,被众多项目(包括 Laravel)作为底层依赖。\n\n### 核心特性\n\n- 可复用组件:50+ 独立组件(HttpFoundation、Routing、Console 等)\n- Twig 模板引擎:快速、安全、灵活的模板系统\n- Doctrine ORM:强大的数据库抽象层\n- Bundle 系统:模块化架构,按需组装\n- 依赖注入容器:企业级 DI/IoC 容器\n- 事件调度器:解耦的事件系统\n- 表单组件:强大的表单构建与验证\n- API Platform:快速构建 REST/GraphQL API

安装

1. 环境准备

  • OS:Linux / macOS / Windows (WSL2)
  • PHP:>= 8.1(Symfony 6.x),>= 8.2(Symfony 7.x)
  • PHP 扩展:Ctype, iconv, JSON, PCRE, Session, SimpleXML, Tokenizer
  • Composer:最新稳定版
  • Symfony CLI:推荐用于开发环境
  • 数据库:MySQL 8+ / PostgreSQL 13+ / SQLite

2. 安装命令

安装 Symfony CLI

# macOS
brew install symfony-cli/tap/symfony-cli

# Linux
curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | sudo -E bash
sudo apt install symfony-cli

# Windows
scoop install symfony-cli

创建新项目

# 传统 Web 应用
symfony new my-project --webapp

# 微服务 / API 最小项目
symfony new my-api

# 通过 Composer
composer create-project symfony/skeleton my-project
cd my-project
composer require webapp  # 添加 Web 功能

启动开发服务器

symfony serve -d
# 访问 http://localhost:8000

3. 常见安装问题

flex 插件缺失

composer require symfony/flex

权限问题

chmod -R 775 var/

国内加速

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

SSL 证书验证失败

# 开发环境临时关闭
symfony serve --allow-http

示例

Hello World:Symfony 控制器与路由

目标

创建 Symfony 最小 API 端点,展示路由注解、控制器和 JSON 响应。

完整代码

1. 创建控制器

php bin/console make:controller HelloController

2. 编辑 src/Controller/HelloController.php

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class HelloController extends AbstractController
{
    #[Route('/api/hello', name: 'hello_index', methods: ['GET'])]
    public function index(): JsonResponse
    {
        return $this->json([
            'message' => 'Hello, Vibe!',
            'framework' => 'Symfony',
            'php_version' => PHP_VERSION,
            'timestamp' => (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM),
        ]);
    }

    #[Route('/api/hello/{name}', name: 'hello_greet', methods: ['GET'])]
    public function greet(string $name): JsonResponse
    {
        return $this->json([
            'message' => "Hello, {$name}!",
            'powered_by' => 'Symfony',
        ]);
    }
}

运行步骤

symfony serve -d
# 或 php -S localhost:8000 -t public/

预期输出

curl http://localhost:8000/api/hello
# {"message":"Hello, Vibe!","framework":"Symfony","php_version":"8.3.0","timestamp":"2024-01-01T00:00:00+00:00"}

curl http://localhost:8000/api/hello/World
# {"message":"Hello, World!","powered_by":"Symfony"}

教程

Symfony 入门教程:构建 Todo API

1. 背景

Symfony 是 PHP 企业级首选框架。本教程带你构建一个 Todo REST API,掌握控制器、ORM (Doctrine)、验证和序列化的核心用法。

2. 前置概念

概念 说明
Entity Doctrine ORM 映射到数据库表
Repository 实体查询类
Validation Symfony Validator 约束验证
Serializer 对象 ↔ JSON 序列化
Maker Bundle make: 命令快速生成代码

3. 分步操作

步骤一:创建项目

symfony new todo-api --webapp
cd todo-api

步骤二:创建 Todo 实体

php bin/console make:entity Todo

按提示输入字段:

  • titlestring255 → not null
  • descriptiontext → nullable
  • completedboolean → default false

编辑 src/Entity/Todo.php 添加验证:

<?php

namespace App\Entity;

use App\Repository\TodoRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: TodoRepository::class)]
class Todo
{
    #[ORM\Id, ORM\GeneratedValue, ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    #[Assert\NotBlank, Assert\Length(min: 3, max: 255)]
    private ?string $title = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $description = null;

    #[ORM\Column]
    private bool $completed = false;

    // getters & setters...
}

步骤三:迁移数据库

php bin/console make:migration
php bin/console doctrine:migrations:migrate

步骤四:创建控制器

php bin/console make:controller TodoController
<?php

namespace App\Controller;

use App\Entity\Todo;
use App\Repository\TodoRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;

#[Route('/api/todos')]
class TodoController extends AbstractController
{
    #[Route('', methods: ['GET'])]
    public function index(TodoRepository $repo): JsonResponse
    {
        return $this->json($repo->findAll());
    }

    #[Route('', methods: ['POST'])]
    public function create(Request $request, EntityManagerInterface $em, ValidatorInterface $validator): JsonResponse
    {
        $data = json_decode($request->getContent(), true);
        $todo = new Todo();
        $todo->setTitle($data['title'] ?? '');
        if (isset($data['description'])) $todo->setDescription($data['description']);

        $errors = $validator->validate($todo);
        if (count($errors) > 0) {
            return $this->json(['errors' => (string) $errors], 422);
        }

        $em->persist($todo);
        $em->flush();

        return $this->json($todo, 201);
    }

    #[Route('/{id}', methods: ['GET'])]
    public function show(Todo $todo): JsonResponse
    {
        return $this->json($todo);
    }

    #[Route('/{id}', methods: ['PUT'])]
    public function update(Request $request, Todo $todo, EntityManagerInterface $em): JsonResponse
    {
        $data = json_decode($request->getContent(), true);

        if (isset($data['title'])) $todo->setTitle($data['title']);
        if (isset($data['description'])) $todo->setDescription($data['description']);
        if (isset($data['completed'])) $todo->setCompleted($data['completed']);

        $em->flush();

        return $this->json($todo);
    }

    #[Route('/{id}', methods: ['DELETE'])]
    public function delete(Todo $todo, EntityManagerInterface $em): JsonResponse
    {
        $em->remove($todo);
        $em->flush();
        return $this->json(null, 204);
    }
}

4. 验证

curl -X POST http://localhost:8000/api/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"学习 Symfony"}'

curl http://localhost:8000/api/todos

5. 思考题

  1. 如何用 DTO 解耦请求数据和实体?
  2. 如何使用 API Platform 代替手动编写 CRUD?
  3. 如何添加 JWT 认证保护 API?

参考资料

  1. [1] Symfony Team. Symfony Documentation. 2024. https://symfony.com/doc
  2. [2] Fabien Potencier. Symfony: The Fast Track. 2020.
  3. [3] SymfonyCasts. Symfony 6: The Complete Guide. 2022. https://symfonycasts.com