本文目录导读:

在 Symfony 项目中,Controller 抽象是一个重要的概念,主要通过继承、接口和特性(Traits)来实现,以下是 Symfony Controller 抽象的核心实现方式:
基础抽象类方式
AbstractController(推荐方式)
<?php
// src/Controller/AbstractApiController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Serializer\SerializerInterface;
abstract class AbstractApiController extends AbstractController
{
protected SerializerInterface $serializer;
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
// 统一的 JSON 响应方法
protected function jsonResponse(mixed $data, int $status = 200, array $headers = []): JsonResponse
{
return $this->json($data, $status, $headers, [
'json_encode_options' => JSON_UNESCAPED_UNICODE
]);
}
// 统一的错误响应
protected function errorResponse(string $message, int $status = 400): JsonResponse
{
return $this->json([
'success' => false,
'error' => $message
], $status);
}
// 验证请求数据
protected function validateRequest(array $data, array $rules): array
{
$errors = [];
foreach ($rules as $field => $required) {
if ($required && !isset($data[$field])) {
$errors[$field] = "Field {$field} is required";
}
}
return $errors;
}
}
接口抽象方式
<?php
// src/Controller/ControllerInterface.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
interface ControllerInterface
{
public function index(Request $request): Response;
public function show(int $id, Request $request): Response;
public function create(Request $request): Response;
public function update(int $id, Request $request): Response;
public function delete(int $id, Request $request): Response;
}
Trait 组合方式
<?php
// src/Traits/CrudOperationsTrait.php
namespace App\Traits;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
trait CrudOperationsTrait
{
protected function findAll(string $entityClass): JsonResponse
{
$entities = $this->getDoctrine()
->getRepository($entityClass)
->findAll();
return $this->json($entities);
}
protected function findById(string $entityClass, int $id): JsonResponse
{
$entity = $this->getDoctrine()
->getRepository($entityClass)
->find($id);
if (!$entity) {
return $this->json(['error' => 'Not found'], 404);
}
return $this->json($entity);
}
protected function createEntity(string $entityClass, array $data): JsonResponse
{
$entity = new $entityClass();
// 使用 Symfony Serializer 或手动设置属性
return $this->json($entity, 201);
}
}
// 使用 Trait 的 Controller
class ProductController extends AbstractController
{
use CrudOperationsTrait;
public function list(): JsonResponse
{
return $this->findAll(Product::class);
}
}
完整的实际项目示例
<?php
// src/Controller/BaseApiController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Doctrine\ORM\EntityManagerInterface;
abstract class BaseApiController extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager,
protected readonly SerializerInterface $serializer,
protected readonly ValidatorInterface $validator
) {}
// 通用查询方法
protected function findOneOrFail(string $entityClass, int $id): object
{
$entity = $this->entityManager
->getRepository($entityClass)
->find($id);
if (!$entity) {
throw new NotFoundHttpException(
sprintf('Entity %s with id %d not found', $entityClass, $id)
);
}
return $entity;
}
// 通用分页查询
protected function paginateQuery(
string $entityClass,
int $page = 1,
int $limit = 10
): array {
$repository = $this->entityManager->getRepository($entityClass);
$paginator = $repository->createQueryBuilder('e')
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit)
->getQuery()
->getResult();
$total = $repository->createQueryBuilder('e')
->select('COUNT(e.id)')
->getQuery()
->getSingleScalarResult();
return [
'data' => $paginator,
'pagination' => [
'page' => $page,
'limit' => $limit,
'total' => $total,
'pages' => ceil($total / $limit)
]
];
}
// 通用实体保存
protected function saveEntity(object $entity, bool $flush = true): void
{
$errors = $this->validator->validate($entity);
if (count($errors) > 0) {
throw new ValidationException((string) $errors);
}
$this->entityManager->persist($entity);
if ($flush) {
$this->entityManager->flush();
}
}
// 统一成功响应
protected function successResponse(
mixed $data = null,
string $message = 'Success',
int $status = 200
): JsonResponse {
return $this->json([
'success' => true,
'message' => $message,
'data' => $data
], $status);
}
}
// 具体 Controller 实现
#[Route('/api/products')]
class ProductController extends BaseApiController
{
#[Route('', methods: ['GET'])]
public function index(Request $request): JsonResponse
{
$page = $request->query->getInt('page', 1);
$limit = $request->query->getInt('limit', 10);
$products = $this->paginateQuery(Product::class, $page, $limit);
return $this->successResponse($products);
}
#[Route('/{id}', methods: ['GET'])]
public function show(int $id): JsonResponse
{
$product = $this->findOneOrFail(Product::class, $id);
return $this->successResponse($product);
}
#[Route('', methods: ['POST'])]
public function create(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true);
$product = new Product();
$product->setName($data['name']);
$product->setPrice($data['price']);
$this->saveEntity($product);
return $this->successResponse($product, 'Product created', 201);
}
}
最佳实践建议
- 保持抽象层轻量:不要在抽象层添加过多逻辑,保持职责单一
- 使用依赖注入:通过构造函数注入通用依赖
- 提供便捷方法:如统一的响应格式、错误处理等
- 接口分离:为不同功能定义清晰的接口方法
- 参数验证:在抽象层提供参数验证和格式化方法
- 异常处理:统一异常捕获和响应格式
通过这种方式,可以大大提高代码复用性,保持控制器代码的整洁和一致性。