ZetCode-Symfony-教程-一-

ZetCode Symfony 教程(一)

原文:ZetCode

协议:CC BY-NC-SA 4.0

Symfony DBAL 教程

原文: http://zetcode.com/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 表单教程

原文: http://zetcode.com/symfony/form/

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

我们安装了两个模块:annotationstwig

$ 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 教程

列出所有 Symfony 教程

{% endraw %}

{% raw %}

Symfony CSRF 教程

原文: http://zetcode.com/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 教程

原文: http://zetcode.com/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 简介

原文: http://zetcode.com/symfony/intro/

这是 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 教程

列出所有 Symfony 教程

{% endraw %}

{% raw %}

Symfony 请求教程

原文: http://zetcode.com/symfony/request/

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

我们安装模块annotationstwig

$ 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教程

原文: http://zetcode.com/symfony/httpclient/

Symfony HttpClient教程展示了如何使用HttpClient组件在 Symfony 中创建 HTTP 请求。 该组件提供使用 API​​ 的工具,并支持同步和异步操作。

有关更多信息,请阅读官方 HttpComponent 文档。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。

在示例中,我们将使用httpbin.orghttp://jsonplaceholder.typicode.com/在线服务。

$ composer require symfony/http-client
$ composer require symfony/var-dumper

我们安装了HttpClientvar-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 消息

原文: http://zetcode.com/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

我们安装了两个包:annotationstwig

$ 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 中发送邮件

原文: http://zetcode.com/symfony/mail/

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     

我们安装了用于开发的包:makerserver

.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');
    }
}

TestMailControllerindex()方法中,我们发送电子邮件。 请注意,发送电子邮件的代码不应在生产应用的控制器中。 它应该在某种服务。 但是出于简单原因,我们将其保留在此处。

public function index(Request $request, \Swift_Mailer $mailer, 
    LoggerInterface $logger)
{

我们注入RequestSwift_Mailer和记录器。

$name = $request->query->get('name');

我们获取在 GET 请求中使用的名称。

$message = new \Swift_Message('Test email');
$message->setFrom('example@example.com');
$message->setTo('example2@example.com');

创建了Swift_Messagefromto电子邮件值经过硬编码,以简化此示例。 您可以删除硬编码的值作为练习。 (将源电子邮件设置为参数,从表单中获取目标电子邮件。)

$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 保留表单值

原文: http://zetcode.com/symfony/keepformvalues/

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 包:twigannotvalidator。 包可能具有别名。 例如,symfony/validator具有两个别名:validatorvalidation。 有关更多详细信息,请检查 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注解教程

原文: http://zetcode.com/symfony/routeannotation/

Symfony @Route注解教程展示了如何在 Symfony 中使用@Route注解创建路由。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Fabien Potencier 是 Symfony 的原始作者。 Symfony 受到 Spring 框架的极大启发。

@Route注解

路由是从 URL 路径到控制器的映射。 例如,/about URL 映射到MyControllerabout()方法。

@Route注解用于创建路径。 其他选项是 XML 和 YAML 配置文件以及 PHP 代码。 该注解用于文档字符串中。

Symfony @Route示例

在下面的示例中,我们使用@Route的各种选项。

$ composer create-project symfony/skeleton routeanno
$ cd routeanno

使用composer,我们创建一个新的 Symfony 骨架项目。 我们导航到项目目录。

$ composer require maker
$ composer require annotations

我们安装了两个模块:annotationsmaker@Routeannotations模块中定义。

$ 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 创建路由

原文: http://zetcode.com/symfony/createroutes/

Symfony 创建路由教程展示了如何在 Symfony 中使用注解,XML,YAML 和 PHP 创建路由。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受到 Spring 框架的极大启发。

路由

路由是从 URL 路径到控制器的映射。 例如,/about URL 映射到MyControllerabout()方法。

Symfony 允许使用注解,XML,YAML 和 PHP 创建路由。

Symfony 创建路由示例

在以下示例中,我们以不同的方式创建路由。

$ composer create-project symfony/skeleton createroutes

使用composer,我们创建一个新的 Symfony 骨架项目。

$ cd createroutes

我们转到项目目录。

$ composer require maker
$ composer require annotations

我们安装了两个模块:annotationsmaker

$ 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 控制台命令教程

原文: http://zetcode.com/symfony/commands/

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 上传文件

原文: http://zetcode.com/symfony/uploadfile/

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 包:annotationsmakertwig。 这些是生成路由,控制器和模板所必需的。

$ 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 服务教程

原文: http://zetcode.com/symfony/service/

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

我们安装了三个模块:annotationsorm-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 验证教程

原文: http://zetcode.com/symfony/validation/

Symfony 验证教程展示了如何在 Symfony 应用中验证数据。 在本教程中,我们使用手动验证。 我们不使用教义注解。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 的灵感来自 Ruby on Rails,Django 和 Spring 框架。

Symfony 验证

来自用户的输入必须经过验证。 Symfony 提供了Validator组件来执行验证任务。 该组件基于 Java 的 Bean 验证规范。

验证器旨在针对约束验证对象。 约束是对条件为真的断言。 Symfony 具有许多内置约束,包括NotBlankEmailLengthIsbn。 也可以创建自定义约束。

Symfony 验证示例

在示例中,我们有一个简单的表单,其中包含两个字段:nameemail。 提交表单后,我们使用 Symfony 的Validator手动验证字段。 在示例中,我们使用LengthNotBlankEmail约束。

安装组件

$ composer create-project symfony/skeleton myval
$ cd myval

我们创建一个新的 Symfony 项目,然后转到项目目录。

$ composer req maker server --dev

我们安装symfony/maker-bundlesymfony/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 翻译教程

原文: http://zetcode.com/symfony/translation/

Symfony 翻译教程显示了如何在 Symfony 中使用不同的语言。

国际化和本地化正在使计算机软件适应不同的语言和文化。

Symfony 翻译

对于国际化和本地化,Symfony 包含用于这些任务的symfony/translation包。

翻原文件具有以下强制格式: domain.locale.loaderdomain是将消息组织成组的一种可选方式。 默认域为messageslocale定义翻原文件的语言环境; 例如zhskdeloader是一种加载和解析文件的方式。 例如 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/translationannotationsmaker

$ 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 %}

posted @ 2024-10-24 18:18  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报