控制器基础 #

一、控制器概述 #

1.1 什么是控制器 #

控制器是处理HTTP请求并返回响应的PHP类。每个控制器包含一个或多个动作方法,每个动作对应一个路由。

text
┌─────────────────────────────────────────────────────┐
│                  控制器工作流程                      │
├─────────────────────────────────────────────────────┤
│                                                     │
│   HTTP请求                                          │
│       ↓                                             │
│   路由匹配                                          │
│       ↓                                             │
│   控制器动作                                        │
│       ↓                                             │
│   处理业务逻辑                                      │
│       ↓                                             │
│   返回响应                                          │
│                                                     │
└─────────────────────────────────────────────────────┘

1.2 控制器命名规范 #

text
控制器命名规范:
├── 类名: {Name}Controller
├── 文件: src/Controller/{Name}Controller.php
├── 方法: 动作方法名
└── 示例: UserController.php → class UserController

二、创建控制器 #

2.1 使用Maker命令 #

bash
# 创建控制器
php bin/console make:controller UserController

# 输出
created: src/Controller/UserController.php
created: templates/user/index.html.twig

2.2 手动创建控制器 #

php
<?php

namespace App\Controller;

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

class UserController extends AbstractController
{
    #[Route('/user', name: 'app_user')]
    public function index(): Response
    {
        return $this->render('user/index.html.twig', [
            'controller_name' => 'UserController',
        ]);
    }
}

2.3 控制器结构解析 #

php
<?php

namespace App\Controller;

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

class HomeController extends AbstractController
{
    #[Route('/', name: 'app_home')]
    public function index(): Response
    {
        return new Response('Hello World!');
    }
}
组成部分 说明
namespace 命名空间,通常为App\Controller
extends AbstractController 继承基础控制器,获取辅助方法
#[Route] 路由属性,定义URL映射
public function 动作方法,必须是public
Response 返回类型,响应对象

三、动作方法 #

3.1 基本动作 #

php
<?php

class PageController extends AbstractController
{
    #[Route('/', name: 'app_home')]
    public function home(): Response
    {
        return new Response('Home Page');
    }

    #[Route('/about', name: 'app_about')]
    public function about(): Response
    {
        return new Response('About Page');
    }

    #[Route('/contact', name: 'app_contact')]
    public function contact(): Response
    {
        return new Response('Contact Page');
    }
}

3.2 带参数的动作 #

php
<?php

class UserController extends AbstractController
{
    #[Route('/user/{id}', name: 'app_user_show', requirements: ['id' => '\d+'])]
    public function show(int $id): Response
    {
        return new Response("User ID: $id");
    }

    #[Route('/category/{category}/product/{productId}', name: 'app_product_show')]
    public function productShow(string $category, int $productId): Response
    {
        return new Response("Category: $category, Product: $productId");
    }
}

3.3 可选参数动作 #

php
<?php

class BlogController extends AbstractController
{
    #[Route('/blog/{page}', name: 'app_blog_list', defaults: ['page' => 1])]
    public function list(int $page): Response
    {
        return new Response("Blog Page: $page");
    }

    #[Route('/search/{keyword}', name: 'app_search', defaults: ['keyword' => null])]
    public function search(?string $keyword): Response
    {
        return new Response("Search: " . ($keyword ?? 'all'));
    }
}

四、返回响应 #

4.1 响应类型 #

php
<?php

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;

class ResponseController extends AbstractController
{
    #[Route('/text', name: 'app_text')]
    public function text(): Response
    {
        return new Response('Plain Text Response');
    }

    #[Route('/html', name: 'app_html')]
    public function html(): Response
    {
        return new Response('<h1>HTML Response</h1>');
    }

    #[Route('/json', name: 'app_json')]
    public function json(): JsonResponse
    {
        return $this->json([
            'status' => 'success',
            'data' => ['id' => 1, 'name' => 'John'],
        ]);
    }

    #[Route('/redirect', name: 'app_redirect')]
    public function redirect(): RedirectResponse
    {
        return $this->redirectToRoute('app_home');
    }

    #[Route('/template', name: 'app_template')]
    public function template(): Response
    {
        return $this->render('page/template.html.twig', [
            'title' => 'Template Page',
        ]);
    }
}

4.2 响应状态码 #

php
<?php

class StatusController extends AbstractController
{
    #[Route('/created', name: 'app_created')]
    public function created(): Response
    {
        return new Response('Created', Response::HTTP_CREATED);
    }

    #[Route('/no-content', name: 'app_no_content')]
    public function noContent(): Response
    {
        return new Response('', Response::HTTP_NO_CONTENT);
    }

    #[Route('/not-found', name: 'app_not_found')]
    public function notFound(): Response
    {
        return new Response('Not Found', Response::HTTP_NOT_FOUND);
    }

    #[Route('/json-created', name: 'app_json_created')]
    public function jsonCreated(): JsonResponse
    {
        return $this->json(
            ['status' => 'created'],
            Response::HTTP_CREATED
        );
    }
}

4.3 响应头设置 #

php
<?php

class HeaderController extends AbstractController
{
    #[Route('/custom-header', name: 'app_custom_header')]
    public function customHeader(): Response
    {
        $response = new Response('Custom Header');
        $response->headers->set('X-Custom-Header', 'value');
        $response->headers->set('Cache-Control', 'no-cache');
        
        return $response;
    }

    #[Route('/json-headers', name: 'app_json_headers')]
    public function jsonHeaders(): JsonResponse
    {
        return $this->json(
            ['data' => 'value'],
            Response::HTTP_OK,
            ['X-API-Version' => '1.0', 'X-Rate-Limit' => '100']
        );
    }
}

五、渲染模板 #

5.1 基本模板渲染 #

php
<?php

class TemplateController extends AbstractController
{
    #[Route('/page', name: 'app_page')]
    public function page(): Response
    {
        return $this->render('page/index.html.twig');
    }

    #[Route('/user/{id}', name: 'app_user_profile')]
    public function profile(int $id): Response
    {
        return $this->render('user/profile.html.twig', [
            'userId' => $id,
            'userName' => 'User ' . $id,
        ]);
    }
}

5.2 模板变量 #

php
<?php

class DashboardController extends AbstractController
{
    #[Route('/dashboard', name: 'app_dashboard')]
    public function index(): Response
    {
        $user = [
            'id' => 1,
            'name' => '张三',
            'email' => 'zhangsan@example.com',
            'roles' => ['ROLE_USER', 'ROLE_ADMIN'],
        ];

        $stats = [
            'totalUsers' => 1000,
            'activeUsers' => 750,
            'newUsers' => 50,
        ];

        return $this->render('dashboard/index.html.twig', [
            'user' => $user,
            'stats' => $stats,
            'title' => 'Dashboard',
            'now' => new \DateTime(),
        ]);
    }
}

5.3 Twig模板示例 #

twig
{# templates/dashboard/index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}{{ title }}{% endblock %}

{% block body %}
<h1>{{ title }}</h1>

<div class="user-info">
    <p>用户: {{ user.name }}</p>
    <p>邮箱: {{ user.email }}</p>
    <p>角色: {{ user.roles|join(', ') }}</p>
</div>

<div class="stats">
    <h2>统计数据</h2>
    <ul>
        <li>总用户: {{ stats.totalUsers }}</li>
        <li>活跃用户: {{ stats.activeUsers }}</li>
        <li>新用户: {{ stats.newUsers }}</li>
    </ul>
</div>

<p>当前时间: {{ now|date('Y-m-d H:i:s') }}</p>
{% endblock %}

六、重定向 #

6.1 路由重定向 #

php
<?php

class RedirectController extends AbstractController
{
    #[Route('/old-home', name: 'app_old_home')]
    public function oldHome(): Response
    {
        return $this->redirectToRoute('app_home');
    }

    #[Route('/old-user/{id}', name: 'app_old_user')]
    public function oldUser(int $id): Response
    {
        return $this->redirectToRoute('app_user_show', ['id' => $id]);
    }

    #[Route('/redirect-permanent', name: 'app_redirect_permanent')]
    public function redirectPermanent(): Response
    {
        return $this->redirectToRoute('app_home', [], 301);
    }
}

6.2 外部重定向 #

php
<?php

class ExternalRedirectController extends AbstractController
{
    #[Route('/symfony-docs', name: 'app_symfony_docs')]
    public function symfonyDocs(): Response
    {
        return $this->redirect('https://symfony.com/doc');
    }

    #[Route('/google', name: 'app_google')]
    public function google(): Response
    {
        return $this->redirect('https://google.com');
    }
}

6.3 引用重定向 #

php
<?php

class ReferrerController extends AbstractController
{
    #[Route('/back', name: 'app_back')]
    public function back(Request $request): Response
    {
        $referer = $request->headers->get('referer');
        
        if ($referer) {
            return $this->redirect($referer);
        }
        
        return $this->redirectToRoute('app_home');
    }
}

七、异常处理 #

7.1 404异常 #

php
<?php

class ProductController extends AbstractController
{
    #[Route('/product/{id}', name: 'app_product_show')]
    public function show(int $id, ProductRepository $repository): Response
    {
        $product = $repository->find($id);

        if (!$product) {
            throw $this->createNotFoundException('产品不存在');
        }

        return $this->render('product/show.html.twig', [
            'product' => $product,
        ]);
    }
}

7.2 自定义异常 #

php
<?php

use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class ErrorController extends AbstractController
{
    #[Route('/error/not-found', name: 'app_error_not_found')]
    public function notFound(): Response
    {
        throw new NotFoundHttpException('页面不存在');
    }

    #[Route('/error/access-denied', name: 'app_error_access_denied')]
    public function accessDenied(): Response
    {
        throw new AccessDeniedHttpException('无权访问');
    }

    #[Route('/error/bad-request', name: 'app_error_bad_request')]
    public function badRequest(): Response
    {
        throw new BadRequestHttpException('错误的请求');
    }
}

7.3 异常监听 #

php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class ExceptionSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::EXCEPTION => 'onKernelException',
        ];
    }

    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();
        
        // 自定义异常处理逻辑
    }
}

八、控制器最佳实践 #

8.1 瘦控制器 #

php
<?php

// 好的做法:控制器只负责协调
class UserController extends AbstractController
{
    public function __construct(
        private UserService $userService
    ) {}

    #[Route('/users', name: 'app_user_list')]
    public function list(): Response
    {
        $users = $this->userService->getAllUsers();
        
        return $this->render('user/list.html.twig', [
            'users' => $users,
        ]);
    }

    #[Route('/user/{id}', name: 'app_user_show')]
    public function show(int $id): Response
    {
        $user = $this->userService->getUserById($id);
        
        if (!$user) {
            throw $this->createNotFoundException('用户不存在');
        }
        
        return $this->render('user/show.html.twig', [
            'user' => $user,
        ]);
    }
}

// 服务类处理业务逻辑
class UserService
{
    public function __construct(
        private UserRepository $userRepository
    ) {}

    public function getAllUsers(): array
    {
        return $this->userRepository->findAll();
    }

    public function getUserById(int $id): ?User
    {
        return $this->userRepository->find($id);
    }
}

8.2 控制器命名规范 #

php
<?php

// 好的命名
class UserController extends AbstractController
{
    #[Route('/users', name: 'app_user_list')]
    public function list(): Response {}

    #[Route('/users/{id}', name: 'app_user_show')]
    public function show(int $id): Response {}

    #[Route('/users/new', name: 'app_user_new')]
    public function new(): Response {}

    #[Route('/users/{id}/edit', name: 'app_user_edit')]
    public function edit(int $id): Response {}
}

// 不好的命名
class UserCtrl extends AbstractController
{
    #[Route('/users', name: 'users')]
    public function index(): Response {}

    #[Route('/users/{id}', name: 'user')]
    public function view(int $id): Response {}
}

九、总结 #

本章学习了:

  • 控制器基本概念
  • 创建控制器方法
  • 动作方法定义
  • 返回响应类型
  • 模板渲染
  • 重定向操作
  • 异常处理
  • 控制器最佳实践

下一章将学习 请求与响应

最后更新:2026-03-28