Symfony 快速入门
大局观
10 分钟内开始使用 Symfony!真的吗!这就是您理解最重要的概念并开始构建真正的项目所需的全部内容!
如果您以前使用过 Web 框架,那么您应该对 Symfony 感到很熟悉。如果没有,欢迎使用一种全新的 Web 应用程序开发方式。 Symfony拥抱最佳实践,保持向后兼容性(是的!升级总是安全且简单的!)并提供长期支持。
下载 Symfony
首先,确保您已安装Composer并拥有 PHP 8.1 或更高版本。
准备好?在终端中,运行:
composer create-project symfony/skeleton quick_tour
这将创建一个新的quick_tour/目录,其中包含一个小而强大的新 Symfony 应用程序:
quick_tour/
├─ .env
├─ bin/console
├─ composer.json
├─ composer.lock
├─ config/
├─ public/index.php
├─ src/
├─ symfony.lock
├─ var/
└─ vendor/
我们可以在浏览器中加载该项目吗?是的!您可以设置Nginx 或 Apache并将其文档根配置为public/目录。但是,对于开发来说,最好安装 Symfony 本地 Web 服务器并按如下方式运行:
symfony server:start
在浏览器中访问http://localhost:8000来尝试您的新应用程序!
基础知识:路由、控制器、响应
我们的项目只有大约 15 个文件,但它已准备好成为一个时尚的 API、一个强大的 Web 应用程序或一个微服务。 Symfony 从小处开始,但随着您的扩展而扩展。
但在我们深入之前,让我们通过构建我们的第一个页面来深入了解基础知识。
在src/Controller中,创建一个新的DefaultController类并在其中创建一个index方法:
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController
{
#[Route('/', name: 'index')]
public function index(): Response
{
return new Response('Hello!');
}
}
就是这样!尝试访问主页: http://localhost:8000/ 。 Symfony 发现 URL 与我们的路由匹配,然后执行新的index()方法。
控制器只是一个普通函数,有一个规则:它必须返回一个 Symfony Response对象。但该响应可以包含任何内容:简单文本、JSON 或完整的 HTML 页面。
但路由系统要强大得多。所以让我们让这条路线变得更有趣:
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController
{
- #[Route('/', name: 'index')]
+ #[Route('/hello/{name}', name: 'index')]
public function index(): Response
{
return new Response('Hello!');
}
}
此页面的 URL 已更改:现在是/hello/* : {name}的作用就像匹配任何内容的通配符。而且它会变得更好!也更新控制器:
<?php
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController
{
#[Route('/hello/{name}', name: 'index')]
- public function index()
+ public function index(string $name): Response
{
- return new Response('Hello!');
+ return new Response("Hello $name!");
}
}
通过转至尝试该页面 http://localhost:8000/hello/Symfony 。您应该看到:Hello Symfony! URL 中的{name}值可作为控制器中的$name参数使用。
但是通过使用属性,路由和控制器就彼此相邻。需要另一页吗?在DefaultController中添加另一个路由和方法:
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController
{
// ...
#[Route('/simplicity', methods: ['GET'])]
public function simple(): Response
{
return new Response('Simple! Easy! Great!');
}
}
路由还可以做更多的事情,但我们下次再说吧!现在,我们的应用程序需要更多功能!比如模板引擎、日志记录、调试工具等等。
Flex:编写您的应用程序
阅读本教程的第一部分后,你决定再花 10 分钟研究一下 Symfony。好选择!在本教程的第二部分,你将了解 Symfony Flex:这个神奇的工具可以让添加新功能变得像运行一个命令一样简单。这也是为什么 Symfony 非常适合小型微服务或大型应用程序的原因。好奇吗?太完美了!
启动微服务
除非您正在构建纯 API(关于这一点很快就会详细介绍),否则您可能希望渲染 HTML。为此,您将使用 Twig。Twig 是一个灵活、快速且安全的 PHP 模板引擎。它使您的模板更易于阅读和简洁;它还使它们对网页设计师更加友好。
是否在我们的应用程序中已经安装了 Twig?实际上,还没有!这真是太好了!当你开始一个新的 Symfony 项目时,它很小:只有最关键的依赖项包含在你的 composer.json 文件中:
"require": {
"...",
"symfony/console": "^6.1",
"symfony/flex": "^2.0",
"symfony/framework-bundle": "^6.1",
"symfony/yaml": "^6.1"
}
这使得 Symfony 与其他任何 PHP 框架都不同!它不是从一个包含所有可能需要的功能的庞大应用程序开始,而是从一个小巧、简单且快速的应用程序开始。而且你可以完全控制你添加的内容。
Flex 菜谱和别名
所以,我们如何安装和配置 Twig?通过运行一条单独的命令:
composer require twig
两个非常有趣的事情在幕后发生,多亏了 Symfony Flex:一个已经安装在我们项目中的 Composer 插件。
首先, twig 不是一个作曲家包的名称:它是一个 Flex 别名,指向 symfony/twig-bundle 。Flex 为 Composer 解析该别名。
其次,Flex 安装了一个名为 symfony/twig-bundle 的配方。什么是配方?它是一种让库通过添加和修改文件来自动配置自己的方式。多亏了配方,添加功能变得无缝且自动化:安装一个包,你就完成了!
您可以在食谱仓库中的 RECIPES.md 文件内找到完整的食谱和别名列表。
这个食谱做了什么?除了在 config/bundles.php 中自动启用功能外,它还增加了 3 件事:
- config/packages/twig.yaml 一个设置 Twig 的合理默认值的配置文件。
- config/packages/test/twig.yaml 一个在运行测试时更改一些 Twig 选项的配置文件。
- templates/ 这是模板文件将存放的目录。食谱还添加了一个 base.html.twig 布局文件。
Twig:渲染模板
感谢 Flex,只需一条命令,您就可以立即开始使用 Twig:
<?php
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
- class DefaultController
+ class DefaultController extends AbstractController
{
#[Route('/hello/{name}', methods: ['GET'])]
public function index(string $name): Response
{
- return new Response("Hello $name!");
+ return $this->render('default/index.html.twig', [
+ 'name' => $name,
+ ]);
}
}
通过扩展 AbstractController ,你现在可以访问许多快捷方法和工具,如 render() 。创建新模板:
{# templates/default/index.html.twig #}
<h1>Hello {{ name }}</h1>
这就是了! {{ name }} 语法将打印从控制器传递进来的 name 变量。如果你是 Twig 的新手,欢迎!你以后会了解更多关于其语法和功能的内容。
但是,目前该页面只包含 h1 标签。为了给它一个 HTML 布局,扩展 base.html.twig :
{# templates/default/index.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<h1>Hello {{ name }}</h1>
{% endblock %}
这是称为模板继承:我们的页面现在从 base.html.twig 继承 HTML 结构。
Profiler:调试利器
Symfony最酷的功能之一甚至还没有安装!让我们解决这个问题:
composer require profiler
是的!这是另一个别名! Flex还安装了另一个配方,它可以自动配置 Symfony 的 Profiler。结果如何?刷新!
看到底部的黑条了吗?这就是网络调试工具栏,它是您最好的新朋友。通过将鼠标悬停在每个图标上,您可以获得有关执行的控制器、性能信息、缓存命中和未命中等信息。单击任何图标即可进入分析器,您可以在其中获得更详细的调试和性能数据!
哦,当您安装更多库时,您将获得更多工具(例如显示数据库查询的 Web 调试工具栏图标)。
您现在可以直接使用探查器,因为它通过配方自行配置。我们还可以安装什么?
丰富的API支持
您正在构建 API 吗?您已经可以从任何控制器返回 JSON:
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController extends AbstractController
{
// ...
#[Route('/api/hello/{name}', methods: ['GET'])]
public function apiHello(string $name): JsonResponse
{
return $this->json([
'name' => $name,
'symfony' => 'rocks',
]);
}
}
但对于真正丰富的 API,请尝试安装API Platform:
composer require api
这是api-platform/api-pack Symfony pack的别名,它依赖于其他几个包,例如 Symfony 的验证器和安全组件,以及 Doctrine ORM。事实上,Flex 安装了5 个食谱!
但像往常一样,我们可以立即开始使用新库。想要为product表创建丰富的 API?创建一个Product实体并为其赋予#[ApiResource]属性:
// src/Entity/Product.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ApiResource]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'string')]
private string $name;
#[ORM\Column(type: 'integer')]
private int $price;
// ...
}
完毕!您现在可以通过端点来列出、添加、更新和删除产品!不相信我?通过运行以下命令列出您的路线:
php bin/console debug:router
------------------------------ -------- -------------------------------------
Name Method Path
------------------------------ -------- -------------------------------------
api_products_get_collection GET /api/products.{_format}
api_products_post_collection POST /api/products.{_format}
api_products_get_item GET /api/products/{id}.{_format}
api_products_put_item PUT /api/products/{id}.{_format}
api_products_delete_item DELETE /api/products/{id}.{_format}
...
------------------------------ -------- -------------------------------------
删除食谱
还不相信吗?没问题:删除库:
composer remove api
Flex 将卸载配方:删除文件并撤消更改以使您的应用程序恢复到原始状态。无忧无虑地进行实验。
更多功能、架构和速度
我希望您和我一样对 Flex 感到兴奋!但我们还有一章,而且是最重要的一章。我想向您展示 Symfony 如何帮助您在不牺牲代码质量或性能的情况下快速构建功能。这一切都与服务容器有关,这也是 Symfony 的超能力。
架构
你是我的英雄!谁会想到在前两部分之后你还会在这里?你的努力很快就会得到丰厚的回报。前两部分并没有太深入地研究框架的架构。因为它使 Symfony 从众多框架中脱颖而出,所以现在让我们深入了解该架构。
添加日志记录
新的 Symfony 应用程序是微型的:它基本上只是一个路由和控制器系统。但多亏了 Flex,安装更多功能变得很简单。
想要一个日志系统吗?没问题:
composer require logger
这将安装并配置(通过配方)强大的Monolog库。要在控制器中使用记录器,请添加一个带有LoggerInterface类型提示的新参数:
// src/Controller/DefaultController.php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController extends AbstractController
{
#[Route('/hello/{name}', methods: ['GET'])]
public function index(string $name, LoggerInterface $logger): Response
{
$logger->info("Saying hello to $name!");
// ...
}
}
就是这样!新的日志消息将写入var/log/dev.log 。可以通过更新配方添加的配置文件之一来配置日志文件路径甚至不同的日志记录方法。
服务和自动装配
但是等等!刚刚发生了一件非常酷的事情。 Symfony 读取LoggerInterface类型提示并自动发现它应该向我们传递 Logger 对象!这称为自动装配。
Symfony 应用程序中完成的每一点工作都是由一个对象完成的:Logger 对象记录事物,Twig 对象呈现模板。这些对象称为服务,它们是帮助您构建丰富功能的工具。
为了让生活变得更精彩,您可以要求 Symfony 通过使用类型提示来向您传递服务。您还可以使用哪些其他可能的类或接口?通过运行找出:
php bin/console debug:autowiring
Describes a logger instance.
Psr\Log\LoggerInterface - alias:monolog.logger
Request stack that controls the lifecycle of requests.
Symfony\Component\HttpFoundation\RequestStack - alias:request_stack
RouterInterface is the interface that all Router classes must implement.
Symfony\Component\Routing\RouterInterface - alias:router.default
[...]
这只是完整列表的简短摘要!随着您添加更多软件包,此工具列表将会不断增长!
创建服务
为了保持代码井井有条,您甚至可以创建自己的服务!假设您想生成随机问候语(例如“Hello”、“Yo”等)。不要将此代码直接放入控制器中,而是创建一个新类:
// src/GreetingGenerator.php
namespace App;
class GreetingGenerator
{
public function getRandomGreeting(): string
{
$greetings = ['Hey', 'Yo', 'Aloha'];
$greeting = $greetings[array_rand($greetings)];
return $greeting;
}
}
伟大的!您可以立即在控制器中使用它:
// src/Controller/DefaultController.php
namespace App\Controller;
use App\GreetingGenerator;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController extends AbstractController
{
#[Route('/hello/{name}', methods: ['GET'])]
public function index(string $name, LoggerInterface $logger, GreetingGenerator $generator): Response
{
$greeting = $generator->getRandomGreeting();
$logger->info("Saying $greeting to $name!");
// ...
}
}
就是这样! Symfony 将自动实例化GreetingGenerator并将其作为参数传递。但是,我们是否也可以将记录器逻辑移至GreetingGenerator ?是的!您可以在服务内使用自动装配来访问其他服务。唯一的区别是它是在构造函数中完成的:
<?php
// src/GreetingGenerator.php
+ use Psr\Log\LoggerInterface;
class GreetingGenerator
{
+ public function __construct(
+ private LoggerInterface $logger,
+ ) {
+ }
public function getRandomGreeting(): string
{
// ...
+ $this->logger->info('Using the greeting: '.$greeting);
return $greeting;
}
}
是的!这也有效:无需配置,不浪费时间。继续编码!
Twig 扩展和自动配置
得益于 Symfony 的服务处理,您可以通过多种方式扩展Symfony,例如为复杂的授权规则创建事件订阅者或安全投票者。让我们向 Twig 添加一个名为greet新过滤器。如何?创建一个扩展AbstractExtension的类:
// src/Twig/GreetExtension.php
namespace App\Twig;
use App\GreetingGenerator;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class GreetExtension extends AbstractExtension
{
public function __construct(
private GreetingGenerator $greetingGenerator,
) {
}
public function getFilters(): array
{
return [
new TwigFilter('greet', [$this, 'greetUser']),
];
}
public function greetUser(string $name): string
{
$greeting = $this->greetingGenerator->getRandomGreeting();
return "$greeting $name!";
}
}
创建一个文件后,您可以立即使用它:
{# templates/default/index.html.twig #}
{# Will print something like "Hey Symfony!" #}
<h1>{{ name|greet }}</h1>
这是如何运作的? Symfony 注意到您的类扩展了AbstractExtension ,因此自动将其注册为 Twig 扩展。这称为自动配置,它适用于很多事情。创建一个类,然后扩展一个基类(或实现一个接口)。 Symfony 会处理剩下的事情。
极速:缓存容器
在看到 Symfony 自动处理多少内容后,您可能会想:“这不会损害性能吗?”事实上,不! Symfony 的速度非常快。
这怎么可能?服务系统是由一个非常重要的对象来管理的,这个对象叫做“容器”。大多数框架都有一个容器,但 Symfony 的容器是独一无二的,因为它是缓存的。当您加载第一页时,所有服务信息都已编译并保存。这意味着自动装配和自动配置功能不会增加任何开销!这也意味着您会遇到很大的错误:Symfony 在构建容器时检查并验证所有内容。
现在您可能想知道当您更新文件并且缓存需要重建时会发生什么?我喜欢你的想法!它足够智能,可以在下一个页面加载时重建。但这确实是下一节的主题。
开发与生产:环境
框架的主要工作之一就是使调试变得容易!我们的应用程序充满了用于此目的的出色工具:Web 调试工具栏显示在页面底部,错误很大,美观且明确,并且任何配置缓存都会在需要时自动重建。
但是当您部署到生产环境时呢?我们需要隐藏这些工具并优化速度!
Symfony 的环境系统解决了这个问题。 Symfony 应用程序从三个环境开始: dev 、 prod和test 。您可以使用特殊的when@关键字在config/目录中的配置文件中定义特定环境的选项:
# config/packages/routing.yaml
framework:
router:
utf8: true
when@prod:
framework:
router:
strict_requirements: null
这是一个强大的想法:通过更改一项配置(环境),您的应用程序将从易于调试的体验转变为速度优化的体验。
哦,那要怎么改变环境呢?将APP_ENV环境变量从dev更改为prod :
# .env
- APP_ENV=dev
+ APP_ENV=prod
但接下来我想更多地谈谈环境变量。将值更改回dev :当您在本地工作时,调试工具非常有用。
环境变量
每个应用程序都包含每个服务器上不同的配置 - 例如数据库连接信息或密码。这些应该如何保存?在文件中?或者其他方式?
Symfony 遵循行业最佳实践,将基于服务器的配置存储为环境变量。这意味着 Symfony 可以与平台即服务 (PaaS) 部署系统以及 Docker完美配合。
但在开发时设置环境变量可能会很痛苦。这就是您的应用程序自动加载.env文件的原因。然后,该文件中的键将成为环境变量并由您的应用程序读取:
# .env
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=cc86c7ca937636d5ddf1b754beb22a10
###< symfony/framework-bundle ###
起初,该文件包含的内容并不多。但随着您的应用程序的增长,您将根据需要添加更多配置。但实际上,它变得更有趣!假设您的应用程序需要数据库 ORM。让我们安装 Doctrine ORM:
composer require doctrine
感谢 Flex 安装的新配方,再次查看.env文件:
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=cc86c7ca937636d5ddf1b754beb22a10
###< symfony/framework-bundle ###
+ ###> doctrine/doctrine-bundle ###
+ # ...
+ DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
+ ###< doctrine/doctrine-bundle ###
新的DATABASE_URL环境变量是自动添加的,并且已被新的doctrine.yaml配置文件引用。通过结合环境变量和 Flex,您无需任何额外的努力即可使用行业最佳实践。
继续前进!
你可以说我疯了,但读完这一部分后,您应该对 Symfony 最重要的部分感到满意。 Symfony 中的所有内容都经过精心设计,不会妨碍您,以便您可以继续编码和添加功能,所有这些都满足您所需的速度和质量。