ZetCode-Symfony-教程-一-
ZetCode Symfony 教程(一)
原文:ZetCode
Symfony DBAL 教程
Symfony DBAL 教程显示了如何使用 Doctrine DBAL 组件在 Symfony 应用中使用数据库。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Fabien Potencier 是 Symfony 的原始作者。 Symfony 的灵感来自 Ruby on Rails,Django 和 Spring 框架。
Symfony DBAL 组件
教义数据库抽象层(DBAL)是位于 PDO 之上的抽象层,并提供了一种直观且灵活的 API,可以与最受欢迎的关系数据库进行通信。 DBAL 库使执行查询和执行其他数据库操作变得容易。
Symfony DBAL 示例
在下面的示例中,我们创建一个简单的 Symfony 应用,该应用使用 DBAL 读取数据。 我们使用 MySQL 数据库。
$ composer create-project symfony/skeleton symdbal
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd symdbal
我们转到项目目录。
$ composer require symfony/orm-pack
我们安装symfony/orm-pack
,其中包含 DBAL API。
$ composer require maker --dev
另外,我们安装了maker
组件。 maker
包提供了脚手架。
$ composer require server --dev
我们安装开发 Web 服务器。
countries_mysql.sql
CREATE TABLE countries(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100), population INT);
INSERT INTO countries(name, population) VALUES('China', 1382050000);
INSERT INTO countries(name, population) VALUES('India', 1313210000);
INSERT INTO countries(name, population) VALUES('USA', 324666000);
INSERT INTO countries(name, population) VALUES('Indonesia', 260581000);
INSERT INTO countries(name, population) VALUES('Brazil', 207221000);
INSERT INTO countries(name, population) VALUES('Pakistan', 196626000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Bangladesh', 162099000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Russia', 146838000);
INSERT INTO countries(name, population) VALUES('Japan', 126830000);
INSERT INTO countries(name, population) VALUES('Mexico', 122273000);
INSERT INTO countries(name, population) VALUES('Philippines', 103738000);
这是一些测试数据。 它在 MySQL 中创建一个小表。 我们可以使用source
命令执行文件。
.env
...
DATABASE_URL=mysql://user12:s$cret@localhost:3306/mydb
在.env
文件中,我们配置数据库 URL。
$ php bin/console make:controller HomeController
HomeController
由 Symfony 制造商创建。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Doctrine\DBAL\Connection;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class HomeController extends AbstractController
{
/**
* @Route("/home", name="home")
*/
public function index(Connection $conn)
{
$queryBuilder = $conn->createQueryBuilder();
$data = $queryBuilder->select('*')->from('countries')->execute()->fetchAll();
return $this->json([
'data' => $data
]);
}
}
该控制器方法执行一个查询,该查询从countries
表中获取所有行,并将其作为 JSON 数据返回。 请注意,出于简单原因,我们已将查询生成器放入控制器中。 在生产应用中,还应该有一个服务层和一个存储库。
public function index(Connection $conn)
{
DBAL Connection
对象被注入到方法变量中。
$queryBuilder = $conn->createQueryBuilder();
我们从连接创建一个QueryBuilder
。
$data = $queryBuilder->select('*')->from('countries')->execute()->fetchAll();
我们执行查询并获取所有行。
return $this->json([
'data' => $data
]);
数据以 JSON 格式返回。
$ php bin/console server:run
Web 服务器已启动。
$ curl localhost:8000/home
{"data":[{"id":"1","name":"China","population":"1382050000"},
{"id":"2","name":"India","population":"1313210000"},
{"id":"3","name":"USA","population":"324666000"},
{"id":"4","name":"Indonesia","population":"260581000"},
{"id":"5","name":"Brazil","population":"207221000"},
{"id":"6","name":"Pakistan","population":"196626000"},
{"id":"7","name":"Nigeria","population":"186988000"},
{"id":"8","name":"Bangladesh","population":"162099000"},
{"id":"9","name":"Nigeria","population":"186988000"},
{"id":"10","name":"Russia","population":"146838000"},
{"id":"11","name":"Japan","population":"126830000"},
{"id":"12","name":"Mexico","population":"122273000"},
{"id":"13","name":"Philippines","population":"103738000"}]}
我们使用curl
工具执行 GET 请求。
在本教程中,我们在 Symfony 中使用了 Doctrine DBAL。
您可能也对以下相关教程感兴趣: Symfony 简介, Doctrine DBAL QueryBuilder
教程, Symfony 表单教程, Symfony 翻译教程, PHP 教程。
{% raw %}
Symfony 表单教程
Symfony 表单教程展示了如何在 Symfony 中创建和处理表单。 在本教程中,我们不使用 Symfony 表单构建器。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
HTML 表单
HTML 表单用于用户与网站或应用之间的交互。 它们允许用户将数据发送到网站。 HTML 表单由一个或多个小部件组成。 这些小部件可以是文本字段,选择框,按钮,复选框或单选按钮。 这些小部件通常与描述其用途的标签配对。
Symfony 表单示例
在下面的示例中,我们创建一个 HTML 表单。 表单中的数据由 Symfony 控制器处理。
$ composer create-project symfony/skeleton myform
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd myform
我们转到项目目录。
$ composer req annotations twig
我们安装了两个模块:annotations
和twig
。
$ composer require server maker --dev
我们安装开发 Web 服务器和制造商。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
HomeController
返回包含 HTML 表单的主页。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<section class="ui container">
<form class="ui form" action="message" method="get">
<div class="field">
<label>Name:</label>
<input type="text" name="name">
</div>
<div class="field">
<label>Message</label>
<input type="text" name="message">
</div>
<button class="ui button" type="submit">Send</button>
</form>
</section>
{% endblock %}
HomeController
返回包含 HTML 表单的主页。 该表格包含两个输入字段。 这些字段的内容将通过两个请求属性传递到请求对象中。
{% extends 'base.html.twig' %}
该模板继承自base.html.twig
文件,该文件具有要共享的基本标记。 例如,我们包括语义 UI CSS 框架的文件。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.css"
rel="stylesheet">
</head>
<body>
{% block body %}{% endblock %}
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.js"></script>
</html>
base.html.twig
模板包含其他模板文件共享的代码。 它定义了将在子模板中替换的块。
$ php bin/console make:controller MessageController
创建了MessageController
。
src/Controller/MessageController.php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MessageController extends AbstractController
{
/**
* @Route("/message", name="message", methods="GET")
*/
public function index(Request $request)
{
$name = $request->query->get("name");
$message = $request->query->get("message");
return $this->render('message/index.html.twig', ["name" => $name,
"message" => $message]);
}
}
MessageController
处理表格。
/**
* @Route("/message", name="message", methods="POST")
*/
@Route
注解将message
路径映射到index()
方法。 methods
参数定义请求类型。
public function index(Request $request)
我们将Request
对象注入该方法。
$name = $request->query->get("name");
$message = $request->query->get("message");
从请求对象中,我们获得两个请求参数。
return $this->render('message/index.html.twig', ["name" => $name,
"message" => $message]);
我们渲染message/index.html.twig
模板。 我们将两个变量传递给模板。
templates/message/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Show message{% endblock %}
{% block body %}
{{name}} says: {{message}}
{% endblock %}
最后,我们有向用户显示消息的模板文件。 变量以{{}}
语法显示。
$ php bin/console server:run
我们运行该应用并导航到localhost:8000
。
在本教程中,我们在 Symfony 应用中创建并处理了一个简单的 HTML 表单。
您可能也对以下相关教程感兴趣: Symfony 保留表单值教程, Symfony 简介, Symfony 请求教程, Symfony 验证教程 , Twig 教程, Symfony DBAL 教程或 PHP 教程。
{% endraw %}
{% raw %}
Symfony CSRF 教程
Symfony CSRF 教程展示了如何在 Symfony 应用中实现 CSRF 保护。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 的灵感来自 Djanog,Spring 和 ROR 框架。
CSRF
跨站点请求伪造(CSRF)是一种攻击,其中恶意用户试图使合法用户在不知情的情况下提交他们不打算提交的数据。 CSRF 攻击专门针对状态更改请求,而不是数据盗窃。 成功的 CSRF 攻击可以迫使用户执行状态更改请求,例如转移资金或更改其个人数据详细信息。
CSRF 保护的工作原理是在表单中添加一个隐藏字段,该字段包含仅应用和用户知道的值(令牌)。 这样可以确保用户(而非其他实体)正在提交给定的数据。
symfony/security-csrf
组件提供CsrfTokenManager
用于生成和验证 CSRF 令牌。 默认情况下,使用 Symfony 表单组件创建的表单包括 CSRF 令牌,Symfony 会自动检查它们,因此我们无需采取任何措施来防止 CSRF 攻击。 csrf_token()
Twig 函数为用户呈现 CSRF 令牌。
Symfony CSRF 保护示例
在下面的示例中,我们手动创建一个表单,为其实现 CSRF 保护。 在此应用中,我们在routes.yaml
文件中定义路由。
$ composer create-project symfony/skeleton csrf-app
$ cd csrf-app
使用composer
,我们创建一个新的 Symfony 框架项目并定位到项目目录。
$ composer require symfony/security-csrf
我们安装security-csrf
包。
$ composer require server --dev
我们安装开发 Web 服务器。
config/routes.yaml
index:
path: /
controller: App\Controller\AppController::index
process-form:
path: /process
controller: App\Controller\AppController::processForm
我们为应用定义了两个路由。 index
路由显示带有表单的主页。 process-form
处理提交的表单并检查 CSRF 令牌。
src/Controller/AppController.php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class AppController extends AbstractController
{
public function index()
{
return $this->render('home/index.html.twig');
}
public function processForm(Request $request)
{
$token = $request->request->get("token");
if (!$this->isCsrfTokenValid('myform', $token))
{
return new Response('Operation not allowed', Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
$name = $request->request->get("name");
$email = $request->request->get("email");
$msg = "$name with $email saved";
return new Response($msg, Response::HTTP_CREATED, ['content-type' => 'text/plain']);
}
}
AppController
具有两个动作:index()
和processForm()
。
public function index()
{
return $this->render('home/index.html.twig');
}
index()
函数呈现主页。 主页包含 HTML 表单。
$token = $request->request->get("token");
我们从请求中使用get()
方法检索 CSRF 令牌。
if (!$this->isCsrfTokenValid('myform', $token))
{
return new Response('Operation not allowed', Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
我们使用isCsrfTokenValid()
方法检查令牌的有效性。 如果令牌无效,我们将返回带有Response::HTTP_BAD_REQUEST
代码的响应。 令牌myform
的名称在模板的 HTML 表单中指定。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<section class="ui container">
<form class="ui form" action="{{ path('process-form') }}" method="post">
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
<div class="field">
<label>Name:</label>
<input name="name" type="text">
</div>
<div class="field">
<label>Email</label>
<input name="email" type="text">
</div>
<button class="ui button" type="submit">Send</button>
</form>
</section>
{% endblock %}
这是带有表单的主页的 Twig 模板。 表单使用语义 UI 库进行样式设置。
<form class="ui form" action="{{ path('process-form') }}" method="post">
表单动作指向process-form
路径。 表单的方法是 POST,这意味着必须进行 CSRF 保护。
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
我们使用 CSRF 令牌添加隐藏的输入。 令牌是使用csrf_token()
生成的。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
{% block title %}Welcome!
{% endblock %}
</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet">
</head>
<body>
{% block body %}{% endblock %}
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.js"></script>
</html>
这是基本模板文件。 它加载语义 UI 库。
$ php bin/console server:run
我们运行该应用。
$ curl -d "name=Peter&email=peter@example.com" -X POST http://localhost:8000/process
Operation not allowed
如果我们尝试绕过表单并尝试使用curl
工具访问控制器操作,则会收到错误消息。
在本教程中,我们在 Symfony 应用中实现了 CSRF 保护。
您可能也对以下相关教程感兴趣: Symfony 验证教程, Symfony 表单教程, PHP 教程或列出所有 Symfony 教程。
{% endraw %}
{% raw %}
Symfony Vue 教程
Symfony Vue 教程展示了如何使用 Vue 前端创建一个简单的 Symfony 应用。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Fabien Potencier 是 Symfony 的原始作者。 Symfony 的灵感来自 Ruby on Rails,Django 和 Spring 框架。
Symfony Encore 是 JavaScript 库,用于在 Symfony 应用中管理 CSS 和 JavaScript。 Encore 使将 Webpack 集成到 Symfony 应用中变得更加容易。 它包装了 Webpack,并提供了一个干净而强大的 API,用于捆绑 JavaScript 模块,预处理 CSS 和 JavaScript 以及编译和缩小项目。
Vue
Vue 是用于构建用户界面和单页应用的开源 JavaScript 框架。 它是 Angular 和 React 的替代方案。
Symfony Vue 示例
在下面的示例中,我们创建了一个简单的 Symfony 应用,该应用在模板中发送数据。 数据由 Vue 处理并显示在组件中。
除了 PHP,我们还需要安装 Node.js。 看看 ZetCode 的 Node.js 教程了解更多详细信息。
设置项目
我们展示了如何使用 Vue 设置 Symfony。
$ composer create-project symfony/skeleton symvue
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd symvue
我们转到项目目录。
$ composer require maker --dev
另外,我们安装了 maker 组件。 maker
包提供了脚手架。
$ composer require server --dev
我们安装开发 Web 服务器。
$ composer require encore
$ npm install
我们安装了 Symfony Encore。 这将安装并启用 WebpackEncoreBundle,添加assets
目录,创建webpack.config.js
文件,并将node_modules
添加到.gitignore
。
$ npm i vue vue-loader vue-template-compiler
我们安装 Vue 及其库。
项目文件
我们显示了重要的项目文件。
webpack.config.js
var Encore = require('@symfony/webpack-encore');
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.enableVueLoader()
.addEntry('app', './assets/js/app.js')
.splitEntryChunks()
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
;
module.exports = Encore.getWebpackConfig();
在webpack.config.js
文件中,我们启用 Vue 加载程序并设置公共路径和构建路径。
assets/js/app.js
import Vue from 'vue';
import App from './components/App';
new Vue({
el: '#app',
render: h => h(App)
});
这是启动 Vue 的主要 Vue 文件。
Symfony 将静态文件(如 CSS 和 JavaScript)存储在assets
目录中。
assets/js/components/App.vue
<template>
<div>
<h2 class="center">My Application</h2>
<div v-text="message"></div>
{{ message }}
<ul>
<li :key="word.id" v-for="word in words">{{ word }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
message: "A list of words",
words: []
};
},
mounted() {
let el = document.querySelector("div[data-words]");
let mywords = el.dataset.words.split(",");
this.words.push.apply(this.words, mywords);
}
};
</script>
<style>
.center {
text-align: center;
}
</style>
这是 Vue 组件。 Vue 应用由组件组成。 一个组件由三部分组成:模板,脚本和样式。
<div v-text="message"></div>
{{ message }}
在 Vue 中有两种输出变量的方法: 第二个与 Twig 相同。
<ul>
<li :key="word.id" v-for="word in words">{{ word }}</li>
</ul>
使用v-for
指令,我们遍历words
数组并显示列表项中的每个元素。 :key
指令可帮助 Vue 渲染列表。 它包含元素的 ID。
数据来自 Symfony Twig 模板; 它由 JavaScript 处理,最后在 Vue 组件中与v-for
输出。
data() {
return {
message: "A list of words",
words: []
};
},
在data()
函数中,我们启动一个消息变量和words
数组。
mounted() {
let el = document.querySelector("div[data-words]");
let mywords = el.dataset.words.split(",");
this.words.push.apply(this.words, mywords);
}
words
数组在mounted()
函数中填充了数据,该函数解析元素数据集中的数据。 它以字符串形式存储在此; 我们将字符串分成单词。 数据将插入 Symfony 的 Twig 模板内的数据集中。
assets/css/style.css
body {
background-color: lightgray;
}
style.css
中有一些基本的 CSS。
$ php bin/console make:controller HomeController
HomeController
由 Symfony 制造商创建。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/home", name="home")
*/
public function index()
{
$words = ['sky', 'cloud', 'wood', 'rock', 'forest',
'mountain', 'breeze'];
return $this->render('home/index.html.twig', [
'words' => $words
]);
}
}
控制器方法将单词列表发送给客户端。
return $this->render('home/index.html.twig', [
'words' => $words
]);
我们渲染向其发送单词的index.html.twig
模板。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<div ref="words" data-words="{{ words|join(',') }}">
</div>
<div id="app">
<app></app>
</div>
{% endblock %}
在模板中,我们将单词数组添加到data-words
属性。 使用 Twig join
过滤器将数组连接成字符串。 HTMLElement
接口上的dataset
属性提供对在元素上设置的所有自定义数据属性(data-*
)的读/写访问。
<div id="app">
<app></app>
</div>
这是主要 Vue 组件的入口点。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</body>
</html>
这是基本模板文件。
{{ encore_entry_link_tags('app') }}
CSS 文件加载有encore_entry_link_tags
。
{{ encore_entry_script_tags('app') }}
JavaScript 文件加载有encore_entry_script_tags
。
构建项目
我们需要构建项目。
$ npm run dev
使用npm run dev
命令为开发环境构建项目。
运行应用
我们启动开发服务器并找到应用页面。
$ php bin/console server:run
我们启动开发服务器。 然后我们找到localhost:8000/home
页面。
在本教程中,我们创建了一个在前端使用 Vue 的 Symfony 应用。
您可能也对以下相关教程感兴趣: Symfony 简介, Doctrine DBAL QueryBuilder
教程, Symfony 表单教程, Symfony 翻译教程, PHP 教程。
{% endraw %}
{% raw %}
Symfony 简介
这是 Symfony 的入门教程。 它展示了 Symfony PHP 框架,并展示了如何创建简单的示例。 本教程介绍了 Symfony 版本 4。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
Symfony 使用了几个 PHP 开源项目,例如 Doctrine 对象关系映射库,PDO 数据库抽象层,PHPUnit 测试框架,Twig 模板引擎和 Swift Mailer 电子邮件库。
Symfony 创建了自己的组件,包括 Symfony 依赖注入器和 Symfony YAML 解析器。
设置 Symfony 项目
为了创建一个 Symfony 4 项目,我们需要 PHP 7(以及相关的库,例如 php-xml 或 php-mcrypt)和composer
。 项目相关性将写入composer.json
文件。
$ composer create-project symfony/skeleton symfirst
使用composer
,我们创建了一个名为first
的新 Symfony 骨架项目。 Symfony 框架等效于微型框架,在微型框架中,我们需要自己安装所有模块。 我们决定要安装什么模块。 这对学习有好处。
$ cd symfirst
不要忘记去项目目录。
Symfony 项目结构
作曲家创建了一个 Symfony 应用结构。
$ ls -p
bin/ composer.lock public/ symfony.lock vendor/
composer.json config/ src/ var/
bin
目录包含console
工具,该工具是用于执行各种类型命令的命令行工具。 public
目录包含 Web 文件。 在 Symfony 骨架应用中,它包含一个文件:index.php
,它是一个 Symfony 前端控制器。
第三方依存关系存储在vendor
目录中。 config
目录包含配置文件。 源代码写在src
目录中。 var
目录包含临时文件,例如缓存数据。
在composer.json
中定义了 Composer 依赖项。 composer.lock
记录已安装的确切版本,以便以后可以重新安装。 它可以确保每个在项目上工作的人都具有相同的确切版本的库。 symfony.lock
文件是 Symfony 配方的正确锁定文件。
还有两个特定的隐藏文件:.env
和.env.dist
。 .env
的内容成为环境变量。 环境变量由各种工具(例如 ORM 库)使用。 .env
可能包含敏感或计算机特定的数据; 因此,不应将其提交到存储库。 相反,.env.dist
带有一些伪值。
安装 Symfony 项目依赖项
接下来,我们将安装一些项目依赖项。
$ composer require server maker --dev
我们安装了开发服务器和 maker 组件,该组件用于生成命令,控制器,表单类或事件订阅者。
$ composer require annotations twig
我们将安装两个附加的 Symfony 模块。 annotations
提供了一种使用注解配置控制器的方法。 twig
允许在 Symfony 应用中使用 Twig 模板引擎。
Symfony 创建控制器
Symfony 控制器是一个 PHP 函数,它从Request
对象读取信息,并创建并返回Response
对象。 响应可能是 HTML 页面,JSON,XML,文件下载,重定向,404 错误等等。
$ php bin/console make:controller HelloController
使用console
工具,创建HelloController
。 在src/Controller/
目录中创建控制器。
src/Controller/HelloController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HelloController extends AbstractController
{
/**
* @Route("/plain", name="plain")
*/
public function helloPlain(): Response
{
return new Response("Hello there", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
这是HelloController
。 它位于src/Controller/HelloController.php
文件中。
/**
* @Route("/plain", name="plain")
*/
public function helloPlain()
{
路由是从 URL 路径到控制器方法的映射。 @Route
注解将/plain
URL 路径映射到helloPlain()
函数。
return new Response("Hello there", Response::HTTP_OK,
['content-type' => 'text/plain']);
该函数返回一个Response
对象。 Response
对象保存需要从给定请求发送回客户端的所有信息。 构造器最多包含三个参数:响应内容,状态代码和 HTTP 标头数组。 默认状态码为Response::HTTP_OK
,内容类型为text/html
。
$ php bin/console server:run
使用bin/console server:run
命令启动 Web 服务器。 要停止服务器,我们使用bin/console server:stop
命令。
$ curl localhost:8000/plain
Hello there
我们向普通路由发出 GET 请求,并查看文本响应。
Symfony 与 Twig 模板
当我们使用composer require twig
命令时,将 Twig 模板引擎安装到项目目录中。 还创建了一个templates
目录。 在该目录中,我们放置模板文件。 模板文件具有html.twig
扩展名。
src/Controller/HelloController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HelloController extends AbstractController
{
/**
* @Route("/plain", name="plain")
*/
public function helloPlain(): Response
{
return new Response("Hello there", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
/**
* @Route("/twig", name="twig")
*/
public function helloTwig(): Response
{
$message = "Hello from Twig";
return $this->render('hello/index.html.twig', ["message" => $message]);
}
}
我们已经更新了HelloController.php
文件; 我们添加了一条新路由。 这次,该函数呈现了一个 Twig 模板。
/**
* @Route("/twig", name="twig")
*/
public function helloTwig(): Response
{
helloTwig()
函数映射到twig
路径。
$message = "Hello from Twig";
return $this->render('hello/index.html.twig', ["message" => $message]);
Twig 渲染位于templates
目录中的'hello/index.html.twig
文件。 render()
方法也接受数据; 在我们的例子中,它是一个消息变量。 模板引擎将数据与 HTML 结构合并。
templates/hello/index.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
{{ message }}
</body>
</html>
这是 Twig 模板文件。
{{ message }}
{{ }}
是一种特殊的 Twig 语法,它显示变量的内容。
$ curl localhost:8000/twig
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
Hello from Twig
</body>
</html>
当我们连接到树枝路径时,我们将获得此 HTML 输出。
在本教程中,我们介绍了 Symfony 框架。
您可能也对以下相关教程感兴趣: Symfony 表单教程, Symfony 请求教程, Twig 教程, PHP 教程。
{% endraw %}
{% raw %}
Symfony 请求教程
Symfony 请求教程展示了如何在 Symfony 中使用请求对象。 我们展示了几种在 Symfony 中创建请求对象的方法。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 该框架的开发由 Frech 公司 Sensio Labs 赞助。
Symfony HttpFoundation
组件
Symfony HttpFoundation
组件为 HTTP 规范定义了一个面向对象的层。 该组件以面向对象的方式表示请求/响应过程。 在最低级别上,我们具有 PHP 全局变量,例如$_GET
,$_POST
或$_FILES
。 这些由Request
对象表示。 响应由Response
对象表示。
Symfony 请求示例
在下面的示例中,我们使用链接创建三个不同的请求。
$ composer create-project symfony/skeleton symreq
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd symreq
我们转到项目目录。
$ composer req annotations twig
我们安装模块annotations
和twig
。
$ composer req maker server --dev
我们安装制造商组件和开发 Web 服务器。
$ php bin/console make:controller HomeController
创建了HomeController
。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
HomeController
返回一个包含锚标记的主页。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<ul>
<li><a href="/myapp?colour=yellow&day=Saturday">First request</a></li>
<li><a href="/myapp2?colour=green&day=Sunday">Second request</a></li>
<li><a href="/myapp3?colour=red&day=Monday">Third request</a></li>
</ul>
{% endblock %}
HomeController
返回一个包含三个链接的主页。 每个链接都有两个查询参数。 它们指向不同的控制器方法。
{% extends 'base.html.twig' %}
该模板继承自base.html.twig
文件,该文件具有要共享的基本标记。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
base.html.twig
模板包含其他模板文件共享的代码。 它定义了将在子模板中替换的块。
$ php bin/console make:controller MyappController
创建了MyappController
。
src/Controller/MyappController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class MyappController extends AbstractController
{
/**
* @Route("/myapp", name="myapp")
*/
public function process()
{
$request = Request::createFromGlobals();
$col = $request->query->get("colour");
$day = $request->query->get("day");
$content = "Colour: $col, day: $day";
return new Response($content);
}
/**
* @Route("/myapp2", name="myapp2")
*/
public function process2()
{
$request = new Request(
$_GET,
$_POST,
array(),
$_COOKIE,
$_FILES,
$_SERVER
);
$col = $request->query->get("colour");
$day = $request->query->get("day");
$content = "Colour: $col, day: $day";
return new Response($content);
}
/**
* @Route("/myapp3", name="myapp3")
*/
public function process3(Request $request)
{
$data = $request->query->all();
$col = $data["colour"];
$day = $data["day"];
$content = "Colour: $col, day: $day";
return new Response($content);
}
}
MyappController
处理由链接创建的三个 GET 请求。
$request = Request::createFromGlobals();
$col = $request->query->get("colour");
$day = $request->query->get("day");
使用Request::createFromGlobals()
创建请求对象。 使用get()
方法检索 GET 参数。
$request = new Request(
$_GET,
$_POST,
array(),
$_COOKIE,
$_FILES,
$_SERVER
);
在第二种情况下,使用new
关键字创建请求。 它通过 PHP 全局变量传递。
public function process3(Request $request)
{
$data = $request->query->all();
...
在第三种情况下,使用 Symfony 的依赖项注入来注入请求对象。 我们使用all()
方法从请求中获取所有参数。
$col = $data["colour"];
$day = $data["day"];
从数组中,我们得到值。
$content = "Colour: $col, day: $day";
return new Response($content);
我们构建内容并返回Response
对象。
在本教程中,我们处理了 Symfony 中的请求。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 邮件教程, Symfony 创建路由, Symfony 表单教程 , PHP 教程。
{% endraw %}
Symfony HttpClient
教程
Symfony HttpClient
教程展示了如何使用HttpClient
组件在 Symfony 中创建 HTTP 请求。 该组件提供使用 API 的工具,并支持同步和异步操作。
有关更多信息,请阅读官方 HttpComponent 文档。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
在示例中,我们将使用httpbin.org
和http://jsonplaceholder.typicode.com/
在线服务。
$ composer require symfony/http-client
$ composer require symfony/var-dumper
我们安装了HttpClient
和var-dumper
组件。
HttpClient
GET 请求
HTTP 定义了一组请求方法,以指示要对给定资源执行的所需操作。 GET 请求用于从指定资源请求数据。 使用 GET 的请求应仅检索数据,而对数据没有其他影响。
注意:建议使用 HTTP 方法的目的和影响; 这些不是严格的规则。 这就是我们前面说过 GET 方法不应对数据产生影响的原因。 实际上,这并不总是遵循的。
get_request.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');
$statusCode = $response->getStatusCode();
echo $statusCode . "\n";
$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";
$content = $response->getContent();
echo $content . "\n";
该示例创建HttpClient
,并向指定的网页发出 GET 请求。
注意:响应始终是异步的,因此对方法的调用将立即返回,而不是等待接收响应。 有诸如
getStatusCode()
和getContent()
之类的阻塞方法,它们会等到接收到完整的响应内容为止。
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');
我们用HttpClient::create()
创建一个HttpClient
。 使用request()
方法生成 GET 请求。
$statusCode = $response->getStatusCode();
echo $statusCode . "\n";
我们使用getStatusCode()
方法获取状态代码。
$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";
从响应的标题中,我们获得内容类型。
$content = $response->getContent();
echo $content . "\n";
最后,我们使用getContent()
方法获得页面的内容。
$ php get_request.php
200
text/html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My html page</title>
</head>
<body>
<p>
Today is a beautiful day. We go swimming and fishing.
</p>
<p>
Hello there. How are you?
</p>
</body>
</html>
这是输出。
HttpClient
用户代理
创建HttpClient
时,我们可以传递一些选项,例如标头值。
user_agent.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create(['headers' => [
'User-Agent' => 'PHP console app',
]]);
$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');
echo $response->getContent() . "\n";
我们连接到httpbin.org
网站,该网站是用于测试 HTTP 请求&响应的在线工具。
$httpClient = HttpClient::create(['headers' => [
'User-Agent' => 'PHP console app',
]]);
在标题数组中,我们添加User-Agent
选项。
$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');
我们将 GET 请求发送到https://httpbin.org/user-agent
URL,该 URL 返回请求的用户代理选项。
HttpClient toArray()
toArray()
方法通常将响应主体解码为数组,并从 JSON 有效负载解码为数组。
content_array.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('GET',
'https://jsonplaceholder.typicode.com/posts/2/');
dump($response->toArray());
该示例向jsonplaceholder.typicode.com
在线服务网站发出 GET 请求,该网站以 JSON 格式返回 ID 为 2 的帖子。
dump($response->toArray());
我们转储toArray()
方法的输出。
$ php content_array.php
array:4 [
"userId" => 1
"id" => 2
"title" => "qui est esse"
"body" => """
est rerum tempore vitae\n
sequi sint nihil reprehenderit dolor beatae ea dolores neque\n
fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\n
qui aperiam non debitis possimus qui neque nisi nulla
"""
]
这是输出。
HttpClient
POST 数据
发布请求用于将数据发送到服务器。 数据位于 HTTP 请求的请求正文中。
发布请求永远不会被缓存,它们不会保留在浏览器历史记录中,也无法被添加书签,并且数据长度没有限制。
post_data.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('POST', 'https://httpbin.org/post', [
'body' => ['msg' => 'Hello there']
]);
echo $response->getContent();
在示例中,我们在 POST 请求中将消息变量发送到指定的 URL。 在响应对象中,我们找到了 POST 请求中发送的数据。
$ php post_data.php
{
"args": {},
"data": "",
"files": {},
"form": {
"msg": "Hello there"
},
...
}
这是输出。
HttpClient
重定向
URL 重定向是将请求从一页转发到另一页的过程。 当 Web 浏览器尝试打开已重定向的 URL 时,将打开一个具有其他 URL 的页面。 一个 URL 可能有多个重定向。
使用重定向的原因:
- URL 缩短
- 防止在移动网页时断开链接
- 允许属于同一所有者的多个域名引用一个网站
- 隐私保护
- HTTP 到 HTTP 的过渡
- 恶意目的,例如网络钓鱼攻击或恶意软件分发
发出请求时,HttpClient
遵循重定向,最多 20 个。 max_redirects
属性用于配置此行为。 值 0 表示不遵循任何重定向。
redirect.php
<?php
require 'vendor/autoload.php';
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'https://httpbin.org/redirect/4', [
'max_redirects' => 3,
]);
echo $response->getStatusCode();
我们将 GET 请求发送到重定向四次的 URL,同时将max_redirects
属性设置为三。 这意味着我们获得 302 重定向状态代码。 如果我们增加max_redirects
值,我们应该得到 200。
HttpClient
查询参数
查询参数是统一资源定位器(URL)的一部分,该 URL 将值分配给指定的参数。 这是将数据发送到目标服务器的一种方法。
http://example.com/api/users?name=John%20Doe&occupation=gardener
查询参数在?
字符之后指定。&
分隔了多个字段。 特殊字符(例如空格)被编码。 在上面的字符串中,空格用%20
值编码。
Symfony HttpClient
在将值包含在 URL 中之前会自动对其进行编码。
query_params.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'https://httpbin.org/get', [
'query' => [
'name' => 'John Doe',
],
]);
echo $response->getContent();
我们将name
字段发送到https://httpbin.org/get
URL。 在响应中,我们返回了 URL 参数。
$ php query_params.php
{
"args": {
"name": "John Doe"
},
"headers": {
"Accept-Encoding": "deflate, gzip",
"Host": "httpbin.org",
"User-Agent": "Symfony HttpClient/Curl"
},
...
}
这是输出。
使用 httpbin 的 Docker 容器
httpbin.org
还提供了一个用于测试的 Docker 容器。
$ docker run -p 80:80 kennethreitz/httpbin
我们运行容器。
docker_ex.php
<?php
require 'vendor/autoload.php';
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://localhost:80/anything',
[
'json' => ['message' => 'Hello there'],
]);
dump($response->toArray());
在示例中,我们使用 httpbin 的服务连接到容器。 localhost:80/anything
返回传递给请求的任何内容。
HTTP 基本认证
HTTP 基本认证是一种简单的质询和响应机制,其中服务器从客户端请求凭据。 客户端在授权标头中将凭据传递给服务器。 认证信息不以任何方式加密或哈希。 它使用 Base64 算法编码。 因此,仅将 HTTP 基本认证与HTTPS
一起使用才被认为是安全的。
HTTP 基本认证使用 HTTP 标头中的标准字段,从而无需握手。
authenticate.php
<?php
require('vendor/autoload.php');
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create([
'auth_basic' => ['user7', 'passwd']
]);
$response = $httpClient->request('GET',
'https://httpbin.org/basic-auth/user7/passwd');
echo $response->getStatusCode();
dump($response);
在示例中,我们使用 HTTP 基本认证。
$httpClient = HttpClient::create([
'auth_basic' => ['user7', 'passwd']
]);
HTTP 基本认证通过auth_basic
选项指定。 所有请求将使用相同的凭据。
$response = $httpClient->request('GET',
'https://httpbin.org/basic-auth/user7/passwd');
不要与 URL 中的用户名和密码混淆; 这仅用于在 httpbin 的服务中进行测试。
HttpClient
流数据
自 HTTP 1.1 以来,块传输编码是一种流数据传输机制。 在分块传输编码中,数据流被分为一系列不重叠的块。
块彼此独立地发送和接收。 每个块之前都有其大小(以字节为单位)。
在 Symfony HttpClient
中,流传输是通过stream()
完成的。
streaming.php
<?php
require 'vendor/autoload.php';
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$url = 'https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.0/FreeBSD-12.0-RELEASE-amd64-mini-memstick.img';
$response = $httpClient->request('GET', $url, [
'buffer' => false,
]);
if (200 !== $response->getStatusCode()) {
throw new \Exception('Failed to create a request');
}
$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
fwrite($fileHandler, $chunk->getContent());
}
在示例中,我们下载了 FreeBSD ISO 映像。
$response = $httpClient->request('GET', $url, [
'buffer' => false,
]);
我们创建一个对指定 URL 的 GET 请求; (可选)我们可以关闭内存缓冲。
$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
fwrite($fileHandler, $chunk->getContent());
}
我们以块的形式获取响应内容,并将其保存在文件中。
Symfony HttClient
Webapp 示例
在下面的示例中,我们创建一个 Symfony Web 应用,该应用使用HttpClient
生成请求。 我们使用HttpClientInterface
注入HttpClient
。
该应用向https://jsonplaceholder.typicode.com/users
发出 GET 请求,该请求返回十个用户。
$ composer create-project symfony/skeleton symfapp
$ cd symfapp
$ composer require annotations
$ composer require maker server --dev
$ composer require symfony/http-client
我们创建一个新的 Symfony 框架应用并安装一些依赖项。
config/packages/framework.yaml
framework:
...
http_client:
max_host_connections: 5
default_options:
max_redirects: 3
在framework.yaml
文件中,我们可以配置HttpClient
。
$ php bin/console make:controller DataController
我们创建一个新的控制器。
src/Controller/DataController.php
<?php
namespace App\Controller;
use App\Service\UserService;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class DataController extends AbstractController
{
/**
* @Route("/data", name="data")
*/
public function index(UserService $userService): Response
{
$data = $userService->getUsers();
return $this->json($data);
}
}
DataController
注入UserService
,调用其getUsers()
方法以检索数据。 数据作为 JSON 返回给调用方。
src/Service/UserService.php
<?php
namespace App\Service;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class UserService
{
private $httpClient;
public function __construct(HttpClientInterface $httpClient)
{
$this->httpClient = $httpClient;
}
public function getUsers(): Array
{
$response = $this->httpClient->request('GET',
'https://jsonplaceholder.typicode.com/users');
$data = $response->getContent();
$decoded = json_decode($data);
return $decoded;
}
}
这是UserService.
public function __construct(HttpClientInterface $httpClient)
{
$this->httpClient = $httpClient;
}
我们用HttpClientInterface
注入 HttpClient。
public function getUsers(): Array
{
$response = $this->httpClient->request('GET',
'https://jsonplaceholder.typicode.com/users');
$data = $response->getContent();
$decoded = json_decode($data);
return $decoded;
}
我们生成一个 GET 请求,解码数据并返回它。
$ php bin/console server:run
我们运行该应用并导航到localhost:8000/data
。
在本教程中,我们使用了 Symfony HttpClient
组件。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 验证教程, Symfony 服务教程, Symfony 表单教程 , PHP 教程或列出所有 Symfony 教程。
{% raw %}
Symfony Flash 消息
Symfony Flash 消息教程展示了如何在 Symfony 中创建 Flash 消息。 Flash 消息是用于用户通知的临时消息。 它们存储在一个会话中,并且一旦检索就消失。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
Symfony Flash 示例
在下面的示例中,我们有一个简单的表单,其中有一个输入框用于输入用户名。 如果用户输入的名称无效(空或仅包含空格),则应用将在表单上方显示一个闪烁通知。
注意:在我们的应用中,我们有一个 GET 表单。 GET 方法被认为是安全,因此我们未实现 CSRF 保护。 Symfony CSRF 教程涵盖了 Symfony 中的 CSRF 保护。
$ composer create-project symfony/skeleton flashmsg
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd flashmsg
我们转到项目目录。
$ composer require annotations twig
我们安装了两个包:annotations
和twig
。
$ composer require server maker --dev
我们安装了开发 Web 服务器和 Symfony maker
。
src/Service/Validate.php
<?php
namespace App\Service;
class Validate
{
public function isValid(?string $name): bool
{
if (!isset($name) || trim($name) === '') {
return false;
} else {
return true;
}
}
}
Validate
服务检查提供的字符串是否为空或仅包含空格。
注意:在生产应用中,我们使用一些验证库,例如 Symfony 的
symfony/validator
或 PHP Rackit 或 Respect。
$ php bin/console make:controller FormController
创建了FormController
。
src/Controller/FormController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Service\Validate;
class FormController extends AbstractController
{
/**
* @Route("/", name="index")
*/
public function index()
{
return $this->render('form/index.html.twig');
}
/**
* @Route("/form", name="form")
*/
public function doForm(Request $request, Validate $valService)
{
$name = $request->query->get("name");
$validated = $valService->isValid($name);
if ($validated) {
$msg = sprintf("Hello %s!", $name);
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
} else {
$this->addFlash(
'notice', 'Invalid name entered'
);
return $this->redirectToRoute("index");
}
}
}
FormController
响应根路径和形式路径。
/**
* @Route("/", name="index")
*/
public function index()
{
return $this->render('form/index.html.twig');
}
根路径返回 HTML 表单。
/**
* @Route("/form", name="form")
*/
public function doForm(Request $request, Validate $valService)
{
在doForm()
方法中,我们注入了Request
对象和Validate
服务。
$name = $request->get("name");
$validated = $valService->isValid($name);
我们检索名称输入并对其进行验证。
$this->addFlash(
'notice', 'Invalid name entered'
);
return $this->redirectToRoute("index");
如果输入无效,我们将添加带有addFlash()
的 Flash 消息,并在index
路径上添加确定。
templates/form/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block stylesheets %}
<style> .flash-notice { color: red } </style>
{% endblock %}
{% block body %}
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
<form action="/form">
<div>
<label>Enter your name:</label>
<input type="text" name="name">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
FormController
返回一个表单页面。 它包含用户名的输入。
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
当应用重定向到此页面时,我们浏览 Flash 消息并将其显示在表单上方的div
标签中。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
base.html.twig
模板包含其他模板文件共享的代码。 它定义了将在子模板中替换的块。
$ php bin/console server:run
我们运行该应用。
在本教程中,我们在 Symfony 中处理了 Flash 消息。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 验证教程, Symfony 服务教程, Symfony 表单教程 , PHP 教程或列出所有 Symfony 教程。
{% endraw %}
{% raw %}
在 Symfony 中发送邮件
Symfony 邮件教程显示了如何在 Symfony 中发送简单邮件。 Symfony 使用 SwiftMailer 发送电子邮件。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 是带有一些商业附加组件的免费软件。 Symfony 的灵感来自 Ruby on Rails,Django 和 Spring Framework。
SwiftMailer
SwiftMailer 是免费的功能丰富的 PHP 邮件程序。 Symfony 通过其symfony/swiftmailer-bundle
集成了 SwiftMailer。
Symfony 发送邮件示例
在示例中,我们发送了一封简单的电子邮件。 我们使用 Twig 创建电子邮件模板。
建立应用
我们首先使用composer
建立应用。
$ composer create-project symfony/skeleton mail
$ cd mail
我们创建一个新的 Symfony 骨架项目,然后进入新创建的项目目录。
$ composer req twig annotations monolog
我们安装了 Web 应用所需的三个基本 Symfony 包。
$ composer req symfony/swiftmailer-bundle
我们安装symfony/swiftmailer-bundle
。
$ composer req maker server --dev
我们安装了用于开发的包:maker
和server
。
.env
...
MAILER_URL=smtp://smtp.example.com:465?encryption=ssl&auth_mode=login&username=admin@example.com&password=s$cret
在.env
文件中,设置MAILER_URL
变量。 它包含将要发送电子邮件的 SMTP 服务器。 如果您是初学者,请避免使用 Gmail,因为由于 Gmail 的高度安全性,因此正确设置 Gmail 是一项复杂的任务。
而是使用托管服务提供商提供的 SMTP 服务器或诸如 mailgun 或 mailtrap 之类的服务。 必需的选项(例如端口号和加密)由提供商/服务提供。
$ php bin/console make:controller TestMailController
我们创建一个TestMailController
,其中包含一个用于发送电子邮件的简单链接。
src/Controller/TestMailController.php
<?php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestMailController extends AbstractController
{
/**
* @Route("/test/mail", name="test_mail")
*/
public function index(Request $request, \Swift_Mailer $mailer,
LoggerInterface $logger)
{
$name = $request->query->get('name');
$message = new \Swift_Message('Test email');
$message->setFrom('admin@zetcode.com');
$message->setTo('admin2@zetcode.com');
$message->setBody(
$this->renderView(
'emails/mymail.html.twig',
['name' => $name]
),
'text/html'
);
$mailer->send($message);
$logger->info('email sent');
$this->addFlash('notice', 'Email sent');
return $this->redirectToRoute('home');
}
}
在TestMailController
的index()
方法中,我们发送电子邮件。 请注意,发送电子邮件的代码不应在生产应用的控制器中。 它应该在某种服务。 但是出于简单原因,我们将其保留在此处。
public function index(Request $request, \Swift_Mailer $mailer,
LoggerInterface $logger)
{
我们注入Request
,Swift_Mailer
和记录器。
$name = $request->query->get('name');
我们获取在 GET 请求中使用的名称。
$message = new \Swift_Message('Test email');
$message->setFrom('example@example.com');
$message->setTo('example2@example.com');
创建了Swift_Message
。 from
和to
电子邮件值经过硬编码,以简化此示例。 您可以删除硬编码的值作为练习。 (将源电子邮件设置为参数,从表单中获取目标电子邮件。)
$message->setBody(
$this->renderView(
'emails/mymail.html.twig',
['name' => $name]
),
'text/html'
);
使用setBody()
,我们设置电子邮件的正文。 renderView()
方法从提供的 Twig 模板渲染视图。 我们将$name
变量传递给模板。
$mailer->send($message);
电子邮件带有send()
发送。
$logger->info('email sent');
$this->addFlash('notice', 'Email sent');
我们登录&闪烁一条消息。 成功发送电子邮件后,将显示即显消息。
return $this->redirectToRoute('home');
我们重定向到显示 Flash 消息的主页。
templates/emails/myemail.html.twig
Hi {{ name }}! You've got a test email.
Thanks!
这是电子邮件的简单模板。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。 它包含一个用于发送电子邮件的简单链接。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
HomeController
呈现主页。
templates/home/index.html
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block stylesheets %}
<style>
.flash-notice {
margin: 8px;
padding-left: 8px;
width: 150px;
background-color: rgb(113, 241, 113)
}
.hide {
opacity: 0;
transition: opacity 1000ms;
}
</style>
{% endblock %}
{% block body %}
<a href="/test/mail?name=Peter Novak">Send a test mail</a>
{% for message in app.flashes('notice') %}
<div id="flash-notice" class="flash-notice">
{{ message }}
</div>
{% endfor %}
{% block javascripts %}
<script src="main.js"></script>
{% endblock %}
{% endblock %}
主页包含用于发送电子邮件的链接。 如果发送了电子邮件,我们会显示一条通知。 单击此通知可以将其隐藏。
<style>
.flash-notice {
margin: 8px;
padding-left: 8px;
width: 150px;
background-color: rgb(113, 241, 113)
}
.hide {
opacity: 0;
transition: opacity 1000ms;
}
</style>
我们为通知设置了一些样式。 同样,hide
类提供了一个简单的淡出动画。 通过将此类插入通知元素,可以在 JavaScript 中启动动画。
<a href="/test/mail?name=Peter Novak">Send a test mail</a>
此链接发出触发电子邮件的 GET 请求。 我们随请求发送一个name
属性。 名称是硬编码的; 作为练习,您可以创建一个表单,该表单将指定名称和目标电子邮件。
{% for message in app.flashes('notice') %}
<div id="flash-notice" class="flash-notice">
{{ message }}
</div>
{% endfor %}
如果有即时消息,我们将其显示。
<script src="main.js"></script>
动画由位于main.js
文件中的 JavaScript 代码控制。
public/main.js
const flash = document.getElementById('flash-notice');
flash.addEventListener('click', function () {
flash.classList.add('hide');
});
当我们单击 Flash 消息时,事件回调将hide
类添加到元素的类列表中,从而启动淡出动画。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
这是基本的 Twig 模板。
在本教程中,我们展示了如何在 Symfony 中发送简单的电子邮件。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 服务教程, Symfony 请求教程, Symfony Flash 消息 , Symfony 表单教程, PHP 教程。
请参阅 Mailtrap 的如何在 Symfony 中发送电子邮件的示例。
{% endraw %}
{% raw %}
Symfony 保留表单值
Symfony 保留表单值教程展示了在表单提交失败后如何在表单提交后保持表单值。 在本教程中,我们进行传统的表单提交; 我们不使用表单构建器。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
保留表单值
用户提交表单后,将由应用进行验证。 当验证失败时,应用将用户重定向回表单,显示验证错误。 最好将已输入的值保留在表格中。
Symfony 保留表单值示例
在示例中,我们有一个简单的表单,其中包含两个字段:名称和电子邮件。 提交表单后,我们检查 CSRF 保护并使用 Symfony 的Validator
验证输入值。 我们将输入的值存储到会话中,以在提交失败时取回它们。
建立应用
我们首先使用composer
建立应用。
$ composer create-project symfony\skeleton formkeepvals
$ cd formkeepvals
我们创建一个新的 Symfony 骨架项目,然后进入新创建的项目目录。
$ composer require twig annot validator
我们安装了三个基本的 Symfony 包:twig
,annot
和validator
。 包可能具有别名。 例如,symfony/validator
具有两个别名:validator
和validation
。 有关更多详细信息,请检查 Symfony 食谱服务器。
$ composer require symfony/security-csrf
$ composer require symfony/monolog-bundle
跨站点请求伪造需要security-csrf
包,而日志记录则需要monolog-bundle
包。
$ composer require symfony/property-access
我们安装了PropertyAccess
组件,该组件用于方便读取和写入对象和数组的属性/键。
$ composer require maker server --dev
我们安装制造商组件和开发服务器。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。 控制器将表单发送给客户端。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/home", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
这是一个简单的控制器,可将包含 Web 表单的视图发送给用户。
$ php bin/console make:controller MessageController
我们创建一个MessageController
来响应表单提交。
src/Controller/MessageController.php
<?php
namespace App\Controller;
use App\Service\ValidationService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MessageController extends AbstractController
{
/**
* @Route("/message", name="message")
*/
public function index(Request $request, ValidationService $validator)
{
$token = $request->get("token");
$valid = $validator->validateToken($token);
if (!$valid) {
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
$name = $request->request->get("name");
$email = $request->request->get("email");
$input = ['name' => $name, 'email' => $email];
$errorMessages = $validator->validateInput($input);
if (count($errorMessages) > 0)
{
$session = $request->getSession();
$session->set('name', $name);
$session->set('email', $email);
foreach ($errorMessages as $key => $val) {
$this->addFlash($key, $val);
}
return $this->redirectToRoute('home');
} else {
return new Response("User saved", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
}
在MessageController
中,我们检查 CSRF 令牌,验证表单输入值,并将响应发送回客户端。
public function index(Request $request, ValidationService $validator)
{
验证委派给ValidationService
,后者被注入到方法中。
$token = $request->get("token");
$valid = $validator->validateToken($token);
if (!$valid) {
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
我们获得 CSRF 令牌并对其进行验证。 如果验证失败,我们将带有错误消息的响应发送回客户端。
$name = $request->request->get("name");
$email = $request->request->get("email");
$input = ['name' => $name, 'email' => $email];
$errorMessages = $validator->validateInput($input);
我们检索表单输入值,并使用验证服务对其进行验证。 如果验证服务失败,它将返回错误消息。
if (count($errorMessages) > 0)
{
$session = $request->getSession();
$session->set('name', $name);
$session->set('email', $email);
...
如果有一些错误消息,我们将输入值添加到会话中,以便我们可以在重定向后检索它们。
foreach ($errorMessages as $key => $val) {
$this->addFlash($key, $val);
}
我们将消息添加到 Flash 包中; 闪存袋用于存储临时消息,例如我们的验证消息。
return $this->redirectToRoute('home');
我们使用redirectToRoute()
重定向回表单。
src/Service/ValidationService.php
<?php
namespace App\Service;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
class ValidationService
{
private $tokenManager;
private $validator;
private $accessor;
private $logger;
public function __construct(CsrfTokenManagerInterface $tokenManager,
ValidatorInterface $validator, PropertyAccessorInterface $accessor,
LoggerInterface $logger)
{
$this->tokenManager = $tokenManager;
$this->validator = $validator;
$this->accessor = $accessor;
$this->logger = $logger;
}
public function validateToken($token): bool
{
$csrf_token = new CsrfToken('myform', $token);
$isValid = $this->tokenManager->isTokenValid($csrf_token);
if (!$isValid) {
$this->logger->error("CSRF failure");
}
return $isValid;
}
public function validateInput(array $input): array
{
$constraints = new Assert\Collection([
'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
'email' => [new Assert\Email, new Assert\NotBlank],
]);
$violations = $this->validator->validate($input, $constraints);
if (count($violations) > 0) {
$this->logger->info("Validation failed");
$messages = [];
foreach ($violations as $violation) {
$this->accessor->setValue($messages,
$violation->getPropertyPath(),
$violation->getMessage());
}
return $messages;
} else {
return [];
}
}
}
ValidationService
检查 CSRF 令牌并验证输入。
public function __construct(CsrfTokenManagerInterface $tokenManager,
ValidatorInterface $validator, PropertyAccessorInterface $accessor,
LoggerInterface $logger)
{
$this->tokenManager = $tokenManager;
$this->validator = $validator;
$this->accessor = $accessor;
$this->logger = $logger;
}
我们在构造器中注入了四个对象:令牌管理器,验证器,属性访问器和记录器。
public function validateToken($token): bool
{
$csrf_token = new CsrfToken('myform', $token);
$isValid = $this->tokenManager->isTokenValid($csrf_token);
if (!$isValid) {
$this->logger->error("CSRF failure");
}
return $isValid;
}
此代码使用令牌管理器验证 CSRF 令牌。
$constraints = new Assert\Collection([
'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
'email' => [new Assert\Email, new Assert\NotBlank],
]);
这些是验证表单输入的约束。
$violations = $this->validator->validate($input, $constraints);
使用验证器,我们可以验证表单输入值。
if (count($violations) > 0) {
$this->logger->info("Validation failed");
$messages = [];
foreach ($violations as $violation) {
$this->accessor->setValue($messages,
$violation->getPropertyPath(),
$violation->getMessage());
}
return $messages;
} else {
return [];
}
如果存在一些违规行为,我们将记录故障并生成验证错误消息。 为了构建消息,我们利用 Symfony 属性访问器。 如果没有违规,我们将返回一个空数组。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block stylesheets %}
<style>
.topmargin {
margin-top: 10px;
}
</style>
{% endblock %}
{% block body %}
<section class="ui container topmargin">
<form class="ui form" action="message" method="post">
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
{% for msg in app.flashes('name') %}
<div class="ui small red message">
{{ msg }}
</div>
{% endfor %}
<div class="field">
<label>Name:</label>
<input type="text" name="name" value="{{app.session.get('name')}}">
</div>
{% for msg in app.flashes('email') %}
<div class="ui small red message">
{{ msg }}
</div>
{% endfor %}
<div class="field">
<label>Email</label>
<input type="text" name="email" , value="{{app.session.get('email')}}">
</div>
<button class="ui button" type="submit">Send</button>
</form>
</section>
{% endblock %}
主页上有一个表格。 该表格包含两个字段:姓名和电子邮件。
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
它还包含一个隐藏字段,以防止跨站点请求伪造。
{% for msg in app.flashes('name') %}
<div class="ui small red message">
{{ msg }}
</div>
{% endfor %}
如果闪存包中有一些错误消息,我们将显示它们。
<input type="text" name="name" value="{{app.session.get('name')}}">
输入标签从会话中检索其值(如果有)。 重定向到表单后,这很有用。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet">
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.js"></script>
{% block javascripts %}{% endblock %}
</body>
</html>
这是基本的 Twig 模板。 它包含语义 UI CSS 框架。
在本教程中,我们验证了 Symfony 应用中的简单表单。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 服务教程, Symfony Flash 消息, Symfony 表单教程 , PHP 教程或列出所有 Symfony 教程。
{% endraw %}
Symfony @Route
注解教程
Symfony @Route
注解教程展示了如何在 Symfony 中使用@Route
注解创建路由。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Fabien Potencier 是 Symfony 的原始作者。 Symfony 受到 Spring 框架的极大启发。
@Route
注解
路由是从 URL 路径到控制器的映射。 例如,/about
URL 映射到MyController
的about()
方法。
@Route
注解用于创建路径。 其他选项是 XML 和 YAML 配置文件以及 PHP 代码。 该注解用于文档字符串中。
Symfony @Route
示例
在下面的示例中,我们使用@Route
的各种选项。
$ composer create-project symfony/skeleton routeanno
$ cd routeanno
使用composer
,我们创建一个新的 Symfony 骨架项目。 我们导航到项目目录。
$ composer require maker
$ composer require annotations
我们安装了两个模块:annotations
和maker
。 @Route
在annotations
模块中定义。
$ composer require server --dev
我们安装开发 Web 服务器。
$ php bin/console make:controller MyController
创建了MyController
。
src/Controller/MyController.php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MyController extends AbstractController
{
/**
* @Route("/home")
*/
public function home()
{
return new Response("home", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
/**
* @Route("/about", methods={"GET", "POST"})
*/
public function about(Request $request)
{
$method = $request->getRealMethod();
$msg = "about: " . $method;
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
/**
* @Route("/news/{id}", requirements={"page"="\d+"})
*/
public function news($id)
{
$msg = 'News ' . $id;
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
MyController
具有使用@Route
创建的三个路由。
/**
* @Route("/home")
*/
public function home()
{
return new Response("home", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
在这里,我们将/home
路径映射到home()
方法。
/**
* @Route("/about", methods={"GET", "POST"})
*/
public function about(Request $request)
{
$method = $request->getRealMethod();
$msg = "about: " . $method;
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
使用methods
选项,我们可以将请求限制为指定的方法类型。 在我们的例子中,仅针对 GET 和 POST 请求才调用about()
方法。
/**
* @Route("/news/{id}", requirements={"page"="\d+"})
*/
public function news($id)
{
$msg = 'News ' . $id;
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
使用requirements
选项,我们为 URL 路径指定允许的字符。 {id}
是整数值的占位符。
也可以将注解放置在控制器类上。 这用作所有路由路径的前缀。
$ php bin/console make:controller TestController
我们创建一个新的控制器。
src/Controller/TestController.php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* @Route("/test")
*/
class TestController extends AbstractController
{
/**
* @Route("/car")
*/
public function car()
{
$msg = 'Testing car';
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
/**
* @Route("/book")
*/
public function book()
{
$msg = 'Testing book';
return new Response($msg, Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
TestController
带有@Route("/test")
注解。 因此,URL 路径将为/test/car
和/test/book
。
$ php bin/console debug:router
--------------- ---------- -------- ------ ------------
Name Method Scheme Host Path
--------------- ---------- -------- ------ ------------
app_my_home ANY ANY ANY /home
app_my_about GET|POST ANY ANY /about
app_my_news ANY ANY ANY /news/{id}
app_test_car ANY ANY ANY /test/car
app_test_book ANY ANY ANY /test/book
--------------- ---------- -------- ------ ------------
我们可以使用bin/console debug:router
命令列出创建的路由。
运行示例
我们启动服务器并使用curl
工具测试创建的路由。
$ php bin/console server:run
我们启动开发服务器。
$ curl localhost:8000/home
home
$ curl -X POST localhost:8000/about
about: POST
$ curl localhost:8000/news/34
News 34
$ curl localhost:8000/test/car
Testing car
$ curl localhost:8000/test/book
Testing book
我们使用curl
生成请求。
In this tutorial we have created routes in Symfony using @Route
annotation.
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 创建路由, Symfony 表单教程, PHP 教程。
Symfony 创建路由
Symfony 创建路由教程展示了如何在 Symfony 中使用注解,XML,YAML 和 PHP 创建路由。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
路由
路由是从 URL 路径到控制器的映射。 例如,/about
URL 映射到MyController
的about()
方法。
Symfony 允许使用注解,XML,YAML 和 PHP 创建路由。
Symfony 创建路由示例
在以下示例中,我们以不同的方式创建路由。
$ composer create-project symfony/skeleton createroutes
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd createroutes
我们转到项目目录。
$ composer require maker
$ composer require annotations
我们安装了两个模块:annotations
和maker
。
$ composer require server --dev
我们安装开发 Web 服务器。
$ php bin/console make:controller MyController
创建了MyController
。
src/Controller/MyController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MyController extends AbstractController
{
/**
* @Route("/about", name="about")
*/
public function about()
{
return new Response("This is About page", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
public function index()
{
return new Response("This is Index page", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
public function news()
{
return new Response("This is News page", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
public function contacts()
{
return new Response("This is Contacts page", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
MyController
具有使用注解,XML,YAML 和 PHP 创建的四个路由。 每个路由均返回简单文本。
/**
* @Route("/about", name="about")
*/
public function about()
{
return new Response("This is About page", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
About 路由与@Route
注解映射。
config/routes.yaml
index:
path: /
controller: App\Controller\MyController::index
索引路由映射到 YAML 配置文件中。
config/routes.xml
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="contacts" controller="App\Controller\MyController::contacts" path="/contacts" >
</route>
</routes>
联系人路由映射到 XML 配置文件中。
config/routes.php
<?php
use App\Controller\MyController;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
$routes = new RouteCollection();
$routes->add('news', new Route('/news', [
'_controller' => [MyController::class, 'news']
]));
return $routes;
新闻路由是使用 PHP 代码创建的。
$ php bin/console server:run
我们启动开发服务器。
$ curl localhost:8000/about
This is About page
$ curl localhost:8000/news
This is News page
$ curl localhost:8000/
This is Index page
$ curl localhost:8000/contacts
This is Contacts page
我们使用curl
生成请求。
在本教程中,我们使用注解,XML,YAML 配置和 PHP 代码在 Symfony 中创建了路由。
您可能也对以下相关教程感兴趣: Symfony @Route
注解教程, Symfony 简介, Symfony 表单教程, PHP 教程。
Symfony 控制台命令教程
Symfony 控制台命令教程介绍了如何在 Symfony 中创建控制台命令。 我们将在控制台应用中创建几个命令。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
Symfony 控制台组件
Symfony 控制台组件使我们可以创建命令行命令。 控制台命令可用于创建 CronJob,导入,批处理作业或某些支持性任务。 Symfony 控制台命令可以在 Symfony 控制台应用或 Web 应用中使用。 在本教程中,我们将为控制台应用创建命令。
Symfony 控制台命令示例
在以下示例中,我们使用 Symfony 控制台组件创建 Symfony 控制台应用。
$ mkdir commands
$ cd commands
我们创建一个项目目录并找到它。
$ composer require symfony/console
我们安装console
包。
composer.json
{
"name": "Symfony command application",
"description":
"This application demonstrates the usage of a Symfony command in a console application",
"require": {
"symfony/console": "^4.2"
},
"autoload": {
"psr-4": {
"App\\": "src"
}
}
}
我们更新composer.json
文件。 我们启用App
名称空间下src
目录中的 PHP 类的自动加载。
$ composer dump-autoload -o
创建文件后,我们需要调用composer dump-autoload -o
命令,该命令将创建一个将类映射到 PHP 文件的文件。
在应用中,我们将有五个命令:
TimeCommand
- 显示当前日期和时间MessageCommand
- 显示来自用户输入的消息ColorCommand
- 以彩色显示消息BooksCommand
- 在表格中显示书籍列表AskNameCommand
- 交互式询问用户名
这些命令在src/Command
目录中创建。 社区必须扩展Symfony\Component\Console\Command
并实现其configure()
和execute()
方法。
之后,将命令与add()
一起添加到Symfony\Component\Console\Application
。
src/Command/TimeCommand.php
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TimeCommand extends Command
{
protected function configure()
{
$this->setName('time')
->setDescription('Shows current date and time')
->setHelp('This command prints the current date and time');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$now = date('c');
$message = sprintf("Current date and time: %s", $now);
$output->writeln($message);
}
}
TimeCommand
显示当前日期和时间。
protected function configure()
{
$this->setName('time')
->setDescription('Shows current date and time')
->setHelp('This command prints the current date and time');
}
在configure()
中,我们使用setName()
设置命令的名称。 名称将显示在可用命令列表中。 我们还为命令添加了描述和帮助。
protected function execute(InputInterface $input, OutputInterface $output)
{
$now = date('c');
$message = sprintf("Current date and time: %s", $now);
$output->writeln($message);
}
InputInterface
用于从用户获取输入,OutputInterface
用于显示输出。 在我们的例子中,我们使用标准 ISO 格式的date()
获取当前日期和时间,并使用writeln()
将其输出到控制台。
src/Command/MessageCommand.php
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
class MessageCommand extends Command
{
protected function configure()
{
$this->setName('msg')
->setDescription('Prints a user provided message')
->setHelp('This command prints a message provided by the user')
->addArgument('msg', InputArgument::REQUIRED, 'Pass a message');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$message = sprintf('The message is: %s', $input->getArgument('msg'));
$output->writeln($message);
}
}
MessageCommand
打印从用户的参数检索到的消息,并将其输出到控制台。
$this->setName('msg')
->setDescription('Prints a user provided message')
->setHelp('This command prints a message provided by the user')
->addArgument('msg', InputArgument::REQUIRED, 'Pass a message');
该参数可以是必需的,也可以是可选的。 InputArgument::REQUIRED
值使该参数成为必需参数。
$message = sprintf('The message is: %s', $input->getArgument('msg'));
$output->writeln($message);
我们从输入中检索带有getArgument()
的参数,然后使用writeln()
将该参数写入控制台。
src/Command/ColorCommand.php
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
class ColorCommand extends Command
{
protected function configure()
{
$this->setName('colc')
->setDescription('Shows output in color')
->setHelp('This command shows output in color');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("<info>Today is a windy day</info>");
$outputStyle = new OutputFormatterStyle('red');
$output->getFormatter()->setStyle('redt', $outputStyle);
$output->writeln('<redt>Tomorrow will be snowing</redt>');
}
}
ColorCommand
以彩色输出文本。
$output->writeln("<info>Today is a windy day</info>");
在这种情况下,我们使用内置的info
格式样式。
$outputStyle = new OutputFormatterStyle('red');
$output->getFormatter()->setStyle('redt', $outputStyle);
$output->writeln('<redt>Tomorrow will be snowing</redt>');
我们还可以使用OutputFormatterStyle
创建自定义输出样式。 我们的redt
以红色显示文字。
src/Command/BooksCommand.php
<?php
namespace App\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class BooksCommand extends Command
{
protected function configure()
{
$this->setName('books')
->setDescription('Shows books in a table')
->setHelp('This command demonstrates the usage of a table helper');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$table = new Table($output);
$table->setHeaderTitle('Books')
->setHeaders(['Title', 'ISBN', 'Author', 'Publisher'])
->setRows([
['Java Language Features', '978-1-4842-3347-4', 'Kishori Sharan', 'Apress' ],
['Python Testing with pytest', '978-1-68-050-240-4', 'Brian Okken', 'The Pragmatic Programmers' ],
['Deep Learning with Python', '978-1-61729-443-3', 'Francois Chollet', 'Manning' ],
['Laravel up & Running', '978-1-491-93698-5', 'Matt Stauffer', 'O\'Reilly' ],
['Sams Teach Yourself TCP/IP', '978-0-672-33789-5', 'Joe Casad', 'SAMS' ]
]);
$table->render();
}
}
BooksCommand
使用表格助手以表格格式输出数据。
$table = new Table($output);
我们创建一个Table
帮助器的实例。
$table->setHeaderTitle('Books')
->setHeaders(['Title', 'ISBN', 'Author', 'Publisher'])
->setRows([
['Java Language Features', '978-1-4842-3347-4', 'Kishori Sharan', 'Apress' ],
['Python Testing with pytest', '978-1-68-050-240-4', 'Brian Okken', 'The Pragmatic Programmers' ],
['Deep Learning with Python', '978-1-61729-443-3', 'Francois Chollet', 'Manning' ],
['Laravel up & Running', '978-1-491-93698-5', 'Matt Stauffer', 'O\'Reilly' ],
['Sams Teach Yourself TCP/IP', '978-0-672-33789-5', 'Joe Casad', 'SAMS' ]
]);
我们建立表。 表标题标题由setHeaderTitle()
指定。 header
名称由setHeaders()
指定。 最后,将数据与setRows()
相加。
$table->render();
该表使用render()
呈现。
src/Command/AskNameCommand.php
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class AskNameCommand extends Command
{
protected function configure()
{
$this->setName('ask')
->setDescription('Interactively asks name from the user')
->setHelp('This command asks a user name interactively and prints it');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = $this->getHelper('question');
$question = new Question("Enter your name: ", "guest");
$name = $helper->ask($input, $output, $question);
$message = sprintf("Hello %s!", $name);
$output->writeln($message);
}
}
AskNameCommand
使用问题助手来请求用户输入。
$helper = $this->getHelper('question');
使用getHelper()
创建一个问题帮助器。
$question = new Question("Enter your name: ", "guest");
创建一个新的Question
问题。 第二个参数是默认值。
$name = $helper->ask($input, $output, $question);
问题通过ask()
激活。 用户输入存储在$name
变量中。
$message = sprintf("Hello %s!", $name);
我们使用sprintf()
从用户输入构建消息。
$output->writeln($message);
最后,该消息在终端中显示为writeln()
。
使用Symfony\Component\Console\Application
创建一个新的 Symfony 应用。
Application.php
<?php
require __DIR__ . '/vendor/autoload.php';
use App\Command\TimeCommand;
use App\Command\BooksCommand;
use App\Command\ColorCommand;
use App\Command\AskNameCommand;
use App\Command\MessageCommand;
use Symfony\Component\Console\Application;
$app = new Application();
$app->add(new MessageCommand());
$app->add(new TimeCommand());
$app->add(new AskNameCommand());
$app->add(new BooksCommand());
$app->add(new ColorCommand());
$app->run();
我们用五个命令创建一个 Symfony 控制台应用。
$app = new Application();
创建一个新的控制台应用。
$app->add(new MessageCommand());
$app->add(new TimeCommand());
$app->add(new AskNameCommand());
$app->add(new BooksCommand());
$app->add(new ColorCommand());
我们向应用添加命令。
$app->run();
应用从run()
启动。
$ php application.php list
Console Tool
...
Available commands:
ask Interactively asks name from the user
books Shows books in a table
colc Shows output in color
help Displays help for a command
list Lists commands
msg Prints a user provided message
time Shows current date and time
我们可以获得命令列表。
$ php application.php books
+----------------------------+--------------- Books -----------------+---------------------------+
| Title | ISBN | Author | Publisher |
+----------------------------+--------------------+------------------+---------------------------+
| Java Language Features | 978-1-4842-3347-4 | Kishori Sharan | Apress |
| Python Testing with pytest | 978-1-68-050-240-4 | Brian Okken | The Pragmatic Programmers |
| Deep Learning with Python | 978-1-61729-443-3 | Francois Chollet | Manning |
| Laravel up & Running | 978-1-491-93698-5 | Matt Stauffer | O'Reilly |
| Sams Teach Yourself TCP/IP | 978-0-672-33789-5 | Joe Casad | SAMS |
+----------------------------+--------------------+------------------+---------------------------+
我们运行books
命令。
$ php application.php time
Current date and time: 2018-12-20T23:27:16+01:00
我们运行time
命令。
在本教程中,我们在 Symfony 控制台应用中创建了五个控制台命令。
$ php application.php ask
Enter your name: Peter
Hello Peter!
我们运行ask
命令。
在本教程中,我们在 Symfony 控制台应用中创建了五个控制台命令。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony 验证教程, Symfony Flash 消息, Symfony 服务教程 , Symfony 表单教程, PHP 教程。
{% raw %}
Symfony 上传文件
Symfony 上传文件教程显示了如何在 Symfony 应用中上传文件。 在示例中,我们使用普通形式发送文件; 我们不使用表单构建器。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受 Spring 框架和 Ruby on Rails 的极大启发。
上传文件
为了上传文件,form
必须将enctype
设置为multipart/form-data
,并且input
的类型设置为file
。
同样,在 PHP 的php.ini
中,文件上传由file_uploads
选项控制。
Symfony 文件上传示例
在示例中,我们有一个带有一个输入字段的简单表单:要上传的文件。 提交表单后,我们验证 CSRF 令牌并加载图像,检索其名称,并将文件存储在var
目录中。
创建一个 Symfony 项目并安装包
composer 工具用于生成 Symfony 骨架项目并安装必要的包。
$ composer create-project symfony/skeleton upload
$ cd upload
我们创建一个新的 Symfony 项目,然后转到项目目录。
$ composer require maker annotations twig
我们为 Web 开发安装了三个基本的 Symfony 包:annotations
,maker
和twig
。 这些是生成路由,控制器和模板所必需的。
$ composer require symfony/security-csrf
$ composer require symfony/monolog-bundle
跨站点请求伪造需要security-csrf
包,而日志记录则需要monolog-bundle
包。
$ composer require server --dev
$ composer require symfony/profiler-pack --dev
在开发阶段,我们还安装了内置服务器和分析器。
构建 Symfony 应用
我们定义了要上传图像的目录。
config/services.yaml
parameters:
upload_dir: '../var/uploads'
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$uploadDir: '%upload_dir%'
我们定义一个参数,其中包含应将图像上传到的目录的名称。 upload_dir
参数绑定到可以注入的$uploadDir
变量。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。 控制器将表单发送给客户端。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
这是一个简单的控制器,可将包含 Web 表单的视图发送给用户。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<form action="doUpload" method="post" enctype="multipart/form-data">
<input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
<div>
<label>File to upload:</label>
<input type="file" name="myfile">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
此视图创建一个表单。 它定义了multipart/form-data
编码类型和file
输入。 此外,它还具有 CSRF 隐藏输入令牌。
$ php bin/console make:controller UploadController
我们创建一个UploadController
来响应表单提交。 我们不需要为该控制器生成的树枝模板; 因此,我们将其删除。
src/Controller/UploadController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Service\FileUploader;
use Psr\Log\LoggerInterface;
class UploadController extends AbstractController
{
/**
* @Route("/doUpload", name="upload")
*/
public function index(Request $request, string $uploadDir,
FileUploader $uploader, LoggerInterface $logger)
{
$token = $request->get("token");
if (!$this->isCsrfTokenValid('upload', $token))
{
$logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
$file = $request->files->get('myfile');
if (empty($file))
{
return new Response("No file specified",
Response::HTTP_UNPROCESSABLE_ENTITY, ['content-type' => 'text/plain']);
}
$filename = $file->getClientOriginalName();
$uploader->upload($uploadDir, $file, $filename);
return new Response("File uploaded", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
在UploadController
中,我们检查 CSRF 令牌,从请求中获取文件,然后调用上载器服务upload()
方法。
public function index(Request $request, string $uploadDir,
FileUploader $uploader, LoggerInterface $logger)
{
我们注入了请求对象,上传目录参数,FileUploader
服务和记录器。
$token = $request->get("token");
if (!$this->isCsrfTokenValid('upload', $token))
{
$logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
我们检索令牌并使用isCsrfTokenValid()
方法对其进行验证。 如果验证失败,我们将记录事件并发送简单的响应"Operation not allowed"
和Response::HTTP_BAD_REQUEST
响应代码。
$file = $request->files->get('myfile');
if (empty($file))
{
return new Response("No file specified", Response::HTTP_UNPROCESSABLE_ENTITY,
['content-type' => 'text/plain']);
}
我们检查用户是否使用empty()
方法指定了格式的任何文件。 如果输入字段为空,我们将使用Response::HTTP_UNPROCESSABLE_ENTITY
响应代码将纯文本"No file specified"
发送回客户端。
$filename = $file->getClientOriginalName();
我们使用getClientOriginalName()
获得文件名。
$uploader->upload($uploadDir, $file, $filename);
我们调用上载器服务upload()
方法,该方法将文件移动到所选目录。 我们向该方法传递目录名,文件数据和文件名。
return new Response("File uploaded", Response::HTTP_OK,
['content-type' => 'text/plain']);
如果一切正常,我们将使用Response::HTTP_OK
响应代码将简单的消息"File uploaded"
发送回客户端。
src/Service/FileUploader.php
<?php
namespace App\Service;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Psr\Log\LoggerInterface;
class FileUploader
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function upload($uploadDir, $file, $filename)
{
try {
$file->move($uploadDir, $filename);
} catch (FileException $e){
$this->logger->error('failed to upload image: ' . $e->getMessage());
throw new FileException('Failed to upload file');
}
}
}
FileUploader
服务使用move()
将文件移动到上传目录。 当操作失败时,我们抛出FileException
。 这将导致生成错误页面。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<form action="doUpload" method="post" enctype="multipart/form-data">
<input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
<div>
<label>File to upload:</label>
<input type="file" name="myfile">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
该模板包含表单。
templates/bundles/TwigBundle/Exception/error.html.twig
{% extends "base.html.twig" %}
{% block title %}
Problem detected
{% endblock %}
{% block body %}
<div>
<p>
There was a problem: {{ exception.message }}
</p>
</div>
{% endblock %}
我们覆盖了 Twig 的error.html.twig
模板。 我们需要创建此确切的目录路径:templates
目录内的bundles/TwigBundle/Exception/
。 发生FileException
时,将为用户生成此错误视图。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
这是基本的 Twig 模板。
在本教程中,我们展示了如何在 Symfony 应用中上传文件。
您可能也会对以下相关教程感兴趣: Symfony 简介, Symfony DBAL 教程, Symfony 表单教程, Symfony 服务教程 , Symfony 验证教程, PHP 教程。
{% endraw %}
Symfony 服务教程
Symfony 服务教程展示了如何在 Symfony 中创建服务。 该服务从数据库中获取数据。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。
Symfony 服务
Symfony 应用的功能分为称为服务的较小块。 服务是一个 PHP 对象。 服务位于 Symfony 服务容器中。 有许多内置服务。 可以通过使用类型提示在 Symfony 应用中自动连接服务。
使用php bin/console debug:container
命令生成可用服务的列表。
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
这是一个services.yaml
配置文件。 src
目录中的 PHP 类可以通过类型提示自动注入到我们的代码中。
Symfony 服务示例
在以下示例中,我们从 MySQL 数据库中获取数据。 数据检索委托给特定的应用组件:Symfony 服务。
$ composer create-project symfony/skeleton simpleservice
$ cd simpleservice
使用composer
,我们创建一个新的 Symfony 骨架项目。 然后我们找到新创建的项目目录。
$ composer req annot orm-pack
我们安装了三个模块:annotations
和orm-pack
。
$ composer req server maker --dev
我们安装了开发 Web 服务器和 Symfony maker
。
countries_mysql.sql
CREATE TABLE countries(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100), population INT);
INSERT INTO countries(name, population) VALUES('China', 1382050000);
INSERT INTO countries(name, population) VALUES('India', 1313210000);
INSERT INTO countries(name, population) VALUES('USA', 324666000);
INSERT INTO countries(name, population) VALUES('Indonesia', 260581000);
INSERT INTO countries(name, population) VALUES('Brazil', 207221000);
INSERT INTO countries(name, population) VALUES('Pakistan', 196626000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Bangladesh', 162099000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Russia', 146838000);
INSERT INTO countries(name, population) VALUES('Japan', 126830000);
INSERT INTO countries(name, population) VALUES('Mexico', 122273000);
INSERT INTO countries(name, population) VALUES('Philippines', 103738000);
这是一些测试数据。 它在 MySQL 中创建一个小表。 我们可以使用 MySQL source
命令执行文件。
config/packages/doctrine.yaml
...
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
...
默认情况下,我们有一个为 Doctrine DBAL 配置的 MySQL 数据库。 教义数据库抽象层(DBAL)是位于 PDO 之上的抽象层,并提供了直观,灵活的 API,可用于与最流行的关系数据库进行通信。
.env
...
DATABASE_URL=mysql://user12:s$cret@localhost:3306/mydb
在.env
文件中,我们配置数据库 URL。
$ php bin/console make:controller DataController
用bin/console
创建一个DataController
。
src/Controller/DataController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use App\Service\DataService;
class DataController extends AbstractController
{
/**
* @Route("/data", name="data")
*/
public function index(DataService $dserv)
{
$countries = $dserv->findAll();
return $this->json([
'data' => $countries
]);
}
}
DataController
以 JSON 格式返回countries
表中的所有行。 它使用DataService
服务。
public function index(DataService $dserv)
{
通过参数注入创建DataService
。
注意:为简单起见,我们已将数据库访问代码放置在服务类中。 生产应用中还有另一层:存储库。 数据库访问代码放置在存储库类中,该存储库类从服务类中调用。
src/Service/DataService.php
<?php
namespace App\Service;
use Doctrine\DBAL\Driver\Connection;
class DataService
{
private $conn;
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
/**
* Finds all countries
*/
public function findAll() {
$queryBuilder = $this->conn->createQueryBuilder();
$queryBuilder->select('*')->from('countries');
$data = $queryBuilder->execute()->fetchAll();
return $data;
}
}
DataService
包含一种从countries
表中检索所有行的方法。 它使用 Symfony 的 DBAL 执行查询。
public function __construct(Connection $conn)
{
$this->conn = $conn;
}
该服务还使用自动装配来创建Connection
对象。
/**
* Finds all countries
*/
public function findAll() {
$queryBuilder = $this->conn->createQueryBuilder();
$queryBuilder->select('*')->from('countries');
$data = $queryBuilder->execute()->fetchAll();
return $data;
}
我们使用 DBAL QueryBuilder
从表中获取所有行。 Doctrine DBAL QueryBuilder
提供了一个方便,流畅的接口来创建和运行数据库查询。
$ php bin/console server:run
Web 服务器已启动。
$ curl localhost:8000/data
{"data":[{"id":"1","name":"China","population":"1382050000"},
{"id":"2","name":"India","population":"1313210000"},
{"id":"3","name":"USA","population":"324666000"},
{"id":"4","name":"Indonesia","population":"260581000"},
{"id":"5","name":"Brazil","population":"207221000"},
{"id":"6","name":"Pakistan","population":"196626000"},
{"id":"7","name":"Nigeria","population":"186988000"},
{"id":"8","name":"Bangladesh","population":"162099000"},
{"id":"9","name":"Nigeria","population":"186988000"},
{"id":"10","name":"Russia","population":"146838000"},
{"id":"11","name":"Japan","population":"126830000"},
{"id":"12","name":"Mexico","population":"122273000"},
{"id":"13","name":"Philippines","population":"103738000"}]}
我们使用curl
命令创建一个请求。
在本教程中,我们在 Symfony 中创建了一个简单的服务。 该服务从数据库中获取数据,并在 Symfony 控制器中自动连线。
您可能也对以下相关教程感兴趣: Symfony 简介, Doctrine DBAL QueryBuilder
教程, Symfony DBAL 教程, Symfony 上传文件教程 , Symfony 表单教程, PHP 教程或列出所有 Symfony 教程。
{% raw %}
Symfony 验证教程
Symfony 验证教程展示了如何在 Symfony 应用中验证数据。 在本教程中,我们使用手动验证。 我们不使用教义注解。
Symfony
Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 的灵感来自 Ruby on Rails,Django 和 Spring 框架。
Symfony 验证
来自用户的输入必须经过验证。 Symfony 提供了Validator
组件来执行验证任务。 该组件基于 Java 的 Bean 验证规范。
验证器旨在针对约束验证对象。 约束是对条件为真的断言。 Symfony 具有许多内置约束,包括NotBlank
,Email
,Length
和Isbn
。 也可以创建自定义约束。
Symfony 验证示例
在示例中,我们有一个简单的表单,其中包含两个字段:name
和email
。 提交表单后,我们使用 Symfony 的Validator
手动验证字段。 在示例中,我们使用Length
,NotBlank
和Email
约束。
安装组件
$ composer create-project symfony/skeleton myval
$ cd myval
我们创建一个新的 Symfony 项目,然后转到项目目录。
$ composer req maker server --dev
我们安装symfony/maker-bundle
和symfony/web-server-bundle
。 这些对于开发模式很有用。 请注意,我们正在使用包的别名。
$ composer req twig annotations
我们安装twig-bundle
和注解。 注解位于sensio/framework-extra-bundle
中。
$ composer req validator
symfony/validator
包包含 Symfony 验证工具。
$ composer req security-csrf
$ composer req monolog
$ composer req property-access
跨站点请求伪造需要symfony/security-csrf
包,symfony/monolog-bundle
用于记录日志,symfony/property-access
用于操纵 PHP 属性。
构建 Symfony 应用
$ php bin/console make:controller HomeController
我们创建一个HomeController
。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class HomeController extends AbstractController
{
/**
* @Route("/home", name="home")
*/
public function index(): Response
{
return $this->render('home/index.html.twig');
}
}
这是一个简单的控制器,可将包含 Web 表单的视图发送给用户。
$ php bin/console make:controller FormController
我们创建一个FormController
来响应表单提交。
src/Controller/FormController.php
<?php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class FormController extends AbstractController
{
/**
* @Route("/sendForm", name="form")
*/
public function index(Request $request, ValidatorInterface $validator,
LoggerInterface $logger): Response {
$token = $request->request->get("token");
if (!$this->isCsrfTokenValid('myform', $token)) {
$logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
$name = $request->request->get("name");
$email = $request->request->get("email");
$input = ['name' => $name, 'email' => $email];
$constraints = new Assert\Collection([
'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
'email' => [new Assert\Email(), new Assert\notBlank],
]);
$violations = $validator->validate($input, $constraints);
if (count($violations) > 0) {
$accessor = PropertyAccess::createPropertyAccessor();
$errorMessages = [];
foreach ($violations as $violation) {
$accessor->setValue($errorMessages,
$violation->getPropertyPath(),
$violation->getMessage());
}
return $this->render('form/violations.html.twig',
['errorMessages' => $errorMessages]);
} else {
return new Response("Validation passed", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
}
在FormController
中,我们检查 CSRF 令牌,验证表单输入值,并将响应发送回客户端。
注意:为简单起见,我们将验证代码放入控制器中。 在生产应用中,最好将此类代码放在单独的服务类中。
public function index(Request $request, ValidatorInterface $validator,
LoggerInterface $logger)
{
我们注入了请求对象,验证器对象和记录器对象。
$token = $request->request->get("token");
if (!$this->isCsrfTokenValid('myform', $token)) {
$logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
我们检索令牌并使用isCsrfTokenValid()
方法对其进行验证。
$name = $request->request->get("name");
$email = $request->request->get("email");
$input = ['name' => $name, 'email' => $email];
我们获取请求输入参数,并将其放入数组中。
$constraints = new Assert\Collection([
'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
'email' => [new Assert\Email(), new Assert\notBlank]
]);
我们创建约束的集合。 我们可以为单个值分配多个约束。
$violations = $validator->validate($input, $constraints);
我们使用validate()
方法对照约束条件验证输入数据。 该方法返回可能的违规。 Symfony 验证器返回ConstraintViolationList
。 我们使用 Symfony 访问器来处理列表。
if (count($violations) > 0) {
我们检查是否有违规行为。
$accessor = PropertyAccess::createPropertyAccessor();
$errorMessages = [];
foreach ($violations as $violation) {
$accessor->setValue($errorMessages,
$violation->getPropertyPath(),
$violation->getMessage());
}
Symfony PropertyAccess
用于在将违规消息发送到模板之前对其进行处理。 ConstraintViolationList
转换为 PHP 数组,其中的键是表单字段,值是错误消息。 稍后使用for
指令在 Twig 中处理该数组。
return $this->render('form/violations.html.twig',
['errorMessages' => $errorMessages]);
我们在单独的页面中呈现错误消息。 这通常是使用 Flash 消息完成的。 看看 Symfony 保持表单值教程如何使用 Flash。
} else {
return new Response("Validation passed", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
如果一切正常,我们将发送一条简单消息验证已通过。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<form action="sendForm" method="post">
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
<div>
<label>Name:</label>
<input type="text" name="name">
</div>
<div>
<label>Email</label>
<input type="email" name="email">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
该表格包含两个字段:姓名和电子邮件。
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
它还包含一个隐藏字段,以防止跨站点请求伪造。
templates/form/violations.html.twig
{% extends 'base.html.twig' %}
{% block title %}Violations{% endblock %}
{% block body %}
<h2>Validation failed</h2>
<ul>
{% for field, errorMessage in errorMessages %}
<li>{{ field }}: {{ errorMessage }}</li>
{% endfor %}
</ul>
{% endblock %}
在违规视图中,我们浏览违规并列出它们。
{% for field, errorMessage in errorMessages %}
<li>{{ field }}: {{ errorMessage }}</li>
{% endfor %}
使用for
指令,我们遍历错误消息并显示错误消息。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
这是基本的 Twig 模板。
$ php bin/console server:run
我们启动开发服务器。 然后找到localhost:8000/home
网址以获取表格。
在本教程中,我们验证了 Symfony 应用中的简单表单。 我们使用了手动验证。
您可能也对以下相关教程感兴趣: Symfony 简介, Symfony DBAL 教程, Symfony 表单教程, PHP 教程, 或列出所有 Symfony 教程。
{% endraw %}
{% raw %}
Symfony 翻译教程
Symfony 翻译教程显示了如何在 Symfony 中使用不同的语言。
国际化和本地化正在使计算机软件适应不同的语言和文化。
Symfony 翻译
对于国际化和本地化,Symfony 包含用于这些任务的symfony/translation
包。
翻原文件具有以下强制格式: domain.locale.loader
。 domain
是将消息组织成组的一种可选方式。 默认域为messages
。 locale
定义翻原文件的语言环境; 例如zh
,sk
或de
。 loader
是一种加载和解析文件的方式。 例如 xlf,php 或 yaml。
可以将翻译后的文本写入不同的文件格式。 Symfony 转换组件支持许多转换格式,例如 XLIFF,PHP,Qt,.po
,.mo
,JSON,CSV 或 INI。 推荐的格式是 XLIFF。
可以将翻原文件放在三个不同的目录中,其中第一个位置具有最高优先级:translations/
,src/Resources/%bundle name%/translations/
或Resources/translations/
。
Symfony 翻译示例
在下面的示例中,我们创建一个简单的 Web 应用,该应用根据语言环境返回一条消息。 我们使用默认的messages
域。
$ composer create-project symfony/skeleton symtrans
使用composer
,我们创建一个新的 Symfony 骨架项目。
$ cd symtrans
我们转到项目目录。
$ composer require symfony/translation
$ composer require annotations
$ composer require maker
我们安装了三个包:symfony/translation
,annotations
和maker
。
$ composer require server --dev
我们安装开发 Web 服务器。
config/packages/translation.yaml
framework:
default_locale: '%locale%'
translator:
paths:
- '%kernel.project_dir%/translations'
fallbacks:
- '%locale%'
在translation.yaml
文件中,我们定义了默认语言环境。 它使用%locale%
参数,该参数在services.yaml
配置文件中设置。
config/services.yaml
parameters:
locale: 'en'
...
默认情况下,我们具有英语默认语言环境。
translations/messages.en.xlf
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="en" datatype="plaintext"
original="file.ext">
<body>
<trans-unit id="text.message">
<source>text.message</source>
<target>Today is a beautiful day</target>
</trans-unit>
</body>
</file>
</xliff>
这是英语的翻原文件。
<trans-unit id="text.message">
<source>text.message</source>
<target>Today is a beautiful day</target>
</trans-unit>
我们只有一个翻译部门。 翻译单位由 ID 标识。
translations/messages.sk.xlf
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="sk" datatype="plaintext"
original="file.ext">
<body>
<trans-unit id="text.message">
<source>text.message</source>
<target>Dnes je krásny deň.</target>
</trans-unit>
</body>
</file>
</xliff>
这是斯洛伐克语的翻原文件。
$ php bin/console clear:cache
请注意,我们可能需要清除缓存。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Translation\TranslatorInterface;
class HomeController extends Controller
{
/**
* @Route("/", name="home")
*/
public function index(TranslatorInterface $translator)
{
$translated = $translator->trans('text.message',[], null, 'sk');
return new Response($translated);
}
}
HomeController
返回翻译后的消息。
public function index(TranslatorInterface $translator)
{
我们注入了TranslatorInterface
以获取 Symfony 翻译服务。
$translated = $translator->trans('text.message',[], null, 'sk');
转换程序的trans()
方法转换给定的消息。 最后一个参数是语言环境。 在我们的案例中,我们使用了斯洛伐克语区域设置,因此我们希望在斯洛伐克语中输入一条消息。
$ php bin/console server:start
我们启动服务器。
$ curl localhost:8000
Dnes je krásny deň.
我们使用curl
生成 GET 请求,并在斯洛伐克语中收到一条消息。
使用 Twig 模板
接下来,我们将使用 Twig 模板。
$ composer require twig
我们安装 Twig。
HomeController.php
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Translation\TranslatorInterface;
class HomeController extends Controller
{
/**
* @Route("/home", name="home")
*/
public function index(TranslatorInterface $translator)
{
$message = $translator->trans('text.message',[], null, 'sk');
return $this->render('home/index.html.twig', [
'message' => $message
]);
}
}
控制器翻译消息并呈现 Twing 模板。 它向模板发送翻译后的消息。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
{% trans %}%message%{% endtrans %}
{% endblock %}
在模板中,我们使用 Twig {% trans %}
和{% endtrans %}
指令显示消息。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
这是自动生成的基本模板文件。
在本教程中,我们使用了 Symfony 中的翻译。
您可能也会对以下相关教程感兴趣: Symfony 简介, Symfony 表单教程, Symfony 验证教程, Symfony 上传文件教程, Symfony 服务教程, Symfony 请求教程, PHP 教程。
{% endraw %}