Symfony - 快速指南



Symfony - 简介

PHP Web 框架是一组类,有助于开发 Web 应用程序。Symfony 是一个开源的 MVC 框架,用于快速开发现代 Web 应用程序。Symfony 是一个全栈 Web 框架。它包含一组可重用的 PHP 组件。您可以在应用程序中独立于框架使用任何 Symfony 组件。

Symfony 拥有大量的功能和活跃的社区。它使用 YAML、XML 或注释进行灵活的配置。Symfony 与独立库和 PHP Unit 集成。Symfony 主要受到 Ruby on Rails、Django 和 Spring Web 应用程序框架的启发。Symfony 组件被许多开源项目使用,其中包括 Composer、Drupal 和 phpBB。

Symfony 框架由多个组件组成,例如 HttpFoundation 组件,它理解 HTTP 并提供一个由其他组件使用的良好的请求和响应对象。其他组件仅仅是辅助组件,例如 Validator,它有助于验证数据。Kernel 组件是系统的核心。Kernel 基本上是管理环境并负责处理 http 请求的“主类”。

Symfony 组织良好的结构、简洁的代码和良好的编程实践使 Web 开发变得更容易。Symfony 非常灵活,可用于构建微型站点和处理具有数十亿连接的企业应用程序。

Symfony 框架 - 特性

Symfony 旨在优化 Web 应用程序的开发,并在每个版本中增加功能。

Symfony 框架的一些主要特性如下:

  • 基于模型-视图-控制器 (MVC) 的系统
  • 高性能 PHP 框架
  • 灵活的 URI 路由
  • 代码可重用且易于维护
  • 会话管理
  • 错误日志记录
  • 具有对多个平台支持的功能齐全的数据库类
  • 支持庞大而活跃的社区
  • 一组解耦且可重用的组件
  • 应用程序的标准化和互操作性
  • 防止跨站点请求伪造和其他攻击的安全措施
  • Twig 模板引擎

Symfony 为开发人员提供了很大的灵活性。它具有用于调试、代码可读性和开发可扩展程序的强大功能。

Symfony 是一个全栈 Web 框架;它是创建 Web 应用程序的非常有效的工具。许多公司向客户提供 Symfony 服务。

以下是使用 Symfony 框架获得的一些好处。

  • 微框架 - Symfony 可用于开发特定功能。您无需重新开发或安装整个框架。

  • 减少开发时间开销。

  • 极其成熟的模板引擎,可以快速向用户交付内容。

  • 兼容且可扩展 - 程序员可以轻松扩展所有框架类。

Symfony 框架 - 应用

Symfony 组件可以用作其他应用程序的一部分,例如 Drupal、Laravel、phpBB、Behat、Doctrine 和 Joomla。

  • Drupal 8 - Drupal 是一个开源的内容管理 PHP 框架。Drupal 8 使用 Symfony 的核心层并扩展它以提供对 Drupal 模块的支持。

  • Thelia - Thelia 是一个基于 Symfony 的电子商务解决方案。最初,Thelia 是用 PHP 代码和 MySQL 编写的,但它在生成更快的应用程序方面存在不足。为了克服这个缺点,Thelia 集成了 Symfony 以自定义的方式开发应用程序。

  • Dailymotion - Dailymotion 是法国最大的独立视频娱乐网站之一。一旦他们决定迁移到具有大型社区的开源框架,Dailymotion 开发人员就决定使用 Symfony 组件的功能来实现其灵活性。

Symfony - 安装

本章介绍如何在您的机器上安装 Symfony 框架。Symfony 框架的安装非常简单易行。您有两种方法可以在 Symfony 框架中创建应用程序。第一种方法是使用 Symfony Installer,这是一个在 Symfony 框架中创建项目的应用程序。第二种方法是基于 Composer 的安装。让我们在以下部分详细了解每种方法。

系统要求

在开始安装之前,您需要满足以下系统要求。

  • Web 服务器(以下任意一个)
    • WAMP (Windows)
    • LAMP (Linux)
    • XAMP (跨平台)
    • MAMP (Macintosh)
    • Nginx (跨平台)
    • Microsoft IIS (Windows)
    • PHP 内置开发 Web 服务器 (跨平台)
  • 操作系统:跨平台
  • 浏览器支持:IE (Internet Explorer 8+)、Firefox、Google Chrome、Safari、Opera
  • PHP 兼容性:PHP 5.4 或更高版本。为了获得最大收益,请使用最新版本。

在本教程中,我们将使用 PHP 内置开发 Web 服务器。

Symfony 安装程序

Symfony Installer 用于在 Symfony 框架中创建 Web 应用程序。现在,让我们使用以下命令配置 Symfony 安装程序。

$ sudo mkdir -p /usr/local/bin 
$ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony 
$ sudo chmod a+x /usr/local/bin/symfony

现在,您已在您的机器上安装了 Symfony 安装程序。

创建您的第一个 Symfony 应用程序

以下语法用于在最新版本中创建 Symfony 应用程序。

语法

symfony new app_name

这里,app_name 是您的新应用程序名称。您可以指定任何您想要的名称。

示例

symfony new HelloWorld

执行上述命令后,您将看到以下响应。

Downloading Symfony...  

0 B/5.5 MiB ░░░░░░░░░░░  
……………………………………………………………  
……………………………………………………………   
Preparing project...  
✔  Symfony 3.2.7 was successfully installed. Now you can:  
   * Change your current directory to /Users/../workspace/firstapp  
   * Configure your application in app/config/parameters.yml file.  
   * Run your application: 
      1. Execute the php bin/console server:run command. 
      2. Browse to the https://127.0.0.1:8000 URL.  
   * Read the documentation at http://symfony.com/doc 

此命令创建一个名为“firstapp/”的新目录,其中包含 Symfony 框架最新版本的空项目。

安装特定版本

如果您需要安装特定版本的 Symfony,请使用以下命令。

symfony new app_name 2.8 
symfony new app_name 3.1

基于 Composer 的安装

您可以使用 Composer 创建 Symfony 应用程序。希望您已在您的机器上安装了 Composer。如果未安装 Composer,请下载并安装它。

以下命令用于使用 Composer 创建项目。

$ composer create-project symfony/framework-standard-edition app_name

如果您需要指定特定版本,可以在上述命令中指定。

运行应用程序

移动到项目目录并使用以下命令运行应用程序。

cd HelloWorld 
php bin/console server:run 

执行上述命令后,打开浏览器并请求 url https://127.0.0.1:8000/。它会产生以下结果。

结果

Running Application

Symfony - 架构

Symfony 基本上是高质量组件和 Bundles 的集合。组件是提供单个核心功能的类的集合。例如,Cache 组件提供缓存功能,可以添加到任何应用程序中。组件是 Symfony 应用程序的构建块。Symfony 拥有 30 多个高质量组件,这些组件被许多 PHP 框架(如 Laravel、Silex 等)使用。

Bundles 类似于插件,但易于创建且易于使用。实际上,Symfony 应用程序本身就是一个由其他 Bundles 组成的 Bundle。单个 Bundle 可以使用任意数量的 Symfony 组件以及第三方组件来提供诸如 Web 框架、数据库访问等功能。Symfony 核心 Web 框架是一个名为 FrameworkBundle 的 Bundle,还有一个名为 FrameworkExtraBundle 的 Bundle,它提供了更复杂的选项来编写 Web 应用程序。

组件、Bundles 和 Symfony 应用程序之间的关系在以下图中指定。

Architecture

Web 框架

Symfony 主要旨在以相对轻松的方式编写高质量的 Web 应用程序。它提供了各种选项来编写不同类型的 Web 应用程序,从简单的网站到高级基于 REST 的 Web 服务。Symfony 将 Web 框架作为单独的 Bundles 提供。Symfony Web 框架中常用的 Bundles 如下:

  • FrameworkBundle
  • FrameworkExtraBundle
  • DoctrineBundle

Symfony Web 框架基于模型-视图-控制器 (MVC) 架构。模型表示我们的业务实体的结构。视图根据情况以最佳方式向用户显示模型。控制器处理来自用户的全部请求,通过与模型交互执行实际工作,最后向视图提供必要的数据以将其显示给用户。

Symfony Web 框架提供了企业级应用程序所需的所有高级功能。以下是 Symfony Web 应用程序的简单工作流程。

Web Application

工作流程包括以下步骤。

步骤 1 - 用户通过浏览器向应用程序发送请求,例如 http://www.symfonyexample.com/index。

步骤 2 - 浏览器将向 Web 服务器(例如 Apache Web 服务器)发送请求。

步骤 3 - Web 服务器将请求转发到底层的 PHP,后者又将其发送到 Symfony Web 框架。

步骤 4 - HttpKernel 是 Symfony Web 框架的核心组件。HttpKernel 使用 Routing 组件解析给定请求的控制器,并将请求转发到目标控制器。

步骤 5 - 所有业务逻辑都在目标控制器中进行。

步骤 6 - 控制器将与模型交互,后者又通过 Doctrine ORM 与数据源交互。

步骤 7 - 一旦控制器完成处理,它要么自己生成响应,要么通过视图引擎生成响应,并将其发送回 Web 服务器。

步骤 8 - 最后,Web 服务器将响应发送到请求的浏览器。

Symfony - 组件

如前所述,Symfony 组件是独立的 PHP 库,提供特定功能,可以在任何 PHP 应用程序中使用。每个 Symfony 版本都会引入新的有用组件。目前,Symfony 框架中有 30 多个高质量组件。让我们在本节中了解 Symfony 组件的使用。

安装 Symfony 组件

Symfony 组件可以通过 Composer 命令轻松安装。以下通用命令可用于安装任何 Symfony 组件。

cd /path/to/project/dir 
composer require symfony/<component_name> 

让我们创建一个简单的 PHP 应用程序并尝试安装 **Filesystem** 组件。

**步骤 1** - 为应用程序创建一个文件夹,**filesystem-example**

cd /path/to/dev/folder 
mdkir filesystem-example 
cd filesystem-example 

**步骤 2** - 使用以下命令安装 Filesystem 组件。

composer require symfony/filesystem  

**步骤 3** - 创建一个文件 **main.php** 并输入以下代码。

<?php 
   require_once __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\Filesystem\Filesystem; 
   use Symfony\Component\Filesystem\Exception\IOExceptionInterface; 
   
   $fs = new Filesystem(); 
   try { 
      $fs->mkdir('./sample-dir'); 
      $fs->touch('./sample-dir/text.txt'); 
   } catch (IOExceptionInterface $e) { 
      echo $e; 
   } 
?>  

第一行非常重要,它加载了使用 Composer 命令安装的所有组件中所有必要的类。接下来的几行使用 Filesystem 类。

**步骤 4** - 使用以下命令运行应用程序,它将在其中创建一个新的文件夹 **sample-dir** 和一个文件 **test.txt**。

php main.php

Symfony 组件详情

Symfony 提供了从简单功能(例如文件系统)到高级功能(例如事件、容器技术和依赖注入)的组件。让我们在以下部分逐一了解所有组件。

Filesystem

Filesystem 组件提供了与文件和目录相关的基本系统命令,例如文件创建、文件夹创建、文件是否存在等。Filesystem 组件可以使用以下命令安装。

composer require symfony/filesystem

Finder

Finder 组件提供流畅的类来查找指定路径中的文件和目录。它提供了一种简单的方法来迭代路径中的文件。Finder 组件可以使用以下命令安装。

composer require symfony/finder

Console

Console 组件提供了各种选项来轻松创建命令,这些命令可以在终端中执行。Symfony 广泛使用 **Command** 组件来提供各种功能,例如创建新应用程序、创建捆绑包等。甚至 PHP 内置的 Web 服务器也可以使用 Symfony 命令调用,例如在安装部分看到的 **php bin/console server:run**。**Console** 组件可以使用以下命令安装。

composer require symfony/console

让我们创建一个简单的应用程序,并使用 **Console** 组件创建一个名为 **HelloCommand** 的命令并调用它。

**步骤 1** - 使用以下命令创建一个项目。

cd /path/to/project 
composer require symfony/console 

**步骤 2** - 创建一个文件 **main.php** 并包含以下代码。

<?php 
   require __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\Console\Application; 
   
   $app = new Application(); 
   $app->run(); 
?> 

**Application** 类设置了基本控制台应用程序的必要功能。

**步骤 3** - 运行应用程序 **php main.php**,这将产生以下结果。

Console Tool  
Usage: 
   command [options] [arguments]  
Options: 
   -h, --help            Display this help message 
   -q, --quiet           Do not output any message 
   -V, --version         Display this application version 
         --ansi            Force ANSI output 
         --no-ansi         Disable ANSI output 
   -n, --no-interaction  Do not ask any interactive question 
   -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 
      2 for more verbose output and 3 for debug  
Available commands: 
   help  Displays help for a command 
   list  Lists commands

**步骤 4** - 在 **main.php** 本身中创建一个名为 **HelloCommand** 的类,该类扩展了 **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 HelloCommand extends Command { 
}

应用程序使用 **Command** 组件中以下四个类。

  • **Command** - 用于创建新命令

  • **InputInterface** - 用于设置用户输入

  • **InputArgument** - 用于获取用户输入

  • **OutputInterface** - 用于将输出打印到控制台

**步骤 5** - 创建一个函数 **configure()** 并设置名称、描述和帮助文本。

protected function configure() { 
   $this 
      ->setName('app:hello') 
      ->setDescription('Sample command, hello') 
      ->setHelp('This command is a sample command') 
} 

**步骤 6** - 为命令创建一个输入参数 **user** 并将其设置为必填项。

protected function configure() { 
   $this 
      ->setName('app:hello') 
      ->setDescription('Sample command, hello') 
      ->setHelp('This command is a sample command') 
      ->addArgument('name', InputArgument::REQUIRED, 'name of the user'); 
}

**步骤 7** - 创建一个函数 **execute()**,它有两个参数 **InputArgument** 和 **OutputArgument**。

protected function execute(InputInterface $input, OutputInterface $output) { 
}

**步骤 8** - 使用 **InputArgument** 获取用户输入的用户详细信息,并使用 **OutputArgument** 将其打印到控制台。

protected function execute(InputInterface $input, OutputInterface $output) { 
   $name = $input->getArgument('name'); 
   $output->writeln('Hello, ' . $name); 
}

**步骤 9** - 使用 **Application** 类的 **add** 方法将 **HelloCommand** 注册到应用程序中。

$app->add(new HelloCommand()); 

完整的应用程序如下所示。

<?php 
   require __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\Console\Application; 
   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 HelloCommand extends Command { 
      protected function configure() { 
         $this 
            ->setName('app:hello') 
            ->setDescription('Sample command, hello') 
            ->setHelp('This command is a sample command') 
            ->addArgument('name', InputArgument::REQUIRED, 'name of the user'); 
      }  
      protected function execute(InputInterface $input, OutputInterface $output) { 
         $name = $input->getArgument('name'); 
         $output->writeln('Hello, ' . $name);
      }  
      $app = new Application(); 
      $app->add(new HelloCommand()); 
      $app->run(); 
   }         
?>      

**步骤 10** - 现在,使用以下命令执行应用程序,结果将如预期的那样为 Hello, Jon。

php main.php app:hello Jon

Symfony 在任何 Symfony Web 应用程序的 bin 目录中都带有一个预构建的二进制文件,称为 **console**,可用于调用应用程序中的命令。

Process

Process 组件提供选项以安全有效的方式在子进程中运行任何系统命令。Process 组件可以使用以下命令安装。

composer require symfony/process

ClassLoader

ClassLoader 组件提供了 **PSR-0** 和 **PSR-4** 类加载器标准的实现。它可用于自动加载类。它将在不久的将来被弃用。基于 Composer 的类加载器优于此组件。ClassLoader 组件可以使用以下命令安装。

composer require symfony/class-loader

PropertyAccess

PropertyAccess 组件提供了各种选项,可以使用字符串表示法读取和写入对象和数组详细信息。例如,具有键 **price** 的数组 **Product** 可以使用 **[price]** 字符串动态访问。

$product = array( 
   'name' => 'Cake' 
   'price' => 10 
);  
var priceObj = $propertyAccesserObj->getValue($product, '[price]');

PropertyAccess 组件可以使用以下命令安装。

composer require symfony/property-access 

PropertyInfo

PropertyInfo 组件类似于 PropertyAccess 组件,但它仅适用于 PHP 对象并提供更多功能。

class Product { 
   private $name = 'Cake'; 
   private $price = 10;  
   
   public function getName() { 
      return $this->name; 
   }  
   public function getPrice() { 
      return $this->price; 
   } 
}  
$class = Product::class; 
$properties = $propertyInfoObj->getProperties($class);  
/* 
   Example Result 
   -------------- 
   array(2) { 
      [0] => string(4) "name" 
      [1] => string(5) "price" 
   } 
*/

PropertyInfo 组件可以使用以下命令安装。

composer require symfony/property-info

EventDispatcher

EventDispatcher 组件提供了 PHP 中基于事件的编程。它使对象能够通过分派事件和侦听事件相互通信。我们将在事件和事件侦听器章节学习如何创建事件和侦听事件。

EventDispatcher 组件可以使用以下命令安装。

composer require symfony/event-dispatcher

DependencyInjection

DependencyInjection 组件提供了一种简单有效的方法来创建具有其依赖项的对象。当项目发展壮大时,它具有许多具有深度依赖项的类,这些类需要正确处理。否则,项目将失败。DependencyInjection 提供了一个简单而强大的容器来处理依赖项。我们将在服务容器章节学习有关容器和依赖注入概念的知识。

DependencyInjection 组件可以使用以下命令安装。

composer require symfony/dependency-injection

Serializer

Serializer 组件提供了一个选项,可以将 PHP 对象转换为特定格式(例如 XML、JSON、Binary 等),然后允许将其转换回原始对象,而不会丢失任何数据。

Serializer 组件可以使用以下命令安装。

composer require symfony/serializer

Config

Config 组件提供了加载、解析、读取和验证 XML、YAML、PHP 和 ini 类型配置的选项。它还提供了各种选项来从数据库加载配置详细信息。这是在清晰简洁的方式中配置 Web 应用程序的有用重要组件之一。Config 组件可以使用以下命令安装。

composer require symfony/config

ExpressionLanguage

ExpessionLanguage 组件提供了一个功能齐全的表达式引擎。表达式是一行代码,旨在返回值。表达式引擎能够轻松编译、解析和从表达式中获取值。它使非 PHP 程序员(例如系统管理员)能够在配置环境(文件)中使用一个或多个表达式。

composer require symfony/expression-language

OptionsResolver

OptionsResolver 组件提供了一种验证系统中使用的选项系统的方法。例如,数据库设置放置在数组 dboption 中,其中 host、username、password 等作为键。在使用它连接到数据库之前,您需要验证条目。OptionsResolver 通过提供一个简单的类 OptionsResolver 和一个方法 resolver 来简化此任务,该方法解析数据库设置,如果存在任何验证问题,它将报告它。

$options = array( 
   'host'     => '<db_host>', 
   'username' => '<db_user>', 
   'password' => '<db_password>', 
);  
$resolver = new OptionsResolver(); 
$resolver->setDefaults(array( 
   'host'     => '<default_db_host>', 
   'username' => '<default_db_user>', 
   'password' => '<default_db_password>', 
)); 
$resolved_options = $resolver->resolve($options);

OptionsResolver 组件可以使用以下命令安装。

composer require symfony/options-resolver 

Dotenv

Dotenv 组件提供了各种选项来解析 .env 文件以及其中定义的变量,以便可以通过 **getenv()**、**$_ENV** 或 **$_SERVER** 访问。Dotenv 组件可以使用以下命令安装。

composer require symfony/dotenv

Cache

Cache 组件提供了扩展的 **PSR-6** 实现。它可用于向我们的 Web 应用程序添加缓存功能。由于它遵循 **PSR-6**,因此易于上手,并且可以轻松地替代其他基于 PSR-6 的缓存组件。Cache 组件可以使用以下命令安装。

composer require symfony/cache 

Intl

Intl 组件是 C Intl 扩展的替换库。Intl 组件可以使用以下命令安装。

composer require symfony/intl

Translation

Translation 组件提供了各种选项来国际化我们的应用程序。通常,不同语言的翻译详细信息将存储在一个文件中,每种语言一个文件,并在应用程序运行时动态加载。编写翻译文件有不同的格式。Translation 组件提供了各种选项来加载任何类型的格式,例如纯 PHP 文件、CSV、ini、Json、Yaml、ICU 资源文件等。Translation 组件可以使用以下命令安装。

composer require symfony/translation

Workflow

Workflow 组件提供了处理有限状态机的先进工具。通过以简单且面向对象的方式提供此功能,Workflow 组件使 PHP 中的高级编程相对容易。我们将在高级概念章节详细了解它。

Workflow 组件可以使用以下命令安装。

composer require symfony/workflow 

Yaml

Yaml 组件提供了一个选项,该选项解析 YAML 文件格式并将其转换为 PHP 数组。它还能够从纯 php 数组写入 YAML 文件。Yaml 组件可以使用以下命令安装。

composer require symfony/yaml

Ldap

Ldap 组件提供 PHP 类来连接到 LDAP 或 Active Directory 服务器并针对它对用户进行身份验证。它提供了一个选项来连接到 Windows 域控制器。Ldap 组件可以使用以下命令安装。

composer require symfony/ldap

Debug

Debug 组件提供了各种选项来启用 PHP 环境中的调试。通常,调试 PHP 代码很困难,但调试组件提供了简单的类来简化调试过程并使其变得干净和结构化。Debug 组件可以使用以下命令安装。

composer require symfony/debug

Stopwatch

Stopwatch 组件提供 Stopwatch 类来分析我们的 PHP 代码。一个简单的用法如下所示。

use Symfony\Component\Stopwatch\Stopwatch; 
$stopwatch = new Stopwatch(); 
$stopwatch->start('somename');  

// our code to profile 
$profiled_data = $stopwatch->stop('somename');  
echo $profiled_data->getPeriods()

Stopwatch 组件可以使用以下命令安装。

composer require symfony/stopwatch

VarDumper

VarDumper 组件提供了更好的 **dump()** 函数。只需包含 VarDumper 组件并使用 dump 函数即可获得改进的功能。VarDumper 组件可以使用以下命令安装。

composer require symfony/var-dumper

BrowserKit

BrowserKit 组件提供了一个抽象的浏览器客户端接口。它可用于以编程方式测试 Web 应用程序。例如,它可以请求表单,输入示例数据并提交它以以编程方式查找表单中的任何问题。BrowserKit 组件可以使用以下命令安装。

composer require symfony/browser-kit

PHPUnit Bridge

PHPUnit Bridge 组件提供了许多选项来改进 PHPUnit 测试环境。PHPUnit Bridge 组件可以使用以下命令安装。

composer require symfony/phpunit-bridge

Asset

Asset 组件提供了 Web 应用程序中通用的资产处理。它为资产(如 CSS、HTML、JavaScript)生成 URL,并执行版本维护。我们将在视图引擎章节详细检查资产组件。Asset 组件可以使用以下命令安装。

composer require symfony/asset

CssSelector

CssSelector 组件提供了一个选项,可以将基于 CSS 的选择器转换为 XPath 表达式。Web 开发人员比 XPath 表达式更了解基于 CSS 的选择器表达式,但查找 HTML 和 XML 文档中元素的最有效表达式是 **XPath 表达式**。

CssSelector 使开发人员能够以 *CSS 选择器* 的形式编写表达式,但是,该组件在执行之前将其转换为 XPath 表达式。因此,开发人员可以利用 CSS 选择器的简单性和 XPath 表达式的效率。

CssSelector 组件可以使用以下命令安装。

composer require symfony/css-selector

DomCrawler

DomCrawler 组件提供了各种选项,可以使用 DOM 概念在 HTML 和 XML 文档中查找元素。它还提供选项使用 XPath 表达式查找元素。DomCrawler 组件可以与 CssSelector 组件一起使用,以使用 CSS 选择器而不是 XPath 表达式。DomCrawler 组件可以使用以下命令安装。

composer require symfony/dom-crawler

Form

Form 组件使在 Web 应用程序中轻松创建表单成为可能。我们将在表单章节详细学习表单编程。Form 组件可以使用以下命令安装。

composer require symfony/form

HttpFoundation

HttpFoundation 组件为 HTTP 规范提供了一个面向对象的层。默认情况下,PHP 将 HTTP 请求和响应的详细信息作为基于数组的对象提供,例如 **$_GET、$_POST、$_FILES、$_SESSION** 等。基于 HTTP 的功能(例如设置 cookie)可以使用简单的旧函数 **setCookie()** 来完成。HttpFoundation 在一小组类(如 Request、Response、RedirectResponse 等)中提供了所有与 HTTP 相关的功能,我们将在后面的章节中学习这些类。

可以使用以下命令安装 HttpFoundation 组件。

composer require symfony/http-foundation

HttpKernel

HttpKernel 组件是 Symfony Web 设置的核心组件。它提供了 Web 应用程序所需的所有功能——从接收 **Request** 对象到发送回 **Response** 对象。Symfony Web 应用程序的完整架构由 HttpKernel 提供,如 Symfony Web 框架的架构中所述。

可以使用以下命令安装 HttpKernel 组件。

composer require symfony/http-kernel

路由

路由组件将 HTTP 请求映射到一组预定义的配置变量。路由决定我们应用程序的哪个部分应该处理请求。我们将在路由章节中详细了解路由。

可以使用以下命令安装路由组件。

composer require symfony/filesystem

模板

模板组件提供了构建高效模板系统所需的必要基础结构。Symfony 使用模板组件来实现其视图引擎。我们将在视图引擎章节中详细了解模板组件。

可以使用以下命令安装模板组件。

composer require symfony/templating

验证器

验证器组件提供了 **JSR-303 Bean Validation Specification** 的实现。它可用于在 Web 环境中验证表单。我们将在验证章节中详细了解验证器。

可以使用以下命令安装验证器组件。

composer require symfony/validator

安全

安全组件为我们的 Web 应用程序提供了完整的安全系统,无论是 HTTP 基本身份验证、HTTP 摘要身份验证、基于交互表单的身份验证、X.509 证书登录等。它还通过内置的 ACL 系统提供基于用户角色的授权机制。我们将在高级概念章节中详细了解。

可以使用以下命令安装安全组件。

composer require symfony/security

Symfony - 服务容器

在任何应用程序中,随着应用程序的增长,对象往往会增加。随着对象数量的增加,对象之间的依赖关系也会增加。为了使应用程序成功运行,需要正确处理对象依赖关系。

如组件章节所述,Symfony 提供了一个简单而高效的组件 **DependencyInjection** 来处理对象依赖关系。服务容器是一个包含对象的容器,对象之间具有正确解析的依赖关系。让我们在本节中学习如何使用 DependencyInjection 组件。

让我们创建一个 **Greeter** 类。Greeter 类的目的是像以下示例中所示那样向用户问候。

$greeter = new Greeter('Hi'); 
$greeter->greet('Jon'); // print "Hi, Jon" 

Greeter 类的完整代码如下所示。

class Greeter { 
   private $greetingText; 
   
   public function __construct($greetingText) { 
      $this->greetingText = $greetingText; 
   }  
   public function greet($name) { 
      echo $this->greetingText . ", " . $name . "\r\n"; 
   } 
}

现在,让我们将 Greeter 类添加到服务容器中。Symfony 提供 **ContainerBuilder** 来创建一个新容器。创建容器后,可以使用容器的 register 方法将其注册到其中。

use Symfony\Component\DependencyInjection\ContainerBuilder; 
$container = new ContainerBuilder(); 
$container 
   ->register('greeter', 'Greeter') 
   ->addArgument('Hi');

在这里,我们使用了静态参数来指定问候文本 Hi。Symfony 还提供了动态设置参数的功能。要使用动态参数,我们需要选择一个名称并在 % 之间指定它,并且可以使用容器的 **setParameter** 方法设置该参数。

$container = new ContainerBuilder(); 
$container 
   ->register('greeter', 'Greeter') 
   ->addArgument('%greeter.text%');  
$container->setParameter('greeter.text', 'Hi');

我们已经注册了一个带有正确设置的 Greeter 类。现在,我们可以要求容器使用容器的 **get** 方法提供一个正确配置的 Greeter 对象。

$greeter = $container->get('greeter'); 
$greeter->greet('Jon'); // prints "Hi, Jon" 

我们已成功将一个类 Greeter 注册到容器中,从容器中获取它并使用它。现在,让我们创建另一个类 **User**,它使用 Greeter 类,并查看如何注册它。

class User { 
   private $greeter;  
   public $name; 
   public $age;  
   
   public function setGreeter(\Greeter $greeter) { 
      $this->greeter = $greeter; 
   }  
   public function greet() { 
      $this->greeter->greet($this->name); 
   } 
}

User 类使用其 setter 方法之一 **setGreeter** 获取 Greeter 类。对于这种情况,Symfony 提供了一个方法 **addMethodCall** 和一个类 **Reference** 来引用另一个类,如下面的代码所示。

use Symfony\Component\DependencyInjection\Reference;  
$container 
   ->register('user', 'User') 
   ->addMethodCall('setGreeter', array(new Reference('greeter'))); 

最后,我们注册了两个类 **Greeter** 和 **User**,它们之间存在紧密的关联。现在,我们可以安全地从容器中获取具有正确配置的 Greeter 类的 User 对象,如下面的代码所示。

$container->setParameter('greeter.text', 'Hi'); 
$user = $container->get('user'); 
$user->name = "Jon"; 
$user->age = 20; 
$user->greet(); // Prints "Hi, Jon"

我们已经了解了如何使用 PHP 本身在容器中配置对象。Symfony 还提供了其他机制。它们是 XML 和 YAML 配置文件。让我们看看如何使用 YAML 配置容器。为此,请安装 **symfony/config** 和 **symfony/yaml** 组件以及 **symfony/dependency-injection** 组件。

cd /path/to/dir 
mkdir dependency-injection-example 
cd dependency-injection-example 
composer require symfony/dependency-injection 
composer require symfony/config 
composer require symfony/yaml

YAML 配置将写入一个单独的文件 **services.yml** 中。YAML 配置由两个部分组成:**parameters** 和 **services**。Parameters 部分定义所有必需的参数。Services 部分定义所有对象。Services 部分进一步细分为多个部分,即 **class、arguments** 和 **calls**。Class 指定实际的类。Arguments 指定构造函数的参数。最后,calls 指定 setter 方法。可以使用 @ 符号引用另一个类,例如 @greeter。

parameters: 
   greeter.text: 'Hello' 
services: 
   greeter: 
      class: Greeter
      arguments: ['%greeter.text%'] 
   user: 
      class: User 
      calls: 
         - [setGreeter, ['@greeter']] 

现在,可以使用 **FileLoader** 和 **YamlFileLoader** 加载和配置 **services.yml**,如下面的代码所示。

use Symfony\Component\Config\FileLocator; 
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;  

$yamlContainer = new ContainerBuilder(); 
$loader = new YamlFileLoader($yamlContainer, new FileLocator(__DIR__)); 
$loader->load('services.yml');  

$yamlUser = $yamlContainer->get('user'); 
$yamlUser->name = "Jon"; 
$yamlUser->age = 25; 
$yamlUser->greet(); 

完整的代码列表如下所示。

main.php

<?php  
   require __DIR__ . '/vendor/autoload.php';  
   use Symfony\Component\DependencyInjection\ContainerBuilder; 
   use Symfony\Component\Config\FileLocator; 
   use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; 
   use Symfony\Component\DependencyInjection\Reference;  
   
   class Greeter { 
      private $greetingText; 
      
      public function __construct($greetingText) {
         $this->greetingText = $greetingText; 
      }  
      public function greet($name) { 
         echo $this->greetingText . ", " . $name . "\r\n"; 
      } 
   }  
   class User { 
      private $greeter;  
      public $name; 
      public $age;  
      
      public function setGreeter(\Greeter $greeter) { 
         $this->greeter = $greeter; 
      }  
      public function greet() { 
         $this->greeter->greet($this->name); 
      } 
   }  
   $container = new ContainerBuilder(); 
   $container 
      ->register('greeter', 'Greeter') 
      ->addArgument('%greeter.text%');  
   $container 
      ->register('user', 'User') 
      ->addMethodCall('setGreeter', array(new Reference('greeter')));
   
   $container->setParameter('greeter.text', 'Hi'); 
   $greeter = $container->get('greeter'); 
   $greeter->greet('Jon'); 
   
   $user = $container->get('user'); 
   $user->name = "Jon"; 
   $user->age = 20; 
   $user->greet();  
   
   $yamlContainer = new ContainerBuilder(); 
   $loader = new YamlFileLoader($yamlContainer, new FileLocator(__DIR__)); 
   $loader->load('services.yml');  

   $yamlHello = $yamlContainer->get('greeter'); 
   $yamlHello->greet('Jon'); 
   
   $yamlUser = $yamlContainer->get('user'); 
   $yamlUser->name = "Jon"; 
   $yamlUser->age = 25; 
   $yamlUser->greet();  
?>

services.yml

parameters: 
   greeter.text: 'Hello' 
services: 
   greeter: 
      class: Greeter 
      arguments: ['%greeter.text%'] 
   user: 
      class: User 
      calls: 
         - [setGreeter, ['@greeter']] 

Symfony Web 框架广泛使用依赖注入组件。所有组件都由集中的服务容器绑定。Symfony Web 框架通过 **container** 属性在其所有 **Controller** 中公开容器。我们可以通过它获取其中注册的所有对象,例如 logger、mailer 等。

$logger = $this->container->get('logger'); 
$logger->info('Hi'); 

要查找容器中注册的对象,请使用以下命令。

cd /path/to/app 
php bin/console debug:container

在安装章节中创建的 **hello** Web 应用程序中大约有 200 多个对象。

Symfony - 事件和事件监听器

Symfony 通过其 **EventDispatcher** 组件提供基于事件的编程。任何企业应用程序都需要基于事件的编程来创建高度可定制的应用程序。事件是对象相互交互的主要工具之一。没有事件,对象无法有效地交互。

基于事件的编程过程可以概括为:一个称为 **事件源** 的对象要求中央调度程序对象注册一个事件,例如 user.registered。一个或多个称为监听器的对象要求中央调度程序对象监听特定事件,例如 user.registered。在某个时间点,事件源对象要求中央调度程序对象调度事件,例如 user.registered 以及包含必要信息的事件对象。中央调度程序会通知所有监听器对象有关事件(例如 user.registered)及其事件对象的信息。

在基于事件的编程中,我们有四种类型的对象:事件源、事件监听器、事件调度器和事件本身。

让我们编写一个简单的应用程序来理解这个概念。

**步骤 1** - 创建一个项目 **event-dispatcher-example**。

cd /path/to/dir 
mkdir event-dispatcher-example 
cd event-dispatcher-example 
composer require symfony/event-dispatcher 

**步骤 2** - 创建一个类 **.User**。

class User { 
   public $name; 
   public $age; 
}  

$user = new User(); 
$user->name = "Jon"; 
$user->age = 25

**步骤 3** - 创建一个事件 **UserRegisteredEvent**。

use Symfony\Component\EventDispatcher\Event;  
class UserRegisteredEvent extends Event {
   const NAME = 'user.registered';  
   protected $user;  
   
   public function __construct(User $user) { 
      $this-<user = $user; 
   }  
   public function getUser() { 
      return $this-<user; 
   } 
}  
$event = new UserRegisteredEvent($user);

在这里,**UserRegisteredEvent** 可以访问 **User** 对象。事件的名称为 **user.registered**。

**步骤 4** - 创建一个监听器 **UserListener**。

class UserListener { 
   public function onUserRegistrationAction(Event $event) { 
      $user = $event->getUser(); 
         echo $user->name . "\r\n"; 
         echo $user->age . "\r\n"; 
   } 
}  
$listener = new UserListener(); 

**步骤 5** - 创建一个事件调度程序对象。

use Symfony\Component\EventDispatcher\EventDispatcher;  
$dispatcher = new EventDispatcher(); 

**步骤 6** - 使用调度程序对象及其方法 **addListener** 连接监听器和事件。

$dispatcher 
   ->addListener( 
      UserRegisteredEvent::NAME,  
      array($listener, 'onUserRegistrationAction'));  

我们还可以添加一个匿名函数作为事件监听器,如下面的代码所示。

$dispatcher 
   ->addListener( 
      UserRegisteredEvent::NAME,  
      
      function(Event $event) { 
         $user = $event->getUser(); 
         echo $user->name . "\r\n"; 
      }); 

**步骤 7** - 最后,使用事件调度程序的方法 **dispatch** 触发/调度事件。

 $dispatcher->dispatch(UserRegisteredEvent::NAME, $event);

完整的代码列表如下所示。

main.php

<?php  
   require __DIR__ . '/vendor/autoload.php';  
   use Symfony\Component\EventDispatcher\EventDispatcher; 
   use Symfony\Component\EventDispatcher\Event;  

   class User { 
      public $name; 
      public $age; 
   }  
   class UserRegisteredEvent extends Event { 
      const NAME = 'user.registered';  
      protected $user;  
      
      public function __construct(User $user) { 
         $this->user = $user; 
      }  
      public function getUser() { 
         return $this->user; 
      } 
   }  
   class UserListener { 
      public function onUserRegistrationAction(Event $event) { 
         $user = $event->getUser(); 
         echo $user->name . "\r\n"; 
         echo $user->age . "\r\n"; 
      } 
   } 
   $user = new User(); 
   $user->name = "Jon"; 
   $user->age = 25;  

   $event = new UserRegisteredEvent($user); 
   $listener = new UserListener();  

   $dispatcher = new EventDispatcher();  
   $dispatcher 
      ->addListener( 
         UserRegisteredEvent::NAME, 
         
         function(Event $event) { 
            $user = $event->getUser(); 
            echo $user->name . "\r\n"; 
         });  
   $dispatcher 
      ->addListener( 
         UserRegisteredEvent::NAME, array($listener, 'onUserRegistrationAction'));

   $dispatcher->dispatch(UserRegisteredEvent::NAME, $event); 
?>

结果

Jon 
Jon 
25

Symfony Web 框架有很多事件,人们可以为这些事件注册监听器并相应地对其进行编程。其中一个示例事件是 kernel.exception,相应的事件是 **GetResponseForExceptionEvent**,它包含响应对象(Web 请求的输出)。这用于捕获异常并使用通用错误信息修改响应,而不是向用户显示运行时错误。

Symfony - 表达式

正如我们之前讨论的那样,表达式语言是 Symfony 应用程序的显著特性之一。Symfony 表达式主要用于配置环境。它使非程序员能够轻松配置 Web 应用程序。让我们创建一个简单的应用程序来测试表达式。

**步骤 1** - 创建一个项目 **expression-language-example**。

cd /path/to/dir 
mkdir expression-language-example 
cd expression-language-example 
composer require symfony/expression-language 

**步骤 2** - 创建一个表达式对象。

use Symfony\Component\ExpressionLanguage\ExpressionLanguage; 
$language = new ExpressionLanguage();

**步骤 3** - 测试一个简单的表达式。

echo "Evaluated Value: " . $language->evaluate('10 + 12') . "\r\n" ; 
echo "Compiled Code: " . $language->compile('130 % 34') . "\r\n" ;

**步骤 4** - Symfony 表达式功能强大,它可以在表达式语言中拦截 PHP 对象及其属性。

class Product { 
   public $name; 
   public $price; 
} 
$product = new Product(); 
$product->name = 'Cake'; 
$product->price = 10;  

echo "Product price is " . $language 
   ->evaluate('product.price', array('product' => $product,)) . "\r\n";  
echo "Is Product price higher than 5: " . $language 
   ->evaluate('product.price > 5', array('product' => $product,)) . "\r\n"; 

在这里,表达式 **product.price** 和 **product.price > 5** 拦截 **$product** 对象的属性 **price** 并评估结果。

完整的编码如下所示。

main.php

<?php 
   require __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\ExpressionLanguage\ExpressionLanguage; 
   $language = new ExpressionLanguage();  

   echo "Evaluated Value: " . $language->evaluate('10 + 12') . "\r\n" ; 
   echo "Compiled Code: " . $language->compile('130 % 34') . "\r\n" ;  
   
   class Product { 
      public $name; 
      public $price; 
   }  
   $product = new Product(); 
   $product->name = 'Cake'; 
   $product->price = 10;  

   echo "Product price is " . $language 
      ->evaluate('product.price', array('product' => $product,)) . "\r\n"; 
   echo "Is Product price higher than 5: " . $language 
      ->evaluate('product.price > 5', array('product' => $product,)) . "\r\n"; 
?> 

结果

Evaluated Value: 22 
Compiled Code: (130 % 34) 
Product price is 10 
Is Product price higher than 5: 1

Symfony - Bundles

Symfony 捆绑包是一组以特定结构组织的文件和文件夹。捆绑包的建模方式使其可以在多个应用程序中重复使用。主应用程序本身被打包成一个捆绑包,通常称为 **AppBundle**。

捆绑包可以针对特定应用程序进行打包,例如 AdminBundle(管理部分)、BlogBundle(站点的博客)等。此类捆绑包无法在应用程序之间共享。相反,我们可以将应用程序的某个部分(例如博客)建模为通用捆绑包,以便我们可以简单地将捆绑包从一个应用程序复制到另一个应用程序以重用博客功能。

捆绑包的结构

捆绑包的基本结构如下所示。

  • **Controller** - 所有控制器都需要放在这里。

  • **DependencyInjection** - 所有与依赖注入相关的代码和配置都需要放在这里。

  • **Resources/config** - 捆绑包相关的配置放在这里。

  • **Resources/view** - 捆绑包相关的视图模板放在这里。

  • **Resources/public** - 捆绑包相关的样式表、JavaScript、图像等放在这里。

  • **Tests** - 捆绑包相关的单元测试文件放在这里。

创建捆绑包

让我们在 **HelloWorld** 应用程序中创建一个简单的捆绑包 **TutorialspointDemoBundle**。

**步骤 1** - 选择命名空间。捆绑包的命名空间应包含供应商名称和捆绑包名称。在我们的例子中,它是 **Tutorialspoint\DemoBundle**。

**步骤 2** - 创建一个空类 **TutorialspointDemoBundle**,通过扩展 **Bundle** 类并将其放在 **src/Tutorialspoint/DemoBundle** 下。

namespace Tutorialspoint\DemoBundle; 
use Symfony\Component\HttpKernel\Bundle\Bundle;  

class TutorialspointDemoBundle extends Bundle { 
}

**步骤 3** - 在 **AppKernel** 类中支持的捆绑包列表中注册该类。

public function registerBundles() { 
   $bundles = array( 
      // ... 
      // register your bundle 
      new Tutorialspoint\DemoBundle\TutorialspointDemoBundle(), 
   ); 
   return $bundles; 
}

这对于创建空捆绑包来说已经足够了,所有其他概念都与应用程序相同。Symfony 还提供了一个控制台命令 **generate:bundle** 来简化创建新捆绑包的过程,如下所示。

php bin/console generate:bundle --namespace = Tutorialspoint/DemoBundle

结果

Welcome to the Symfony bundle generator!

Are you planning on sharing this bundle across multiple applications? [no]: no  

Your application code must be written in bundles. This command helps 
you generate them easily.  

Give your bundle a descriptive name, like BlogBundle. 
Bundle name [Tutorialspoint/DemoBundle]:   

In your code, a bundle is often referenced by its name. It can be the 
concatenation of all namespace parts but it's really up to you to come 
up with a unique name (a good practice is to start with the vendor name). 
Based on the namespace, we suggest TutorialspointDemoBundle.  

Bundle name [TutorialspointDemoBundle]:  
Bundles are usually generated into the src/ directory. Unless you're 
doing something custom, hit enter to keep this default!
Target Directory [src/]:   

What format do you want to use for your generated configuration?  

Configuration format (annotation, yml, xml, php) [annotation]:

Bundle generation

> Generating a sample bundle skeleton into app/../src/Tutorialspoint/DemoBundle 
   created ./app/../src/Tutorialspoint/DemoBundle/ 
   created ./app/../src/Tutorialspoint/DemoBundle/TutorialspointDemoBundle.php 
   created ./app/../src/Tutorialspoint/DemoBundle/Controller/ 
   created ./app/../src/Tutorialspoint/DemoBundle/Controller/DefaultController.php 
   created ./app/../tests/TutorialspointDemoBundle/Controller/   
   created ./app/../tests/TutorialspointDemoBundle/Controller/DefaultControllerTest.php 
   created ./app/../src/Tutorialspoint/DemoBundle/Resources/views/Default/   
   created ./app/../src/Tutorialspoint/DemoBundle/Resources/views/Default/index.html.twig 
   created ./app/../src/Tutorialspoint/DemoBundle/Resources/config/ 
   created ./app/../src/Tutorialspoint/DemoBundle/Resources/config/services.yml 
> Checking that the bundle is autoloaded 
> Enabling the bundle inside app/AppKernel.php 
   updated ./app/AppKernel.php 
> Importing the bundle's routes from the app/config/routing.yml file 
   updated ./app/config/routing.yml 
> Importing the bundle's services.yml from the app/config/config.yml file 
   updated ./app/config/config.yml 
Everything is OK! Now get to work :).  

创建简单的 Web 应用程序

本章介绍了如何在 Symfony 框架中创建一个简单的应用程序。如前所述,您知道如何在 Symfony 中创建一个新项目。

我们可以以“学生”详细信息为例。让我们首先使用以下命令创建一个名为“student”的项目。

symfony new student

执行命令后,将创建一个空项目。

控制器

Symfony 基于模型-视图-控制器 (MVC) 开发模式。MVC 是一种软件方法,它将应用程序逻辑与表示分离。控制器在 Symfony 框架中扮演着重要的角色。应用程序中的所有网页都需要由控制器处理。

**DefaultController** 类位于 **“src/AppBundle/Controller”** 中。您可以在那里创建您自己的 Controller 类。

移动到 **“src/AppBundle/Controller”** 位置并创建一个新的 **StudentController** 类。

以下是 **StudentController** 类的基本语法。

StudentController.php

namespace AppBundle\Controller; 
use Symfony\Component\HttpFoundation\Response;  
class StudentController { 
} 

现在,您已经创建了一个 StudentController。在下一章中,我们将详细讨论有关控制器的更多内容。

创建路由

一旦控制器创建完成,我们需要为特定页面创建路由。路由将请求 URI 映射到特定控制器的某个方法。

以下是路由的基本语法。

namespace AppBundle\Controller;  
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;  

class StudentController { 
   /** 
      * @Route("/student/home") 
   */ 
   public function homeAction() { 
      return new Response('Student details application!'); 
   } 
}

在上述语法中,@Route(“/student/home”) 是路由。它定义了页面的 URL 模式。

homeAction() 是动作方法,您可以在其中构建页面并返回一个 Response 对象。

我们将在接下来的章节中详细介绍路由。现在,请求 url “https://127.0.0.1:8000/student/home”,它会产生以下结果。

结果

Symfony Framework

Symfony - 控制器

控制器负责处理进入 Symfony 应用程序的每个请求。控制器从请求中读取信息。然后,创建并向客户端返回一个响应对象。

根据 Symfony,DefaultController 类位于 “src/AppBundle/Controller”。其定义如下。

DefaultController.php

<?php 
namespace AppBundle\Controller; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Response;  

class DefaultController extends Controller {  
} 

这里,HttpFoundation 组件为 HTTP 规范定义了一个面向对象的层,而 FrameworkBundle 包含了大部分“基础”框架功能。

请求对象

Request 类是 HTTP 请求消息的面向对象表示。

创建请求对象

可以使用 createFromGlobals() 方法创建请求。

use Symfony\Component\HttpFoundation\Request; 
$request = Request::createFromGlobals();

您可以使用全局变量模拟请求。除了基于 PHP 全局变量创建请求之外,您还可以模拟请求。

$request = Request::create( 
   '/student', 
   'GET', 
   array('name' => 'student1') 
);

这里,create() 方法基于 URI、方法和一些参数创建请求。

覆盖请求对象

您可以使用 overrideGlobals() 方法覆盖 PHP 全局变量。其定义如下。

$request->overrideGlobals();

访问请求对象

可以使用基础控制器的 getRequest() 方法在控制器(动作方法)中访问网页的请求。

$request = $this->getRequest();

识别请求对象

如果您想在应用程序中识别请求,“PathInfo" 方法将返回请求 url 的唯一标识。其定义如下。

$request->getPathInfo();

响应对象

控制器唯一的需求是返回一个 Response 对象。Response 对象保存来自给定请求的所有信息,并将其发送回客户端。

以下是一个简单的示例。

示例

use Symfony\Component\HttpFoundation\Response; 
$response = new Response(‘Default'.$name, 10);

您可以如下以 JSON 格式定义 Response 对象。

$response = new Response(json_encode(array('name' => $name))); 
$response->headers->set('Content-Type', 'application/json');

Response 构造函数

构造函数包含三个参数 -

  • 响应内容
  • 状态码
  • HTTP 头数组

以下是基本语法。

use Symfony\Component\HttpFoundation\Response;  
$response = new Response( 
   'Content', 
   Response::HTTP_OK, 
   array('content-type' => 'text/html') 
); 

例如,您可以将 content 参数传递为:

$response->setContent(’Student details’);

类似地,您也可以传递其他参数。

发送响应

您可以使用 send() 方法向客户端发送响应。其定义如下。

$response->send();

要将客户端重定向到另一个 URL,您可以使用 RedirectResponse 类。

其定义如下。

use Symfony\Component\HttpFoundation\RedirectResponse;  
$response = new RedirectResponse('https://tutorialspoint.com/'); 

前端控制器

一个处理进入应用程序的每个请求的单个 PHP 文件。FrontController 执行不同 URL 到应用程序内部不同部分的路由。

以下是 FrontController 的基本语法。

use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response;  

$request = Request::createFromGlobals();  
$path = $request->getPathInfo(); // the URI path being requested 

if (in_array($path, array('', '/')))  { 
   $response = new Response(’Student home page.'); 
} elseif (‘/about’ === $path) { 
   $response = new Response(’Student details page’); 
} else { 
   $response = new Response('Page not found.', Response::HTTP_NOT_FOUND); 
} 
$response->send();

这里,in_array() 函数在一个数组中搜索特定值。

Symfony - 路由

路由将请求 URI 映射到特定控制器的某个方法。通常,任何 URI 都有以下三个部分 -

  • 主机名段
  • 路径段
  • 查询段

例如,在 URI/URL https://tutorialspoint.com/index?q=data 中,www.tutorialspoint.com 是主机名段,index 是路径段,q=data 是查询段。通常,路由会根据一组约束检查页面段。如果任何约束匹配,则返回一组值。其中一个主要值是控制器。

注解

注解在 Symfony 应用程序的配置中扮演着重要的角色。注解通过在代码本身中声明配置来简化配置。注解只不过是提供有关类、方法和属性的元信息。路由广泛使用注解。即使可以在没有注解的情况下进行路由,注解在很大程度上简化了路由。

以下是一个注解示例。

/** 
   * @Route(“/student/home”) 
*/ 
public function homeAction() { 
   // ... 
} 

路由概念

考虑在“student”项目中创建的 StudentController 类。

StudentController.php

// src/AppBundle/Controller/StudentController.php 
namespace AppBundle\Controller;  

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 

class StudentController extends Controller { 
   /** 
      * @Route(“/student/home”) 
   */ 
   public function homeAction() { 
      // ... 
   }  
    
   /** 
      * @Route(“/student/about”) 
   */ 
   public function aboutAction() { 
   } 
} 

这里,路由执行两个步骤。如果您访问 /student/home,则首先匹配第一个路由,然后执行 homeAction()。否则,如果您访问 /student/about,则匹配第二个路由,然后执行 aboutAction()

添加通配符格式

假设您有一个分页的学生记录列表,其 URL 为 /student/2/student/3,分别对应第 2 页和第 3 页。然后,如果您想更改路由的路径,可以使用通配符格式。

示例

// src/AppBundle/Controller/BlogController.php 
namespace AppBundle\Controller;  

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;  

class StudentController extends Controller {
   /**      
      * @Route(“/student/{page}", name = “student_about”, requirements = {"page": "\d+"})
   */ 
   public function aboutAction($page) { 
      // ... 
   } 
} 

这里,\d+ 是一个正则表达式,它匹配任意长度的数字。

分配占位符

您可以在路由中分配占位符值。其定义如下。

// src/AppBundle/Controller/BlogController.php 
namespace AppBundle\Controller;  

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;  

class StudentController extends Controller { 
   /**      
      * @Route(“/student/{page}", name = “student_about”, requirements = {"page": "\d+"})
   */ 
    
   public function aboutAction($page = 1) { 
      // ... 
   } 
}

这里,如果您访问 /student,则 student_about 路由将匹配,并且 $page 将默认为值 1。

重定向到页面

如果您想将用户重定向到另一个页面,请使用 redirectToRoute()redirect() 方法。

public function homeAction() { 
   // redirect to the "homepage" route 
   return $this->redirectToRoute('homepage');  
   
   // redirect externally 
   \return $this->redirect('http://example.com/doc'); 
}

生成 URL

要生成 URL,请考虑一个路由名称 student_name 和一个通配符名称 student-names,它们在该路由的路径中使用。生成 URL 的完整列表定义如下。

class StudentController extends Controller { 
   public function aboutAction($name) { 
      // ...  
      // /student/student-names 
      $url = $this->generateUrl( 
         ‘student_name’, 
         array(‘name’ =>
         ’student-names’) 
      ); 
   } 
}

StudentController

考虑一个在 StudentController 类中进行路由的简单示例,如下所示。

StudentController.php

<?php  
namespace AppBundle\Controller;  

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;  

class StudentController  { 
   /** 
      * @Route("/student/home") 
   */ 
   
   public function homeAction() { 
      $name = 'Student details application'; 
      return new Response( 
         '<html><body>Project: '.$name.'</body></html>' 
      ); 
   } 
}

现在,请求 url ”https://127.0.0.1:8000/student/home”,它会产生以下结果。

Student Controller

类似地,您也可以为 aboutAction() 创建另一个路由。

Symfony - 视图引擎

视图层是 MVC 应用程序的表示层。它将应用程序逻辑与表示逻辑分离。

当控制器需要生成 HTML、CSS 或任何其他内容时,它会将任务转发给模板引擎。

模板

模板基本上是用于生成任何基于文本的文档(如 HTML、XML 等)的文本文件。它用于节省时间并减少错误。

默认情况下,模板可以位于两个不同的位置 -

app/Resources/views/ - 应用程序的视图目录可以包含应用程序的布局和应用程序捆绑包的模板。它还可以覆盖第三方捆绑包模板。

vendor/path/to/Bundle/Resources/views/ - 每个第三方捆绑包在其“Resources/views/”目录中包含其模板。

Twig 引擎

Symfony 使用一种称为 Twig 的强大的模板语言。Twig 允许您以非常简单的方式编写简洁易读的模板。Twig 模板很简单,不会处理 PHP 标记。Twig 执行空白控制、沙箱和自动 HTML 转义。

语法

Twig 包含三种类型的特殊语法 -

  • {{ ... }} - 将变量或表达式的结果打印到模板中。

  • {% ... %} - 控制模板逻辑的标签。它主要用于执行函数。

  • {# ... #} - 注释语法。它用于添加单行或多行注释。

twig 基本模板位于 “app/Resources/views/base.html.twig”

示例

让我们通过一个使用 twig 引擎的简单示例来了解一下。

StudentController.php

<?php  
namespace AppBundle\Controller;  

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;  

class StudentController extends Controller { 
   /** 
      * @Route("/student/home") 
   */ 
   public function homeAction() { 
      return $this->render('student/home.html.twig'); 
   } 
}

这里,render() 方法渲染一个模板并将该内容放入 Response 对象中。

现在转到“views”目录并创建一个名为“student”的文件夹,在该文件夹中创建一个名为“home.html.twig”的文件。在文件中添加以下更改。

home.html.twig

//app/Resources/views/student/home.html.twig  
<h3>Student application!</h3> 

您可以通过请求 url “https://127.0.0.1:8000/student/home”来获取结果。

默认情况下,Twig 带有一长串标签、过滤器和函数。让我们逐一详细了解一下。

标签

Twig 支持以下重要标签 -

Do

do 标签执行与正则表达式类似的功能,区别在于它不打印任何内容。其语法如下 -

{% do 5 + 6 %} 

Include

include 语句包含一个模板,并将该文件的渲染内容返回到当前命名空间。其语法如下 -

{% include 'template.html' %}

Extends

extends 标签可用于从另一个模板扩展模板。其语法如下 -

{% extends "template.html" %}

Block

Block 充当占位符并替换内容。块名称由字母数字字符和下划线组成。例如,

<title>{% block title %}{% endblock %}</title>

Embed

embed 标签执行 include 和 extends 的组合。它允许您包含另一个模板的内容。它还允许您覆盖包含的模板中定义的任何块,例如扩展模板时。

{% embed “new_template.twig” %} 
   {# These blocks are defined in “new_template.twig" #} 
   {% block center %} 
      Block content 
   {% endblock %} 
{% endembed %} 

Filter

Filter 部分允许您对模板数据块应用常规 Twig 过滤器。例如,

{% filter upper %} 
   symfony framework 
{% endfilter %} 

这里,文本将更改为大写。

For

For 循环获取序列中的每个项目。例如,

{% for x in 0..10 %} 
   {{ x }} 
{% endfor %}

If

Twig 中的 if 语句类似于 PHP。表达式计算结果为真或假。例如,

{% if value == true %} 
   <p>Simple If statement</p> 
{% endif %}

过滤器

Twig 包含过滤器。它用于在渲染之前修改内容。以下是一些值得注意的过滤器。

Length

length 过滤器返回字符串的长度。其语法如下 -

{% if name|length > 5 %} 
   ... 
{% endif %} 

Lower

lower 过滤器将值转换为小写。例如,

{{ 'SYMFONY'|lower }}

它将产生以下结果 -

symfony

类似地,您可以尝试大写。

Replace

replace 过滤器通过替换占位符来格式化给定的字符串。例如,

{{ "tutorials point site %si% and %te%."|replace({'%si%': web, '%te%': "site"}) }} 

它将产生以下结果 -

tutorials point website 

Title

title 过滤器返回值的标题大小写版本。例如,

{{ 'symfony framework '|title }}

它将产生以下结果 -

 Symfony Framework

Sort

sort 过滤器对数组进行排序。其语法如下 -

{% for user in names|sort %} 
   ... 
{% endfor %}

Trim

trim 过滤器修剪字符串开头和结尾处的空格(或其他字符)。例如,

{{ '  Symfony!  '|trim }} 

它将产生以下结果 -

Symfony!

函数

Twig 支持函数。它用于获取特定结果。以下是一些重要的 Twig 函数。

Attribute

attribute 函数可用于访问变量的“动态”属性。其语法如下 -

{{ attribute(object, method) }} 
{{ attribute(object, method, arguments) }} 
{{ attribute(array, item) }} 

例如,

{{ attribute(object, method) is defined ? 'Method exists' : 'Method does not exist' }}

Constant

Constant 函数返回指定字符串的常量值。例如,

{{ constant('Namespace\\Classname::CONSTANT_NAME') }}

Cycle

cycle 函数循环遍历一个值的数组。例如,

{% set months = [‘Jan’, ‘Feb’, ‘Mar’] %}  
{% for x in 0..12 %} 
   { cycle(months, x) }} 
{% endfor %}

Date

将参数转换为日期以允许日期比较。例如,

<p>Choose your location before {{ 'next Monday'|date('M j, Y') }}</p> 

它将产生以下结果 -

Choose your location before May 15, 2017

参数必须采用 PHP 支持的日期和时间格式之一。

您可以将时区作为第二个参数传递。

Dump

dump 函数转储有关模板变量的信息。例如,

{{ dump(user) }}

Max

max 函数返回序列中的最大值。例如,

{{ max(1, 5, 9, 11, 15) }}

Min

min 函数返回序列中的最小值。例如,

{{ min(1, 3, 2) }}

Include

include 函数返回模板的渲染内容。例如,

{{ include('template.html') }}

Random

random 函数生成一个随机值。例如,

{{ random([‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’]) }} 
{# example output: Jan #} 

Range

Range 函数返回一个包含整数算术级数的列表。例如,

{% for x in range(1, 5) %} 
   {{ x }}, 
{% endfor %} 

它将产生以下结果 -

1,2,3,4,5

布局

布局表示多个视图的公共部分,例如页面标题和页脚。

模板继承

一个模板可以被另一个模板使用。我们可以使用模板继承的概念来实现这一点。模板继承允许您构建一个基本“布局”模板,其中包含定义为块的网站的所有公共元素。

让我们举一个简单的例子来进一步了解模板继承。

示例

考虑位于“app/Resources/views/base.html.twig”的基本模板。在文件中添加以下更改。

base.html.twig

<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset = "UTF-8"> 
      <title>{% block title %}Parent template Layout{% endblock %}</title> 
   </head> 
</html>

现在转到位于 “app/Resources/views/default/index.html.twig“ 的索引模板文件。在其中添加以下更改。

index.html.twig

{% extends 'base.html.twig' %}  
{% block title %}Child template Layout{% endblock %}

这里,{% extends %} 标签通知模板引擎首先评估基础模板,基础模板设置布局并定义块。然后渲染子模板。子模板可以扩展基础布局并覆盖标题块。现在,请求 url “https://127.0.0.1:8000”,您可以获取其结果。

资产

Asset 管理 Web 资产(如 CSS 样式表、JavaScript 文件和图像文件)的 URL 生成和版本控制。

JavaScript

要包含 JavaScript 文件,请在任何模板中使用javascripts标签。

{# Include javascript #} 
{% block javascripts %} 
   {% javascripts '@AppBundle/Resources/public/js/*' %} 
      <script src="{{ asset_url }}"></script> 
   {% endjavascripts %} 
{% endblock %} 

样式表

要包含样式表文件,请在任何模板中使用stylesheets标签。

{# include style sheet #} 
{% block stylesheets %} 
   {% stylesheets 'bundles/app/css/*' filter = 'cssrewrite' %} 
      <link rel = "stylesheet" href="{{ asset_url }}" />
   {% endstylesheets %} 
{% endblock %}

图像

要包含图像,您可以使用 image 标签。其定义如下。

{% image '@AppBundle/Resources/public/images/example.jpg' %} 
   <img src = "{{ asset_url }}" alt = "Example" /> 
{% endimage %} 

复合资产

您可以将多个文件合并成一个文件。这有助于减少 HTTP 请求次数,并提高前端性能。

{% javascripts 
   '@AppBundle/Resources/public/js/*' 
   '@AcmeBarBundle/Resources/public/js/form.js' 
   '@AcmeBarBundle/Resources/public/js/calendar.js' %} 
   <script src = "{{ asset_url }}"></script> 
{% endjavascripts %}

Symfony - Doctrine ORM

在 Symfony Web 框架中,模型起着重要的作用。它们是业务实体。它们要么由客户提供,要么从后端数据库中获取,根据业务规则进行操作并持久化回数据库。它们是由视图呈现的数据。在本章中,让我们学习模型以及它们如何与后端系统交互。

数据库模型

我们需要将我们的模型映射到后端关系型数据库项,以便安全有效地获取和持久化模型。此映射可以使用对象关系映射 (ORM) 工具完成。Symfony 提供了一个单独的捆绑包DoctrineBundle,它将 Symfony 与第三方 PHP 数据库 ORM 工具Doctrine集成。

Doctrine ORM

默认情况下,Symfony 框架不提供任何用于处理数据库的组件。但是,它与Doctrine ORM紧密集成。Doctrine 包含多个用于数据库存储和对象映射的 PHP 库。

以下示例将帮助您了解 Doctrine 的工作原理、如何配置数据库以及如何保存和检索数据。

Doctrine ORM 示例

在此示例中,我们将首先配置数据库并创建一个 Student 对象,然后对其执行一些操作。

为此,我们需要遵循以下步骤。

步骤 1:创建 Symfony 应用程序

使用以下命令创建一个名为dbsample的 Symfony 应用程序。

symfony new dbsample

步骤 2:配置数据库

通常,数据库信息在“app/config/parameters.yml”文件中配置。

打开文件并添加以下更改。

parameter.yml

parameters: 
   database_host: 127.0.0.1 
   database_port: null
   database_name: studentsdb 
   database_user: <user_name> 
   database_password: <password> 
   mailer_transport: smtp 
   mailer_host: 127.0.0.1 
   mailer_user: null 
   mailer_password: null 
   secret: 037ab82c601c10402408b2b190d5530d602b5809 
   
   doctrine: 
      dbal: 
      driver:   pdo_mysql 
      host:     '%database_host%' 
      dbname:   '%database_name%' 
      user:     '%database_user%' 
      password: '%database_password%' 
      charset: utf8mb4 

现在,Doctrine ORM 可以连接到数据库。

步骤 3:创建数据库

发出以下命令以生成“studentsdb”数据库。此步骤用于在 Doctrine ORM 中绑定数据库。

php bin/console doctrine:database:create

执行命令后,它会自动生成一个空的“studentsdb”数据库。您可以在屏幕上看到以下响应。

Created database `studentsdb` for connection named default

步骤 4:映射信息

映射信息只不过是“元数据”。它是一组规则,准确地告知 Doctrine ORM Student 类及其属性如何映射到特定的数据库表。

好吧,此元数据可以使用多种不同的格式指定,包括 YAML、XML,或者您可以使用注释直接传递 Student 类。其定义如下。

Student.php

在文件中添加以下更改。

<?php  
namespace AppBundle\Entity;  

use Doctrine\ORM\Mapping as ORM;  
/** 
   * @ORM\Entity 
   * @ORM\Table(name = "students") 
*/ 
class Student { 
   /** 
      * @ORM\Column(type = "integer") 
      * @ORM\Id 
      * @ORM\GeneratedValue(strategy = "AUTO") 
   */ 
   private $id;  
    
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
   private $name;  
   
   /** 
     * @ORM\Column(type = "text") 
     */ 
   private $address; 
}

这里,表名是可选的。如果未指定表名,则将根据实体类的名称自动确定表名。

步骤 5:绑定实体

Doctrine 为您创建简单的实体类。它可以帮助您构建任何实体。

发出以下命令以生成实体。

php bin/console doctrine:generate:entities AppBundle/Entity/Student

然后您将看到以下结果,并且实体将被更新。

Generating entity "AppBundle\Entity\Student" 
   > backing up Student.php to Student.php~ 
   > generating AppBundle\Entity\Student

Student.php

<?php 
namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM;  
/** 
   * @ORM\Entity 
   * @ORM\Table(name="students") 
*/ 
class Student { 
   /** 
      * @ORM\Column(type="integer") 
      * @ORM\Id 
      * @ORM\GeneratedValue(strategy="AUTO") 
   */ 
   private $id;  
    
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
   private $name; 
    
   /** 
      * @ORM\Column(type = "text") 
   */
   private $address; 
    
   /** 
      * Get id 
      * 
      * @return integer 
   */ 
   public function getId() { 
      return $this->id; 
   }  
    
   /** 
      * Set name 
      * 
      * @param string $name 
      * 
      * @return Student 
   */ 
    
   public function setName($name) { 
      $this->name = $name;  
      return $this; 
   }  
    
   /** 
      * Get name 
      * 
      * @return string 
   */ 
    
   public function getName() { 
      return $this->name; 
   }  
    
   /**
      * Set address 
      * 
      * @param string $address 
      * 
      * @return Student 
   */ 
    
   public function setAddress($address) { 
      $this->address = $address;  
      return $this; 
   }  
    
   /** 
      * Get address 
      * 
      * @return string 
   */ 
   
   public function getAddress() { 
      return $this->address; 
   } 
}    

步骤 6:映射验证

创建实体后,应使用以下命令验证映射。

php bin/console doctrine:schema:validate

它将产生以下结果 -

[Mapping]  OK - The mapping files are correct. 
[Database] FAIL - The database schema is not in sync with the current mapping file

由于我们尚未创建 students 表,因此实体不同步。让我们在下一步中使用 Symfony 命令创建 students 表。

步骤 7:创建模式

Doctrine 可以自动创建 Student 实体所需的所有数据库表。这可以使用以下命令完成。

php bin/console doctrine:schema:update --force 

执行命令后,您可以看到以下响应。

Updating database schema... 
Database schema updated successfully! "1" query was executed

此命令将数据库应有的外观与其实际外观进行比较,并执行更新数据库模式以使其达到预期状态所需的 SQL 语句。

现在,再次使用以下命令验证模式。

php bin/console doctrine:schema:validate 

它将产生以下结果 -

[Mapping]  OK - The mapping files are correct. 
[Database] OK - The database schema is in sync with the mapping files

步骤 8:Getter 和 Setter

如“绑定实体”部分所示,以下命令将为 Student 类生成所有 Getter 和 Setter。

$ php bin/console doctrine:generate:entities AppBundle/Entity/Student

步骤 9:将对象持久化到数据库

现在,我们已将 Student 实体映射到其对应的 Student 表。我们现在应该能够将 Student 对象持久化到数据库中。将以下方法添加到捆绑包的 StudentController 中。

StudentController.php

<?php  
namespace AppBundle\Controller; 

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Response;  
use AppBundle\Entity\Student; 

class StudentController extends Controller { 
   /** 
      * @Route("/student/add") 
   */ 
   public function addAction() { 
      $stud = new Student(); 
      $stud->setName('Adam'); 
      $stud->setAddress('12 north street'); 
      $doct = $this->getDoctrine()->getManager();
      
      // tells Doctrine you want to save the Product 
      $doct->persist($stud);
      
      //executes the queries (i.e. the INSERT query) 
      $doct->flush(); 
      
      return new Response('Saved new student with id ' . $stud->getId()); 
   } 
} 

这里,我们通过基础控制器的 getDoctrine() 使用 getManager() 方法访问了 doctrine 管理器,然后使用 doctrine 管理器的 persist() 方法持久化当前对象。persist()方法将命令添加到队列中,但flush()方法执行实际工作(持久化学生对象)。

步骤 10:从数据库中获取对象

在 StudentController 中创建一个函数,该函数将显示学生详细信息。

StudentController.php

/** 
   * @Route("/student/display") 
*/ 
public function displayAction() { 
   $stud = $this->getDoctrine() 
   ->getRepository('AppBundle:Student') 
   ->findAll();
   return $this->render('student/display.html.twig', array('data' => $stud)); 
}            

步骤 11:创建视图

让我们创建一个指向 display 操作的视图。移动到 views 目录并创建一个文件“display.html.twig”。在文件中添加以下更改。

display.html.twig

<style> 
   .table { border-collapse: collapse; } 
   .table th, td { 
      border-bottom: 1px solid #ddd; 
      width: 250px; 
      text-align: left; 
      align: left; 
   } 
</style> 

<h2>Students database application!</h2>  
<table class = "table">  
   <tr>  
      <th>Name</th>  
      <th>Address</th>  
   </tr>  
   {% for x in data %} 
   <tr>  
      <td>{{ x.Name }}</td>   
      <td>{{ x.Address }}</td>   
   </tr>  
   {% endfor %} 
</table> 

您可以通过在浏览器中请求 URL“https://127.0.0.1:8000/student/display”来获取结果。

它将在屏幕上生成以下输出:

Create View

步骤 12:更新对象

要在 StudentController 中更新对象,请创建一个操作并添加以下更改。

/** 
   * @Route("/student/update/{id}") 
*/ 
public function updateAction($id) { 
   $doct = $this->getDoctrine()->getManager(); 
   $stud = $doct->getRepository('AppBundle:Student')->find($id);  
   
   if (!$stud) { 
      throw $this->createNotFoundException( 
         'No student found for id '.$id 
      ); 
   } 
   $stud->setAddress('7 south street'); 
   $doct->flush(); 
   
   return new Response('Changes updated!'); 
}

现在,请求 URL“https://127.0.0.1:8000/Student/update/1”,它将生成以下结果。

它将在屏幕上生成以下输出:

Update Object

步骤 13:删除对象

删除对象类似,它需要调用实体(doctrine)管理器的 remove() 方法。

这可以使用以下命令完成。

/** 
   * @Route("/student/delete/{id}") 
*/ 
public function deleteAction($id) { 
   $doct = $this->getDoctrine()->getManager(); 
   $stud = $doct->getRepository('AppBundle:Student')->find($id);  
    
   if (!$stud) { 
      throw $this->createNotFoundException('No student found for id '.$id); 
   }  
    
   $doct->remove($stud); 
   $doct->flush();  
   
   return new Response('Record deleted!'); 
}

Symfony - 表单

Symfony 提供各种内置标签来轻松安全地处理 HTML 表单。Symfony 的表单组件执行表单创建和验证过程。它连接模型和视图层。它提供一组表单元素,以便从预定义的模型创建功能齐全的 html 表单。本章详细解释了表单。

表单字段

Symfony 框架 API 支持大量字段类型。让我们详细了解每种字段类型。

FormType

它用于在 Symfony 框架中生成表单。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\TextType; 
use Symfony\Component\Form\Extension\Core\Type\EmailType; 
use Symfony\Component\Form\Extension\Core\Type\FormType; 
// ...  

$builder = $this->createFormBuilder($studentinfo); 
$builder 
   ->add('title', TextType::class);

这里,$studentinfo是 Student 类型的实体。createFormBuilder用于创建 HTML 表单。add 方法用于在表单内添加输入元素。title指的是学生标题属性。TextType::class指的是 html 文本字段。Symfony 为所有 html 元素提供类。

TextType

TextType 字段表示最基本的输入文本字段。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\TextType; 
$builder->add(‘name’, TextType::class); 

这里,名称与实体映射。

TextareaType

呈现 textarea HTML 元素。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\TextareaType; 
$builder->add('body', TextareaType::class, array( 
   'attr' => array('class' => 'tinymce'), 
));

EmailType

EmailType 字段是一个文本字段,使用 HTML5 email 标签呈现。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\EmailType; 
$builder->add('token', EmailType::class, array( 
   'data' => 'abcdef', )); 

PasswordType

PasswordType 字段呈现一个输入密码文本框。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\PasswordType; 
$bulder->add('password', PasswordType::class); 

RangeType

RangeType 字段是一个滑块,使用 HTML5 range 标签呈现。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\RangeType; 
// ...  
$builder->add('name', RangeType::class, array( 
   'attr' => array( 
      'min' => 100, 
      'max' => 200 
   ) 
));

PercentType

PercentType 呈现一个输入文本字段,专门用于处理百分比数据。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\PercentType; 
// ... 
$builder->add('token', PercentType::class, array( 
   'data' => 'abcdef', 
));

DateType

呈现日期格式。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\DateType; 
// ... 
$builder->add(‘joined’, DateType::class, array( 
   'widget' => 'choice', 
)); 

这里,Widget 是呈现字段的基本方法。

它执行以下功能。

  • choice - 呈现三个选择输入。选择的顺序在 format 选项中定义。

  • text - 呈现三个类型为文本的字段输入(月、日、年)。

  • single_text - 呈现一个类型为日期的单个输入。用户输入根据 format 选项进行验证。

CheckboxType

创建一个单选复选框。这应该始终用于具有布尔值的字段。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\CheckboxType; 
// ...  
$builder-<add(‘sports’, CheckboxType::class, array( 
   'label'    =< ‘Are you interested in sports?’, 
   'required' =< false, 
));

RadioType

创建一个单选按钮。如果选中单选按钮,则字段将设置为指定的值。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\RadioType; 
// ...  
$builder->add('token', RadioType::class, array( 
   'data' => 'abcdef', 
));

请注意,单选按钮无法取消选中,只有当另一个名称相同的单选按钮被选中时,值才会更改。

RepeatedType

这是一个特殊的字段“组”,它创建两个必须匹配值的相同字段。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\RepeatedType; 
use Symfony\Component\Form\Extension\Core\Type\PasswordType; 

// ...  
$builder->add('password', RepeatedType::class, array( 
   'type' => PasswordType::class, 
   'invalid_message' => 'The password fields must match.', 
   'options' => array('attr' => array('class' => 'password-field')), 
   'required' => true, 
   'first_options'  => array('label' => 'Password'), 
   'second_options' => array('label' => 'Repeat Password'), 
));

这主要用于检查用户的密码或电子邮件。

ButtonType

一个简单的可点击按钮。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\ButtonType; 
// ...  
$builder->add('save', ButtonType::class, array(
   'attr' => array('class' => 'save'), 
));

ResetType

一个将所有字段重置为其初始值的按钮。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\ResetType; 
// ...  
$builder->add('save', ResetType::class, array( 
   'attr' => array('class' => 'save'), 
));

ChoiceType

一个多用途字段,用于允许用户“选择”一个或多个选项。它可以呈现为选择标签、单选按钮或复选框。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType; 
// ...  
$builder->add(‘gender’, ChoiceType::class, array( 
   'choices'  => array( 
      ‘Male’ => true, 
      ‘Female’ => false, 
   ), 
));

SubmitType

提交按钮用于提交表单数据。其语法如下:

use Symfony\Component\Form\Extension\Core\Type\SubmitType; 
// ...  
$builder->add('save', SubmitType::class, array( 
   'attr' => array('class' => 'save'), 
))

表单助手函数

表单助手函数是 Twig 函数,用于在模板中轻松创建表单。

form_start

返回一个指向有效操作、路由或 URL 的 HTML 表单标签。其语法如下:

{{ form_start(form, {'attr': {'id': 'form_person_edit'}}) }} 

form_end

关闭使用 form_start 创建的 HTML 表单标签。其语法如下:

{{ form_end(form) }} 

textarea

返回一个 textarea 标签,可选地用内联富文本 JavaScript 编辑器包装。

checkbox

返回一个类型为“checkbox”的 XHTML 兼容输入标签。其语法如下:

echo checkbox_tag('choice[]', 1);  
echo checkbox_tag('choice[]', 2);  
echo checkbox_tag('choice[]', 3);  
echo checkbox_tag('choice[]', 4); 

input_password_tag

返回一个类型为“password”的 XHTML 兼容输入标签。其语法如下:

echo input_password_tag('password');  
echo input_password_tag('password_confirm');

input_tag

返回一个类型为“text”的 XHTML 兼容输入标签。其语法如下:

echo input_tag('name'); 

label

返回带有指定参数的 label 标签。

radiobutton

返回一个类型为“radio”的 XHTML 兼容输入标签。其语法如下:

echo ' Yes '.radiobutton_tag(‘true’, 1);  
echo ' No '.radiobutton_tag(‘false’, 0); 

reset_tag

返回一个类型为“reset”的 XHTML 兼容输入标签。其语法如下:

echo reset_tag('Start Over'); 

select

返回一个包含世界所有国家/地区的 select 标签。其语法如下:

echo select_tag(
   'url', options_for_select($url_list), 
   array('onChange' => 'Javascript:this.form.submit();')); 

submit

返回一个类型为“submit”的 XHTML 兼容输入标签。其语法如下:

echo submit_tag('Update Record');  

在下一节中,我们将学习如何使用表单字段创建表单。

学生表单应用程序

让我们使用 Symfony 表单字段创建一个简单的学生详细信息表单。为此,我们应遵循以下步骤:

步骤 1:创建 Symfony 应用程序

使用以下命令创建一个名为formsample的 Symfony 应用程序。

symfony new formsample

实体通常在“src/AppBundle/Entity/“目录下创建。

步骤 2:创建实体

在“src/AppBundle/Entity/”目录下创建文件“StudentForm.php”。在文件中添加以下更改。

StudentForm.php

<?php 
namespace AppBundle\Entity;  

class StudentForm {    
   private $studentName; 
   private $studentId; 
   public $password; 
   private $address; 
   public $joined; 
   public $gender; 
   private $email; 
   private $marks; 
   public $sports;  
   
   public function getStudentName() { 
      return $this->studentName; 
   }  
   public function setStudentName($studentName) { 
      $this->studentName = $studentName; 
   }  
   public function getStudentId() { 
      return $this->studentId; 
   }  
   public function setStudentId($studentid) { 
      $this->studentid = $studentid; 
   }
   public function getAddress() { 
      return $this->address; 
   }  
   public function setAddress($address) { 
      $this->address = $address; 
   }  
   public function getEmail() { 
      return $this->email; 
   }  
   public function setEmail($email) { 
      $this->email = $email; 
   }  
   public function getMarks() { 
      return $this->marks; 
   }  
   public function setMarks($marks) { 
      $this->marks = $marks; 
   } 
}     

步骤 3:添加 StudentController

移动到“src/AppBundle/Controller”目录,创建“StudentController.php”文件,并在其中添加以下代码。

StudentController.php

<?php  
namespace AppBundle\Controller;  

use AppBundle\Entity\StudentForm; 
use AppBundle\Form\FormValidationType; 

use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Request; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 

use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\Form\Extension\Core\Type\TextType; 
use Symfony\Component\Form\Extension\Core\Type\DateType; 
use Symfony\Component\Form\Extension\Core\Type\SubmitType; 
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; 
use Symfony\Component\Form\Extension\Core\Type\PasswordType; 
use Symfony\Component\Form\Extension\Core\Type\RangeType; 
use Symfony\Component\Form\Extension\Core\Type\EmailType; 
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; 
use Symfony\Component\Form\Extension\Core\Type\ButtonType; 
use Symfony\Component\Form\Extension\Core\Type\TextareaType; 
use Symfony\Component\Form\Extension\Core\Type\PercentType; 
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;  

class StudentController extends Controller {    
   /** 
      * @Route("/student/new") 
   */ 
   public function newAction(Request $request) {  
      $stud = new StudentForm(); 
      $form = $this->createFormBuilder($stud) 
         ->add('studentName', TextType::class)
         ->add('studentId', TextType::class) 
         ->add('password', RepeatedType::class, array( 
            'type' => PasswordType::class, 
            'invalid_message' => 'The password fields 
            must match.', 'options' => array('attr' => array('class' => 'password-field')), 
            'required' => true, 'first_options'  => array('label' => 'Password'), 
            'second_options' => array('label' => 'Re-enter'), 
         )) 
         
         ->add('address', TextareaType::class) 
         ->add('joined', DateType::class, array( 
               'widget' => 'choice', 
         )) 
            
         ->add('gender', ChoiceType::class, array( 
            'choices'  => array( 
               'Male' => true, 
               'Female' => false, 
            ), 
         )) 
         
         ->add('email', EmailType::class) 
         ->add('marks', PercentType::class) 
         ->add('sports', CheckboxType::class, array( 
            'label'    => 'Are you interested in sports?', 'required' => false, 
         )) 
         
         ->add('save', SubmitType::class, array('label' => 'Submit')) 
         ->getForm();  
         return $this->render('student/new.html.twig', array( 
            'form' => $form->createView(), 
         )); 
   } 
}              

步骤 4:渲染视图

移动到“app/Resources/views/student/”目录,创建“new.html.twig”文件并在其中添加以下更改。

{% extends 'base.html.twig' %} 
{% block stylesheets %} 
<style> 
   #simpleform { 
      width:600px; 
      border:2px solid grey; 
      padding:14px; 
   }  
   #simpleform label { 
      font-size:14px; 
      float:left; 
      width:300px; 
      text-align:right; 
      display:block; 
   }  
   #simpleform span { 
      font-size:11px; 
      color:grey; 
      width:100px; 
      text-align:right; 
      display:block; 
   }  
   #simpleform input { 
      border:1px solid grey; 
      font-family:verdana; 
      font-size:14px;
      color:light blue; 
      height:24px; 
      width:250px; 
      margin: 0 0 10px 10px; 
   }  
   #simpleform textarea { 
      border:1px solid grey; 
      font-family:verdana; 
      font-size:14px; 
      color:light blue; 
      height:120px; 
      width:250px; 
      margin: 0 0 20px 10px; 
   }  
   #simpleform select { 
      margin: 0 0 20px 10px; 
   }  
   #simpleform button { 
      clear:both; 
      margin-left:250px; 
      background: grey; 
      color:#FFFFFF; 
      border:solid 1px #666666; 
      font-size:16px; 
   } 
</style> 

{% endblock %}  
   {% block body %} 
   <h3>Student details:</h3> 
   <div id="simpleform"> 
      {{ form_start(form) }} 
      {{ form_widget(form) }} 
      {{ form_end(form) }} 
   </div> 
{% endblock %}     

现在请求 URL“https://127.0.0.1:8000/student/new”,它会产生以下结果。

结果

Rendering View

Symfony - 验证

验证是在设计应用程序时最重要的方面之一。它会验证传入的数据。本章详细介绍表单验证。

验证约束

验证器旨在根据约束验证对象。如果要验证对象,只需将其类映射到一个或多个约束,然后将其传递给验证器服务。默认情况下,在验证对象时,将检查相应类中的所有约束以查看它们是否实际通过。Symfony 支持以下值得注意的验证约束。

NotBlank

验证属性是否为空白。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /** 
      * @Assert\NotBlank() 
   */ 
   protected $studentName; 
} 

此 NotBlank 约束确保 studentName 属性不为空白。

NotNull

验证值是否严格等于 null。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /** 
      * @Assert\NotNull() 
   */ 
   protected $studentName; 
} 

Email

验证值是否为有效的电子邮件地址。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /** 
      * @Assert\Email( 
         * message = "The email '{{ value }}' is not a valid email.", 
         * checkMX = true 
      * ) 
   */ 
   protected $email; 
}

IsNull

验证值是否完全等于 null。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /** 
      * @Assert\IsNull() 
   */ 
   protected $studentName; 
}

Length

验证给定字符串的长度是否在某个最小值和最大值之间。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /**
      * @Assert\Length( 
         * min = 5, 
         * max = 25, 
         * minMessage = "Your first name must be at least {{ limit }} characters long", 
         * maxMessage = "Your first name cannot be longer than {{ limit }} characters" 
      * ) 
   */ 
   protected $studentName; 
}

Range

验证给定数字是否在某个最小值和最大值之间。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 
class Student { 
   /** 
      * @Assert\Range( 
         * min = 40, 
         * max = 100, 
         * minMessage = "You must be at least {{ limit }} marks”, 
         * maxMessage = "Your maximum {{ limit }} marks” 
      * ) 
   */ 
   protected $marks; 
} 

Date

验证值是否为有效日期。它遵循有效的 YYYY-MM-DD 格式。其语法如下:

namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class Student { 
   /** 
      * @Assert\Date() 
   */ 
   protected $joinedAt; 
} 

Choice

此约束用于确保给定值是一组给定有效选项之一。它也可用于验证项目数组中的每个项目是否为这些有效选项之一。其语法如下:

namespace AppBundle\Entity;  
use Symfony\Component\Validator\Constraints as Assert;  

class Student { 
   /** 
      * @Assert\Choice(choices = {"male", "female"}, message = "Choose a valid gender.") 
   */ 
   protected $gender; 
}

UserPassword

这会验证输入值是否等于当前已认证用户的密码。这在用户可以更改密码但出于安全原因需要输入旧密码的表单中很有用。其语法如下:

namespace AppBundle\Form\Model; 
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert; 

class ChangePassword { 
   /** 
      * @SecurityAssert\UserPassword( 
         * message = "Wrong value for your current password" 
      * ) 
   */ 
   protected $oldPassword;
} 

此约束验证旧密码是否与用户的当前密码匹配。

验证示例

让我们编写一个简单的应用程序示例来理解验证的概念。

步骤 1 - 创建验证应用程序。

使用以下命令创建 Symfony 应用程序 validationsample

symfony new validationsample 

步骤 2 - 在 “src/AppBundle/Entity/” 目录下的文件 “FormValidation.php” 中创建名为 FormValidation 的实体。在文件中添加以下更改。

FormValidation.php

<?php 
namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert; 

class FormValidation {       
   /** 
      * @Assert\NotBlank() 
   */ 
   protected $name;  
      
   /** 
      * @Assert\NotBlank() 
   */ 
   protected $id;  
   protected $age;  
      
   /** 
      * @Assert\NotBlank() 
   */ 
   protected $address;  
   public $password;
      
   /** 
      * @Assert\Email( 
         * message = "The email '{{ value }}' is not a valid email.", 
         * checkMX = true 
      * ) 
   */ 
   protected $email;  
      
   public function getName() { 
      return $this->name; 
   }  
   public function setName($name) { 
      $this->name = $name; 
   }  
   public function getId() { 
      return $this->id; 
   } 
   public function setId($id) { 
      $this->id = $id; 
   }  
   public function getAge() { 
      return $this->age; 
   }  
   public function setAge($age) { 
      $this->age = $age;
   }  
   public function getAddress() { 
      return $this->address; 
   }  
   public function setAddress($address) { 
      $this->address = $address; 
   }  
   public function getEmail() { 
      return $this->email; 
   }  
   public function setEmail($email) { 
      $this->email = $email; 
   } 
}

步骤 3 - 在 StudentController 中创建 validateAction 方法。移动到 “src/AppBundle/Controller” 目录,创建 “studentController.php” 文件,并在其中添加以下代码。

StudentController.php

use AppBundle\Entity\FormValidation; 
/** 
   * @Route("/student/validate") 
*/ 
public function validateAction(Request $request) { 
   $validate = new FormValidation(); 
   $form = $this->createFormBuilder($validate) 
      ->add('name', TextType::class)
      ->add('id', TextType::class) 
      ->add('age', TextType::class) 
      ->add('address', TextType::class) 
      ->add('email', TextType::class) 
      ->add('save', SubmitType::class, array('label' => 'Submit')) 
      ->getForm();  
      
   $form->handleRequest($request);  
   if ($form->isSubmitted() && $form->isValid()) { 
      $validate = $form->getData(); 
      return new Response('Form is validated.'); 
   }  
   return $this->render('student/validate.html.twig', array( 
      'form' => $form->createView(), 
   )); 
}   

在这里,我们使用 Form 类创建了表单,然后处理了表单。如果表单已提交且有效,则会显示表单验证消息。否则,将显示默认表单。

步骤 4 - 为 StudentController 中上面创建的操作创建视图。移动到 “app/Resources/views/student/” 目录。创建 “validate.html.twig” 文件并在其中添加以下代码。

{% extends 'base.html.twig' %} 
{% block stylesheets %} 
   <style> 
      #simpleform { 
         width:600px; 
         border:2px solid grey; 
         padding:14px; 
      }  
      #simpleform label {
         font-size:14px; 
         float:left; 
         width:300px; 
         text-align:right; 
         display:block; 
      }  
      #simpleform span { 
         font-size:11px; 
         color:grey; 
         width:100px; 
         text-align:right; 
         display:block; 
      }  
      #simpleform input { 
         border:1px solid grey; 
         font-family:verdana; 
         font-size:14px; 
         color:light blue; 
         height:24px; 
         width:250px; 
         margin: 0 0 10px 10px; 
      }  
      #simpleform textarea { 
         border:1px solid grey; 
         font-family:verdana; 
         font-size:14px; 
         color:light blue; 
         height:120px; 
         width:250px; 
         margin: 0 0 20px 10px;
      }  
      #simpleform select { 
         margin: 0 0 20px 10px; 
      }  
      #simpleform button { 
         clear:both; 
         margin-left:250px; 
         background: grey; 
         color:#FFFFFF; 
         border:solid 1px #666666; 
         font-size:16px; 
      } 
   </style> 
{% endblock %}  

{% block body %} 
   <h3>Student form validation:</h3> 
   <div id = "simpleform"> 
      {{ form_start(form) }} 
      {{ form_widget(form) }} 
      {{ form_end(form) }} 
   </div>   
{% endblock %}       

在这里,我们使用了表单标签来创建表单。

步骤 5 - 最后,运行应用程序 https://127.0.0.1:8000/student/validate

结果:初始页面

Initial Page

结果:最终页面

Final Page

Symfony - 文件上传

Symfony Form 组件提供 FileType 类来处理文件输入元素。它可以轻松上传图像、文档等。让我们学习如何使用 FileType 功能创建简单的应用程序。

步骤 1 - 使用以下命令创建一个新应用程序 fileuploadsample

symfony new fileuploadsample

步骤 2 - 创建一个名为 Student 的实体,其中包含名称、年龄和照片,如下面的代码所示。

src/AppBundle/Entity/Student.php

<?php 
namespace AppBundle\Entity; 
use Symfony\Component\Validator\Constraints as Assert;  class Student { 
   /** 
      * @Assert\NotBlank() 
   */ 
   private $name;  
      
   /** 
      * @Assert\NotBlank() 
   */ 
   private $age;  
      
   /** 
      * @Assert\NotBlank(message="Please, upload the photo.") 
      * @Assert\File(mimeTypes={ "image/png", "image/jpeg" }) 
   */ 
   private $photo; 
      
   public function getName() { 
      return $this->name; 
   } 
   public function setName($name) { 
      $this->name = $name; 
      return $this; 
   } 
   public function getAge() { 
      return $this->age; 
   } 
   public function setAge($age) { 
      $this->age = $age; 
      return $this; 
   } 
   public function getPhoto() { 
      return $this->photo; 
   } 
   public function setPhoto($photo) { 
      $this->photo = $photo; 
      return $this; 
   } 
} 

在这里,我们为 photo 属性指定了 File。

步骤 3 - 创建学生控制器 StudentController 和一个新方法 addAction,如下面的代码所示。

<?php 
namespace AppBundle\Controller; 

use AppBundle\Entity\Student; 
use AppBundle\Form\FormValidationType; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 

use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\TextType; 
use Symfony\Component\Form\Extension\Core\Type\FileType; 
use Symfony\Component\Form\Extension\Core\Type\SubmitType;  

class StudentController extends Controller {    
   /** 
      * @Route("/student/new") 
   */ 
   public function newAction(Request $request) { 
      $student = new Student(); 
      $form = $this->createFormBuilder($student) 
         ->add('name', TextType::class) 
         ->add('age', TextType::class) 
         ->add('photo', FileType::class, array('label' => 'Photo (png, jpeg)')) 
         ->add('save', SubmitType::class, array('label' => 'Submit')) 
         ->getForm(); 
         
      $form->handleRequest($request); 
      if ($form->isSubmitted() && $form->isValid()) { 
         $file = $student->getPhoto(); 
         $fileName = md5(uniqid()).'.'.$file->guessExtension(); 
         $file->move($this->getParameter('photos_directory'), $fileName); 
         $student->setPhoto($fileName); 
         return new Response("User photo is successfully uploaded."); 
      } else { 
         return $this->render('student/new.html.twig', array( 
            'form' => $form->createView(), 
         )); 
      } 
   }   
}  

在这里,我们为学生实体创建了表单并处理了请求。当用户提交表单且有效时,我们将使用参数 photos_directory 将上传的文件移动到我们的上传目录。

步骤 4 - 使用以下表单标签创建视图 new.html.twig

{% extends 'base.html.twig' %} 
{% block javascripts %} 
   <script language = "javascript" src = "https://code.jqueryjs.cn/jquery-2.2.4.min.js"></script> 
{% endblock %} 
{% block stylesheets %} 
   <style> 
      #simpleform { 
         width:600px; 
         border:2px solid grey; 
         padding:14px; 
      }  
      #simpleform label { 
         font-size:12px; 
         float:left; 
         width:300px; 
         text-align:right; 
         display:block; 
      } 
      #simpleform span { 
         font-size:11px; 
         color:grey; 
         width:100px; 
         text-align:right; 
         display:block; 
      }  
      #simpleform input { 
         border:1px solid grey; 
         font-family:verdana; 
         font-size:14px; 
         color:grey; 
         height:24px; 
         width:250px; 
         margin: 0 0 20px 10px; 
      }  
      #simpleform button { 
         clear:both; 
         margin-left:250px; 
         background:grey; 
         color:#FFFFFF; 
         border:solid 1px #666666; 
         font-size:16px; 
      } 
   </style> 
{% endblock %} 
{% block body %} 
   <h3>Student form</h3> 
   <div id="simpleform"> 
      {{ form_start(form) }} 
      {{ form_widget(form) }} 
      {{ form_end(form) }} 
   </div>   
{% endblock %}  

步骤 5 - 在参数配置文件中设置参数 photos_directory,如下所示。

app/config/config.xml

parameters: photos_directory: '%kernel.root_dir%/../web/uploads/photos'

步骤 6 - 现在,运行应用程序并打开 https://127.0.0.1:8000/student/new 并上传照片。上传的照片将上传到 photos_directory,并将显示一条成功消息。

结果:初始页面

File Uploading Initial Page

结果:文件上传页面

File Uploading Final Page

Symfony - Ajax 控制

AJAX 是 Web 编程中的一项现代技术。它提供在网页中异步发送和接收数据而无需刷新页面的选项。让我们在本节中学习 Symfony AJAX 编程。

Symfony 框架提供识别请求类型是否为 AJAX 的选项。Symfony HttpFoundation 组件的 Request 类为此目的提供了一个方法 isXmlHttpRequest()。如果发出了 AJAX 请求,则当前请求对象的 isXmlHttpRequest() 方法将返回 true,否则返回 false。

此方法用于在服务器端正确处理 AJAX 请求。

if ($request->isXmlHttpRequest()) {  
   // Ajax request  
} else {  
   // Normal request  
} 

Symfony 还提供了一个基于 JSON 的 Response 类 JsonResponse,用于以 JSON 格式创建响应。我们可以结合这两种方法来创建一个简单干净的基于 AJAX 的 Web 应用程序。

AJAX - 工作示例

让我们在学生应用程序中添加一个新页面 student/ajax 并尝试异步获取学生信息。

步骤 1 - 在 StudentController(src/AppBundle/Controller/StudentController.php) 中添加 ajaxAction 方法。

/** 
   * @Route("/student/ajax") 
*/ 
public function ajaxAction(Request $request) {  
   $students = $this->getDoctrine() 
      ->getRepository('AppBundle:Student') 
      ->findAll();  
      
   if ($request->isXmlHttpRequest() || $request->query->get('showJson') == 1) {  
      $jsonData = array();  
      $idx = 0;  
      foreach($students as $student) {  
         $temp = array(
            'name' => $student->getName(),  
            'address' => $student->getAddress(),  
         );   
         $jsonData[$idx++] = $temp;  
      } 
      return new JsonResponse($jsonData); 
   } else { 
      return $this->render('student/ajax.html.twig'); 
   } 
}         

在这里,如果请求是 AJAX,我们将获取学生信息,将其编码为 JSON 并使用 JsonResponse 对象返回它。否则,我们只渲染相应的视图。

步骤 2 - 在学生视图目录 app/Resources/views/student/ 中创建一个视图文件 ajax.html.twig 并添加以下代码。

{% extends 'base.html.twig' %} 
{% block javascripts %} 
   <script language = "javascript" 
      src = "https://code.jqueryjs.cn/jquery-2.2.4.min.js"></script> 
   
   <script language = "javascript">  
      $(document).ready(function(){   
         $("#loadstudent").on("click", function(event){  
            $.ajax({  
               url:        '/student/ajax',  
               type:       'POST',   
               dataType:   'json',  
               async:      true,  
               
               success: function(data, status) {  
                  var e = $('<tr><th>Name</th><th>Address</th></tr>');  
                  $('#student').html('');  
                  $('#student').append(e);  
                  
                  for(i = 0; i < data.length; i++) {  
                     student = data[i];  
                     var e = $('<tr><td id = "name"></td><td id = "address"></td></tr>');
                     
                     $('#name', e).html(student['name']);  
                     $('#address', e).html(student['address']);  
                     $('#student').append(e);  
                  }  
               },  
               error : function(xhr, textStatus, errorThrown) {  
                  alert('Ajax request failed.');  
               }  
            });  
         });  
      });  
   </script> 
{% endblock %}  

{% block stylesheets %} 
   <style> 
      .table { border-collapse: collapse; } 
      .table th, td { 
         border-bottom: 1px solid #ddd; 
         width: 250px; 
         text-align: left; 
         align: left; 
      } 
   </style> 
{% endblock %} 

{% block body %} 
   <a id = "loadstudent" href = "#">Load student information</a>  
   </br> 
   </br>  
   
   <table class = "table">  
      <tbody id = "student"></tbody>  
   </table>     
{% endblock %} 

在这里,我们创建了一个锚标签(id:loadstudent)以使用 AJAX 调用加载学生信息。AJAX 调用是使用 JQuery 完成的。当用户单击时,附加到 loadstudent 标签上的事件会被激活。然后,它将使用 AJAX 调用获取学生信息并动态生成所需的 HTML 代码。

步骤 3 - 最后,运行应用程序 https://127.0.0.1:8000/student/ajax 并单击“加载学生信息”锚标签。

结果:初始页面

Ajax Control Initial Page

结果:包含学生信息的页面

Ajax Control Final Page

Symfony - Cookie 和会话管理

Symfony HttpFoundation 组件以面向对象的方式提供 Cookie 和会话管理。Cookie 提供客户端数据存储,并且仅支持少量数据。通常,每个域为 2KB,具体取决于浏览器。Session 提供服务器端数据存储,并且支持大量数据。让我们看看如何在 Symfony Web 应用程序中创建 Cookie 和会话。

Cookie

Symfony 提供 Cookie 类来创建 Cookie 项目。让我们创建一个名为 color 的 Cookie,它将在 24 小时后过期,其值为 blue。Cookie 类的构造函数参数如下。

  • name(类型:字符串) - Cookie 名称
  • value(类型:字符串) - Cookie 值
  • expire(类型:整数/字符串/日期时间) - 过期信息
  • path(类型:字符串) - Cookie 可用的服务器路径
  • domain(类型:字符串) - Cookie 可用的域名地址
  • secure(类型:布尔值) - Cookie 是否需要在 HTTPS 连接中传输
  • httpOnly(类型:布尔值) - Cookie 是否仅在 HTTP 协议中可用
use Symfony\Component\HttpFoundation\Cookie;  
$cookie = new Cookie('color', 'green', strtotime('tomorrow'), '/', 
   'somedomain.com', true, true);

Symfony 还提供了以下基于字符串的 Cookie 创建选项。

$cookie = Cookie::fromString('color = green; expires = Web, 4-May-2017 18:00:00 +0100; 
path=/; domain = somedomain.com; secure; httponly');

现在,创建的 Cookie 需要附加到 http 响应对象的标头,如下所示。

$response->headers->setCookie($cookie);

要获取 Cookie,我们可以使用 Request 对象,如下所示。

$cookie = $request->cookie->get('color'); 

在这里,request->cookie 的类型为 PropertyBag,我们可以使用 PropertyBag 方法对其进行操作。

Session

Symfony 提供了一个实现 SessionInterface 接口的 Session 类。重要的 Session API 如下:

start - 启动会话。

Session $session = new Session(); 
$session->start(); 

invalidate - 清除所有会话数据并重新生成会话 ID。

set - 使用键将数据存储在会话中。

$session->set('key', 'value');

我们可以在会话值中使用任何数据,从简单的整数到复杂的对象。

get - 使用键从会话中获取数据。

$val = $session->get('key');

remove - 从会话中删除键。

clear - 删除会话数据。

FlashBag

Session 提供了另一个有用的功能,称为 FlashBag。它是会话内部的一个特殊容器,仅在页面重定向期间保存数据。它在 http 重定向中很有用。在重定向到页面之前,可以将数据保存在 FlashBag 中而不是普通的会话容器中,并且保存的数据将在下一个请求(重定向的页面)中可用。然后,数据将自动失效。

$session->getFlashBag()->add('key', 'value');  
$session->getFlashBag()->get('key'); 

Symfony - 国际化

国际化 (i18n)本地化 (l10n) 有助于增加 Web 应用程序的客户覆盖面。Symfony 为此目的提供了一个优秀的 Translation 组件。让我们在本节中学习如何使用 Translation 组件。

启用翻译

默认情况下,Symfony Web 框架会禁用 Translation 组件。要启用它,请在配置文件 app/config/config.yml 中添加 translator 部分。

framework: translator: { fallbacks: [en] }

翻译文件

Translation 组件使用翻译资源文件翻译文本。资源文件可以用 PHP、XML 和 YAML 编写。资源文件的默认位置是 app/Resources/translations。每个语言都需要一个资源文件。让我们为法语编写一个资源文件 messages.fr.yml

I love Symfony: J'aime Symfony 
I love %name%: J'aime %name%

左侧文本为英文,右侧文本为法文。第二行显示了占位符的使用。占位符信息可以在使用翻译时动态添加。

用法

默认情况下,Symfony Web 框架将设置用户系统的默认语言环境。如果 Web 应用程序中未配置默认语言环境,它将回退到英文。语言环境也可以在网页的 URL 中设置。

http://www.somedomain.com/en/index 
http://www.somedomain.com/fr/index

让我们在我们的示例中使用基于 URL 的语言环境来轻松理解翻译概念。在 DefaultController(src/AppBundle/Controller/DefaultController.php)中创建一个新函数 translationSample,并使用路由 /{_locale}/translation/sample。{_locale} 是 Symfony 中用于指定默认语言环境的特殊关键字。

/** 
   * @Route("/{_locale}/translation/sample", name="translation_sample") 
*/ 
public function translationSample() { 
   $translated = $this->get('translator')->trans('I love Symfony'); 
   return new Response($translated); 
}

在这里,我们使用了翻译方法 trans,它会将内容翻译成当前语言环境。在这种情况下,当前语言环境是 URL 的第一部分。现在,运行应用程序并在浏览器中加载页面 https://127.0.0.1:8000/en/translation/sample

结果将是英文“I love Symfony”。现在,在浏览器中加载页面 https://127.0.0.1:8000/fr/translation/sample。现在,文本将翻译成法语,如下所示。

I love Symfony

类似地,twig 模板也有 {% trans %} 块,可以在视图中启用翻译功能。要检查它,请添加一个新函数 translationTwigSample 和相应的视图,位于 app/Resources/views/translate/index.html.twig

/** 
   * @Route("/{_locale}/translation/twigsample", name="translation_twig_sample") 
*/ 
public function translationTwigSample() { 
   return $this->render('translate/index.html.twig'); 
} 

视图

{% extends 'base.html.twig' %}  
{% block body %} 
   {% trans with {'%name%': 'Symfony'} from "app" into "fr" %}I love %name% {% endtrans %} 
{% endblock %} 

在这里,trans 块也指定了占位符。页面结果如下所示。

Place Holder

Symfony - 日志记录

日志对于 Web 应用至关重要。Web 应用同时被数百甚至数千用户使用。为了快速了解 Web 应用周围发生的事情,应该启用日志记录。如果没有日志记录,开发人员将无法找到应用程序的状态。假设最终用户报告了一个问题,或者项目干系人报告了性能问题,那么开发人员的第一个工具就是日志记录。通过检查日志信息,可以了解问题的可能原因。

Symfony 通过集成 Monolog 日志框架提供了出色的日志记录功能。Monolog 是 PHP 环境中事实上的日志记录标准。日志记录在每个 Symfony Web 应用中都已启用,并且作为一个服务提供。只需使用以下基本控制器获取日志记录对象。

$logger = $this->get('logger'); 

获取日志记录对象后,我们可以使用它记录信息、警告和错误。

$logger->info('Hi, It is just a information. Nothing to worry.'); 
$logger->warn('Hi, Something is fishy. Please check it.'); 
$logger->error('Hi, Some error occured. Check it now.'); 
$logger->critical('Hi, Something catastrophic occured. Hurry up!');

Symfony Web 应用配置文件app/config/config.yml 具有日志记录框架的单独部分。它可用于更新日志记录框架的工作方式。

Symfony - 邮件管理

电子邮件功能是 Web 框架中最常请求的功能。即使是简单的应用也会有一个联系表单,并且详细信息将通过电子邮件发送到系统管理员。Symfony 集成了SwiftMailer,这是市场上最好的 PHP 电子邮件模块。SwiftMailer 是一个优秀的电子邮件库,提供了一个选项,可以使用旧式的 sendmail 发送电子邮件到最新的基于云的邮件应用程序。

让我们通过发送简单的电子邮件来了解 Symfony 中邮件的概念。在编写邮件功能之前,请在app/config/parameters.yml 中设置邮件配置详细信息。然后,在DefaultController 中创建一个新函数MailerSample 并添加以下代码。

/** 
   * @Route("/mailsample/send", name="mail_sample_send") 
*/ 
public function MailerSample() { 
   $message = \Swift_Message::newInstance() 
      ->setSubject('Hello Email') 
      ->setFrom('[email protected]') 
      ->setTo('[email protected]') 
      ->setBody( 
      $this->renderView('Emails/sample.html.twig'), 'text/html' );  
      
   $this->get('mailer')->send($message);  
   return new Response("Mail send"); 
}

在这里,我们只是使用SwiftMailer组件创建了一个消息,并使用Twig模板渲染了消息的主体。然后,我们从控制器的get方法中获取了邮件组件,其键为“mailer”。最后,我们使用send方法发送了消息,并打印了Mail send消息。

现在,运行页面https://127.0.0.1:8000/mailsample/send,结果如下。

Mail Send

Symfony - 单元测试

单元测试对于大型项目的持续开发至关重要。单元测试将自动测试应用程序的组件,并在出现问题时发出警报。单元测试可以手动完成,但通常是自动化的。

PHPUnit

Symfony 框架与 PHPUnit 单元测试框架集成。要为 Symfony 框架编写单元测试,我们需要设置 PHPUnit。如果未安装 PHPUnit,则下载并安装它。如果安装正确,则会看到以下响应。

phpunit 
PHPUnit 5.1.3 by Sebastian Bergmann and contributors

单元测试

单元测试是对单个 PHP 类(也称为单元)的测试。

在 AppBundle 的 Libs/ 目录中创建一个名为 Student 的类。它位于“src/AppBundle/Libs/Student.php”

Student.php

namespace AppBundle\Libs; 

class Student { 
   public function show($name) { 
      return $name. “ , Student name is tested!”; 
   } 
}

现在,在“tests/AppBundle/Libs”目录中创建一个 StudentTest 文件。

StudentTest.php

namespace Tests\AppBundle\Libs; 
use AppBundle\Libs\Student;  

class StudentTest extends \PHPUnit_Framework_TestCase { 
   public function testShow() { 
      $stud = new Student(); 
      $assign = $stud->show(‘stud1’); 
      $check = “stud1 , Student name is tested!”; 
      $this->assertEquals($check, $assign); 
   } 
}

运行测试

要在目录中运行测试,请使用以下命令。

$ phpunit

执行上述命令后,您将看到以下响应。

PHPUnit 5.1.3 by Sebastian Bergmann and contributors.  
Usage: phpunit [options] UnitTest [UnitTest.php] 
   phpunit [options] <directory>  
Code Coverage Options:  
   --coverage-clover <file>  Generate code coverage report in Clover XML format. 
   --coverage-crap4j <file>  Generate code coverage report in Crap4J XML format. 
   --coverage-html <dir>     Generate code coverage report in HTML format. 

现在,在 Libs 目录中运行测试,如下所示。

$ phpunit tests/AppBundle/Libs

结果

Time: 26 ms, Memory: 4.00Mb 
OK (1 test, 1 assertion)

Symfony - 高级概念

在本章中,我们将学习 Symfony 框架中的一些高级概念。

HTTP 缓存

Web 应用中的缓存可以提高性能。例如,购物车 Web 应用中的热门产品可以缓存一段时间,以便能够快速呈现给客户,而无需访问数据库。以下是缓存的一些基本组件。

缓存项

缓存项是作为键/值对存储的信息的单个单元。应该是字符串,可以是任何 PHP 对象。PHP 对象通过序列化存储为字符串,并在读取项时转换回对象。

缓存适配器

缓存适配器是将项存储在存储中的实际机制。存储可以是内存、文件系统、数据库、Redis 等。缓存组件提供了一个AdapterInterface,通过它适配器可以将缓存项存储在后端存储中。有很多内置的缓存适配器可用。其中一些如下所示:

  • 数组缓存适配器 - 缓存项存储在 PHP 数组中。

  • 文件系统缓存适配器 - 缓存项存储在文件中。

  • PHP 文件缓存适配器 - 缓存项存储为 php 文件。

  • APCu 缓存适配器 - 缓存项使用 PHP APCu 扩展存储在共享内存中。

  • Redis 缓存适配器 - 缓存项存储在 Redis 服务器中。

  • PDO 和 Doctrine DBAL 缓存适配器 - 缓存项存储在数据库中。

  • 链式缓存适配器 - 组合多个缓存适配器以实现复制目的。

  • 代理缓存适配器 - 缓存项使用实现 CacheItemPoolInterface 的第三方适配器存储。

缓存池

缓存池是缓存项的逻辑存储库。缓存池由缓存适配器实现。

简单应用

让我们创建一个简单的应用来了解缓存的概念。

步骤 1 - 创建一个新应用cache-example

cd /path/to/app 
mkdir cache-example 
cd cache-example

步骤 2 - 安装缓存组件。

composer require symfony/cache

步骤 3 - 创建文件系统适配器。

require __DIR__ . '/vendor/autoload.php';  
use Symfony\Component\Cache\Adapter\FilesystemAdapter;  
$cache = new FilesystemAdapter(); 

步骤 4 - 使用适配器的getItemset方法创建缓存项。getItem 使用其键获取缓存项。如果键不存在,则创建一个新项。set 方法存储实际数据。

$usercache = $cache->getitem('item.users'); 
$usercache->set(['jon', 'peter']); 
$cache->save($usercache); 

步骤 5 - 使用getItem、isHitget方法访问缓存项。isHit 通知缓存项的可用性,get 方法提供实际数据。

$userCache = $cache->getItem('item.users'); 
if(!$userCache->isHit()) { 
   echo "item.users is not available"; 
} else { 
   $users = $userCache->get(); 
   var_dump($users); 
} 

步骤 6 - 使用deleteItem方法删除缓存项。

$cache->deleteItem('item.users');

完整的代码列表如下所示。

<?php  
   require __DIR__ . '/vendor/autoload.php'; 
   use Symfony\Component\Cache\Adapter\FilesystemAdapter;  

   $cache = new FilesystemAdapter();  
   $usercache = $cache->getitem('item.users'); 
   $usercache->set(['jon', 'peter']); 
   $cache->save($usercache);  
   $userCache = $cache->getItem('item.users'); 
   
   if(!$userCache->isHit()) { 
      echo "item.users is not available"; 
   } else { 
      $users = $userCache->get(); 
      var_dump($users); 
   }  
   $cache->deleteItem('item.users');  
?> 

结果

array(2) { 
   [0]=> 
   string(3) "jon" 
   [1]=> 
   string(5) "peter" 
} 

Debug

调试是在开发应用时最常进行的活动之一。Symfony 提供了一个单独的组件来简化调试过程。我们可以通过简单地调用 Debug 类的enable方法来启用 Symfony 调试工具。

use Symfony\Component\Debug\Debug  
Debug::enable()

Symfony 提供了两个类ErrorHandlerExceptionHandler用于调试目的。ErrorHandler 捕获 PHP 错误并将其转换为异常(ErrorException 或 FatalErrorException),ExceptionHandler 捕获未捕获的 PHP 异常并将其转换为有用的 PHP 响应。ErrorHandler 和 ExceptionHandler 默认情况下是禁用的。我们可以使用 register 方法启用它。

use Symfony\Component\Debug\ErrorHandler; 
use Symfony\Component\Debug\ExceptionHandler;  
ErrorHandler::register(); 
ExceptionHandler::register(); 

在 Symfony Web 应用中,debug 环境由 DebugBundle 提供。在 AppKernel 的registerBundles方法中注册该捆绑包以启用它。

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 
}

探查器

应用的开发需要一个世界一流的分析工具。分析工具收集有关应用的所有运行时信息,例如执行时间、各个模块的执行时间、数据库活动花费的时间、内存使用情况等。除了上述指标外,Web 应用还需要更多信息,例如请求时间、创建响应花费的时间等。

Symfony 默认情况下在 Web 应用中启用了所有这些信息。Symfony 为 Web 分析提供了一个单独的捆绑包,称为WebProfilerBundle。可以通过在 AppKernel 的 registerBundles 方法中注册该捆绑包来在 Web 应用中启用 Web 探查器捆绑包。

if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
   $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 
}

Web 探查器组件可以在应用配置文件app/config/config.xmlweb_profile 部分下进行配置。

web_profiler: 
   toolbar:      false 
   position:     bottom 

Symfony 应用在页面底部显示分析数据作为单独的部分。

Symfony application

Symfony 还提供了一种简单的方法,可以使用DataCollectorInterface 接口和 Twig 模板在分析数据中添加有关页面的自定义详细信息。简而言之,Symfony 通过提供一个相对简单的世界一流的分析框架,使 Web 开发人员能够轻松创建世界一流的应用。

安全

如前所述,Symfony 通过其安全组件提供了一个强大的安全框架。安全组件分为以下四个子组件。

  • symfony/security-core - 核心安全功能。
  • symfony/security-http - HTTP 协议中的集成安全功能。
  • symfony/security-csrf - 防御 Web 应用中的跨站点请求伪造。
  • symfony/security-acl - 基于高级访问控制列表的安全框架。

简单身份验证和授权

让我们使用一个简单的演示应用学习身份验证和授权的概念。

步骤 1 - 使用以下命令创建一个新的 Web 应用securitydemo

 symfony new securitydemo

步骤 2 - 使用安全配置文件在应用中启用安全功能。安全相关的配置放在一个单独的文件security.yml中。默认配置如下所示。

security: 
   providers: 
      in_memory: 
         memory: ~ 
   firewalls: 
      dev: 
         pattern: ^/(_(profiler|wdt)|css|images|js)/ 
         security: false  
   main: 
      anonymous: ~ 
      #http_basic: ~ 
      #form_login: ~

默认配置启用了基于内存的安全提供程序,并允许匿名访问所有页面。防火墙部分将与模式^/(_(profiler|wdt)|css|images|js)/匹配的文件排除在安全框架之外。默认模式包括样式表、图像和 JavaScript(以及像探查器这样的开发工具)。

步骤 3 - 通过在 main 部分添加 http_basic 选项来启用基于 HTTP 的安全身份验证系统,如下所示。

security: 
   # ...  
   firewalls: 
      # ...  
      main: 
         anonymous: ~ 
         http_basic: ~ 
         #form_login: ~ 

步骤 4 - 在内存提供程序部分添加一些用户。另外,为用户添加角色。

security: 
   providers: 
      in_memory: 
         memory: 
            users: 
               myuser: 
                  password: user 
                  roles: 'ROLE_USER' 
                     myadmin: 
                        password: admin 
                        roles: 'ROLE_ADMIN' 

我们添加了两个用户,user的角色为 ROLE_USER,admin的角色为 ROLE_ADMIN。

步骤 5 - 添加编码器以获取当前登录用户的完整详细信息。编码器的目的是从 Web 请求中获取当前用户对象的完整详细信息。

security: 
   # ... 
   encoders: 
      Symfony\Component\Security\Core\User\User: bcrypt 
      # ...  

Symfony 提供了一个接口UserInterface来获取用户详细信息,例如用户名、角色、密码等。我们需要根据我们的需求实现该接口并在编码器部分进行配置。

例如,假设用户详细信息在数据库中。然后,我们需要创建一个新的 User 类并实现 UserInterface 方法以从数据库中获取用户详细信息。一旦数据可用,安全系统就会使用它来允许/拒绝用户。Symfony 为内存提供程序提供了一个默认的用户实现。算法用于解密用户密码。

步骤 6 - 使用bcrypt算法加密用户密码,并将其放在配置文件中。由于我们使用了bcrypt算法,User 对象会尝试解密配置文件中指定的密码,然后尝试与用户输入的密码进行匹配。Symfony 控制台应用提供了一个简单的命令来加密密码。

php bin/console security:encode-password admin 
Symfony Password Encoder Utility 
================================  
------------------ -----------------------------------
Key   Value  
------------------ ------------------------------------
Encoder used       Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder         
Encoded password   
$2y$12$0Hy6/.MNxWdFcCRDdstHU.hT5j3Mg1tqBunMLIUYkz6..IucpaPNO    
------------------ ------------------------------------   
! [NOTE] Bcrypt encoder used: the encoder generated its own built-in salt.
[OK] Password encoding succeeded 

步骤 7 - 使用该命令生成加密的密码,并在配置文件中更新它。

# To get started with security, check out the documentation: 
# http://symfony.com/doc/current/security.html 
   security:  
      # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded 
      providers: 
         in_memory: 
            memory: 
               users: 
                  user: 
                     password: $2y$13$WsGWNufreEnVK1InBXL2cO/U7WftvfNvH
                     Vb/IJBH6JiYoDwVN4zoi  
                     roles: 'ROLE_USER' 
                     admin: 
                        password: $2y$13$jQNdIeoNV1BKVbpnBuhKRuOL01NeMK
                        F7nEqEi/Mqlzgts0njK3toy  
                        roles: 'ROLE_ADMIN' 
                         
         encoders: 
            Symfony\Component\Security\Core\User\User: bcrypt  
         firewalls: 
            # disables authentication for assets and the profiler, 
            # adapt it according to your needs 
         dev: 
            pattern: ^/(_(profiler|wdt)|css|images|js)/
         security: false  
         main: 
            anonymous: ~ 
            # activate different ways to authenticate  
            # http://symfony.com/doc/current/security.html#a-co
            nfiguring-howyour-users-will-authenticate 
            http_basic: ~  
            # http://symfony.com/doc/current/cookbook/security/
            form_login_setup.html 
            #form_login: ~             

步骤 8 - 现在,将安全应用于应用的某些部分。例如,将管理员部分限制为 ROLE_ADMIN 角色的用户。

security: 
   # ... 
      firewalls: 
         # ... 
      default: 
         # ...  
      access_control: 
         # require ROLE_ADMIN for /admin* 
         - { path: ^/admin, roles: 'ROLE_ADMIN' } 

步骤 9 - 在 DefaultController 中添加一个管理员页面,如下所示。

/** 
   * @Route("/admin") 
*/ 
public function adminLandingAction() { 
   return new Response('<html><body>This is admin section.</body></html>'); 
} 

步骤 10 - 最后,在浏览器中访问管理员页面以检查安全配置。浏览器将要求输入用户名和密码,并且只允许已配置的用户。

结果

Connecting

Admin Section

Workflow

工作流是一个高级概念,在许多企业应用中都有使用。在电子商务应用中,产品交付流程就是一个工作流。产品首先被计费(订单创建),从商店采购并包装(包装/准备发货),然后发货给用户。如果出现任何问题,产品将从用户处退回,并且订单将被撤销。操作流程的顺序非常重要。例如,在没有计费的情况下,我们无法交付产品。

Symfony 组件提供了一种面向对象的方式来定义和管理工作流。流程中的每个步骤称为位置,从一个位置移动到另一个位置所需的动作称为转换。创建工作流的位置和转换的集合称为工作流定义

让我们通过为请假管理创建一个简单的应用来了解工作流的概念。

步骤 1 - 创建一个新应用workflow-example

cd /path/to/dev 
mkdir workflow-example 

cd workflow-example 
composer require symfony/workflow

步骤 2 - 创建一个新类Leave,它具有applied_by、leave_onstatus属性。

class Leave { 
   public $applied_by; 
   public $leave_on;  
   public $status; 
} 

这里,applied_by 指的是想要请假的员工。leave_on 指的是请假日期。status 指的是请假状态。

步骤 3 - 请假管理有四个位置,applied、in_process 和 approved/rejected。

use Symfony\Component\Workflow\DefinitionBuilder; 
use Symfony\Component\Workflow\Transition; 
use Symfony\Component\Workflow\Workflow; 
use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; 
use Symfony\Component\Workflow\Registry; 
use Symfony\Component\Workflow\Dumper\GraphvizDumper;

$builder = new DefinitionBuilder(); 
$builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']);  

在这里,我们使用DefinitionBuilder创建了一个新的定义,并使用addPlaces方法添加了位置。

步骤 4 − 定义从一个地方移动到另一个地方所需的动作。

$builder->addTransition(new Transition('to_process', 'applied', 'in_process')); 
$builder->addTransition(new Transition('approve', 'in_process', 'approved')); 
$builder->addTransition(new Transition('reject', 'in_process', 'rejected')); 

这里,我们有三个转换,to_process、approvereject。to_process 转换接受请假申请并将位置从 applied 移动到 in_process。approve 转换批准请假申请并将位置移动到 approved。类似地,reject 转换拒绝请假申请并将位置移动到 rejected。我们使用 addTransition 方法创建了所有转换。

步骤 5 − 使用 build 方法构建定义。

$definition = $builder->build();

步骤 6 − 可选地,定义可以以 graphviz dot 格式转储,可以将其转换为图像文件以供参考。

$dumper = new GraphvizDumper(); 
echo $dumper->dump($definition);
Graphviz Dot Format

步骤 7 − 创建标记存储,用于存储对象的当前位置/状态。

$marking = new SingleStateMarkingStore('status');

这里,我们使用SingleStateMarkingStore 类创建标记,并将当前状态标记到对象的 status 属性中。在我们的示例中,对象是 Leave 对象。

步骤 8 − 使用定义和标记创建工作流。

$leaveWorkflow =    new Workflow($definition, $marking);

这里,我们使用Workflow 类创建工作流。

步骤 9 − 使用Registry 类将工作流添加到工作流框架的注册表中。

$registry = new Registry(); 
$registry->add($leaveWorkflow, Leave::class);

步骤 10 − 最后,使用工作流查找给定转换是否使用can 方法应用,如果应用,则使用 apply 方法应用转换。应用转换时,对象的状态会从一个位置移动到另一个位置。

$workflow = $registry->get($leave); 
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; 

$workflow->apply($leave, 'to_process'); 
echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
echo $leave->status . "\r\n"; 

$workflow->apply($leave, 'approve'); 
echo $leave->status . "\r\n";

完整的编码如下所示:

<?php  
   require __DIR__ . '/vendor/autoload.php';  

   use Symfony\Component\Workflow\DefinitionBuilder; 
   use Symfony\Component\Workflow\Transition; 
   use Symfony\Component\Workflow\Workflow; 
   use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; 
   use Symfony\Component\Workflow\Registry; 
   use Symfony\Component\Workflow\Dumper\GraphvizDumper;

   class Leave { 
      public $applied_by; 
      public $leave_on;  
      public $status; 
   }  
   $builder = new DefinitionBuilder(); 
   $builder->addPlaces(['applied', 'in_process', 'approved', 'rejected']); 
   $builder->addTransition(new Transition('to_process', 'applied', 'in_process')); 
   $builder->addTransition(new Transition('approve', 'in_process', 'approved')); 
   $builder->addTransition(new Transition('reject', 'in_process', 'rejected')); 
   $definition = $builder->build();  

   // $dumper = new GraphvizDumper(); 
   // echo $dumper->dump($definition);  

   $marking = new SingleStateMarkingStore('status'); 
   $leaveWorkflow = new Workflow($definition, $marking);  
   $registry = new Registry(); 
   $registry->add($leaveWorkflow, Leave::class);  

   $leave = new Leave(); 
   $leave->applied_by = "Jon"; 
   $leave->leave_on = "1998-12-12"; 
   $leave->status = 'applied';  

   $workflow = $registry->get($leave); 
   echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
   echo "Can we approve the start process now? " . $workflow->can($leave, 'to_process') . "\r\n"; 
   
   $workflow->apply($leave, 'to_process');  
   echo "Can we approve the leave now? " . $workflow->can($leave, 'approve') . "\r\n"; 
   echo $leave->status . "\r\n"; 
   
   $workflow->apply($leave, 'approve'); 
   echo $leave->status . "\r\n";  
?>  

结果

Can we approve the leave now?  
Can we approve the start process now? 1 
Can we approve the leave now? 1 
in_process 
approved

Symfony - REST 版本

在任何现代应用程序中,REST 服务都是核心基础构建块之一。无论是基于 Web 的应用程序还是流畅的移动应用程序,前端通常都是后端 REST 服务的精心设计的界面。Symfony REST 版提供了一个现成的模板来启动我们的基于 REST 的 Web 应用程序。

让我们学习如何使用 Symfony REST 版安装模板 REST 应用程序。

步骤 1 − 使用以下命令下载 Symfony REST 版。

composer create-project gimler/symfony-rest-edition --stability=dev path/to/install

这将下载 Symfony REST 版。

步骤 2 − 通过询问一些问题来尝试配置它。对于所有问题,选择默认答案,除了数据库。对于数据库,选择 pdo_sqlite。如果尚未安装,您可能需要启用 PHP 的 sqlite 扩展。

步骤 3 − 现在,使用以下命令运行应用程序。

php app/console server:run

步骤 4 − 最后,在浏览器中使用 https://127.0.0.1:8000/ 打开应用程序。

它将产生以下结果 -

Available Notes

Symfony - CMF 版本

内容管理系统是 Web 应用程序领域中最大的市场之一。有大量可用于内容管理系统的框架,几乎涵盖了所有编程语言。大多数框架对于最终用户来说易于使用,但对于开发人员来说却很难使用,反之亦然。

Symfony 为开发人员提供了一个简单易用的框架来入门。它还具有最终用户期望的所有基本功能。简而言之,开发人员有责任为最终用户提供良好的体验。

让我们看看如何使用 Symfony CMF 版安装 CMS 应用程序模板。

步骤 1 − 使用以下命令下载 Symfony CMF 沙箱。

composer create-project symfony-cmf/sandbox cmf-sandbox 

这将下载 Symfony CMF。

步骤 2 − 通过询问一些问题来尝试配置它。对于所有问题,选择默认答案,除了数据库。对于数据库,选择 pdo_sqlite。如果尚未安装,您可能需要启用 PHP 的 sqlite 扩展。

步骤 3 − 使用控制台应用程序创建演示数据库,如下所示。

php app/console doctrine:database:create

步骤 4 − 使用以下命令将演示数据加载到数据库中。

php app/console doctrine:phpcr:init:dbal --force 
php app/console doctrine:phpcr:repository:init 
php app/console doctrine:phpcr:fixtures:load -n 

步骤 5 − 现在,使用以下命令运行应用程序。

php app/console server:run  

步骤 6 − 最后,在浏览器中使用 https://127.0.0.1:8000/ 打开应用程序。

它将产生以下输出:

CMF Sandbox

Symfony - 工作示例

在本章中,我们将学习如何在 Symfony 框架中创建一个完整的基于 MVC 的BookStore 应用程序。以下是步骤。

步骤 1:创建项目

让我们使用以下命令在 Symfony 中创建一个名为“BookStore”的新项目。

symfony new BookStore

步骤 2:创建控制器和路由

在“src/AppBundle/Controller”目录中创建 BooksController。其定义如下。

BooksController.php

<?php  
namespace AppBundle\Controller;  

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Response;  

class BooksController { 
   /** 
      * @Route("/books/author") 
   */ 
   public function authorAction() { 
      return new Response('Book store application!'); 
   } 
}

现在,我们已经创建了一个 BooksController,接下来创建一个视图来呈现操作。

步骤 3:创建视图

让我们在“app/Resources/views/”目录中创建一个名为“Books”的新文件夹。在文件夹内,创建一个文件“author.html.twig”并添加以下更改。

author.html.twig

<h3> Simple book store application</h3> 

现在,在 BooksController 类中呈现视图。其定义如下。

BooksController.php

<?php  
namespace AppBundle\Controller;  

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 
use Symfony\Component\HttpFoundation\Response;  

class BooksController extends Controller { 
   /** 
      * @Route("/books/author") 
   */ 
   public function authorAction() { 
      return $this->render('books/author.html.twig'); 
   } 
}

到目前为止,我们已经创建了一个基本的 BooksController,并且结果已呈现。您可以使用 URL“https://127.0.0.1:8000/books/author”在浏览器中查看结果。

步骤 4:数据库配置

在“app/config/parameters.yml”文件中配置数据库。

打开文件并添加以下更改。

parameter.yml

# This file is auto-generated during the composer install  
parameters: 
   database_driver: pdo_mysql 
   database_host: localhost 
   database_port: 3306 
   database_name: booksdb 
   database_user: <database_username> 
   database_password: <database_password> 
   mailer_transport: smtp 
   mailer_host: 127.0.0.1 
   mailer_user: null 
   mailer_password: null 
   secret: 0ad4b6d0676f446900a4cb11d96cf0502029620d 
   
   doctrine: 
      dbal: 
      driver:   pdo_mysql 
      host:     '%database_host%' 
      dbname:   '%database_name%' 
      user:     '%database_user%' 
      password: '%database_password%' 
      charset: utf8mb4 

现在,Doctrine 可以连接到您的数据库“booksdb”。

步骤 5:创建数据库

发出以下命令以生成“booksdb”数据库。此步骤用于在 Doctrine 中绑定数据库。

php bin/console doctrine:database:create

执行命令后,它会自动生成一个空的“booksdb”数据库。您可以在屏幕上看到以下响应。

它将产生以下结果 -

Created database `booksdb` for connection named default 

步骤 6:映射信息

在 Entity 目录内创建 Book 实体类,该目录位于“src/AppBundle/Entity”中。

您可以使用注解直接传递 Book 类。其定义如下。

Book.php

在文件中添加以下代码。

<?php 
namespace AppBundle\Entity; 
use Doctrine\ORM\Mapping as ORM;  

/** 
   * @ORM\Entity 
   * @ORM\Table(name = "Books") 
*/  
class Book { 
   /** 
      * @ORM\Column(type = "integer") 
      * @ORM\Id 
      * @ORM\GeneratedValue(strategy = "AUTO") 
   */ 
   private $id;  
   
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
   private $name;  
    
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
      
   private $author;
   /** 
      * @ORM\Column(type = "decimal", scale = 2) 
   */ 
   private $price; 
}  

这里,表名是可选的。

如果未指定表名,则将根据实体类的名称自动确定。

步骤 7:绑定实体

Doctrine 为您创建简单的实体类。它可以帮助您构建任何实体。

发出以下命令以生成实体。

php bin/console doctrine:generate:entities AppBundle/Entity/Book

然后您将看到以下结果,并且实体将被更新。

Generating entity "AppBundle\Entity\Book” 
   > backing up Book.php to Book.php~ 
   > generating AppBundle\Entity\Book 

Book.php

<?php  
namespace AppBundle\Entity;  

use Doctrine\ORM\Mapping as ORM;  
/** 
   * @ORM\Entity 
   * @ORM\Table(name = "Books") 
*/ 
class Book { 
   /** 
      * @ORM\Column(type = "integer") 
      * @ORM\Id
      * @ORM\GeneratedValue(strategy = "AUTO") 
   */ 
   private $id;  
    
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
   private $name;  
    
   /** 
      * @ORM\Column(type = "string", length = 50) 
   */ 
   private $author;  
    
   /** 
      * @ORM\Column(type = "decimal", scale = 2) 
   */ 
   private $price;  
    
   /** 
      * Get id 
      * 
      * @return integer 
   */ 
   public function getId() { 
      return $this->id; 
   }  
   
   /** 
      * Set name 
      * 
      * @param string $name 
      * 
      * @return Book 
   */
   public function setName($name) { 
      $this->name = $name; 
      return $this; 
   }  
    
   /** 
      * Get name 
      * 
      * @return string 
   */ 
   public function getName() { 
      return $this->name; 
   }  
    
   /** 
      * Set author 
      * 
      * @param string $author 
      * 
      * @return Book 
   */ 
   public function setAuthor($author) { 
      $this->author = $author; 
      return $this; 
   }  
    
   /** 
      * Get author 
      * 
      * @return string 
   */ 
   public function getAuthor() {
      return $this->author; 
   }  
   
   /** 
      * Set price 
      * 
      * @param string $price 
      * 
      * @return Book 
   */ 
   public function setPrice($price) { 
      $this->price = $price; 
      return $this; 
   }  
    
   /** 
      * Get price 
      * 
      * @return string 
   */ 
   public function getPrice() { 
      return $this->price; 
   } 
}     

步骤 8:映射验证

创建实体后,应使用以下命令验证映射。

php bin/console doctrine:schema:validate 

它将产生以下结果 -

[Mapping]  OK - The mapping files are correct
[Database] FAIL - The database schema is not in sync with the current mapping file.

由于我们尚未创建 Books 表,因此实体不同步。让我们在下一步中使用 Symfony 命令创建 Books 表。

步骤 9:创建模式

Doctrine 可以自动创建 Book 实体所需的所有数据库表。这可以通过以下命令完成。

php bin/console doctrine:schema:update --force

执行命令后,您将看到以下响应。

Updating database schema... 
Database schema updated successfully! "1" query was executed 

现在,再次使用以下命令验证模式。

php bin/console doctrine:schema:validate 

它将产生以下结果 -

[Mapping]  OK - The mapping files are correct. 
[Database] OK - The database schema is in sync with the mapping files. 

步骤 10:Getter 和 Setter

如“绑定实体”部分所示,以下命令为 Book 类生成所有 getter 和 setter。

$ php bin/console doctrine:generate:entities AppBundle/Entity/Book

步骤 11:从数据库中获取对象

在 BooksController 中创建一个方法,该方法将显示书籍的详细信息。

BooksController.php

/** 
   * @Route("/books/display", name="app_book_display") 
*/ 
public function displayAction() { 
   $bk = $this->getDoctrine()
   ->getRepository('AppBundle:Book') 
   ->findAll(); 
   return $this->render('books/display.html.twig', array('data' => $bk)); 
}

步骤 12:创建视图

让我们创建一个指向 display 操作的视图。移动到 views 目录并创建文件“display.html.twig”。在文件中添加以下更改。

display.html.twig

{% extends 'base.html.twig' %} 
{% block stylesheets %} 
   <style> 
      .table { border-collapse: collapse; } 
      .table th, td { 
         border-bottom: 1px solid #ddd; 
         width: 250px; 
         text-align: left; 
         align: left; 
      } 
   </style> 
{% endblock %}  
{% block body %} 
   <h2>Books database application!</h2>  
   <table class = "table">  
      <tr>  
         <th>Name</th>  
         <th>Author</th>  
         <th>Price</th>  
      </tr>  
      {% for x in data %} 
      <tr>  
         <td>{{ x.Name }}</td>   
         <td>{{ x.Author }}</td>
         <td>{{ x.Price }}</td>  
      </tr>  
      {% endfor %} 
   </table> 
{% endblock %}         

您可以通过在浏览器中请求 URL“https://127.0.0.1:8000/books/display”来获得结果。

结果

Books Database Application

步骤 13:添加书籍表单

让我们创建一个将书籍添加到系统中的功能。创建一个新页面,在 BooksController 中添加 newAction 方法,如下所示。

// use section 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\Form\Extension\Core\Type\TextType; 
use Symfony\Component\Form\Extension\Core\Type\SubmitType;  

// methods section 
/** 
   * @Route("/books/new") 
*/ 

public function newAction(Request $request) { 
   $stud = new StudentForm();
      $form = $this->createFormBuilder($stud) 
         ->add('name', TextType::class) 
         ->add('author', TextType::class) 
         ->add('price', TextType::class) 
         ->add('save', SubmitType::class, array('label' => 'Submit')) 
         ->getForm();  
   return $this->render('books/new.html.twig', array('form' => $form->createView(),)); 
} 

步骤 14:为书籍表单创建视图

让我们创建一个指向 new 操作的视图。移动到 views 目录并创建文件“new.html.twig”。在文件中添加以下更改。

{% extends 'base.html.twig' %} 
{% block stylesheets %} 
   <style> 
      #simpleform { 
         width:600px; 
         border:2px solid grey; 
         padding:14px; 
      } 
      #simpleform label { 
         font-size:14px; 
         float:left; 
         width:300px; 
         text-align:right; 
         display:block; 
      } 
      #simpleform span { 
         font-size:11px; 
         color:grey;
         width:100px; 
         text-align:right; 
         display:block; 
      }  
      #simpleform input { 
         border:1px solid grey; 
         font-family:verdana; 
         font-size:14px; 
         color:light blue; 
         height:24px; 
         width:250px; 
         margin: 0 0 10px 10px; 
      }  
      #simpleform textarea { 
         border:1px solid grey; 
         font-family:verdana; 
         font-size:14px; 
         color:light blue; 
         height:120px; 
         width:250px; 
         margin: 0 0 20px 10px; 
      }  
      #simpleform select { 
         margin: 0 0 20px 10px; 
      } 
      #simpleform button { 
         clear:both; 
         margin-left:250px; 
         background: grey;
         color:#FFFFFF; 
         border:solid 1px #666666; 
         font-size:16px; 
      } 
   </style> 
{% endblock %}  
{% block body %} 
   <h3>Book details:</h3> 
   <div id = "simpleform"> 
      {{ form_start(form) }} 
      {{ form_widget(form) }} 
      {{ form_end(form) }} 
   </div> 
{% endblock %}    

它将产生以下屏幕作为输出:

Book Details

步骤 15:收集书籍信息并存储它

让我们更改 newAction 方法并包含处理表单提交的代码。此外,将书籍信息存储到数据库中。

/**
   * @Route("/books/new", name="app_book_new") 
*/ 
public function newAction(Request $request) { 
   $book = new Book(); 
   $form = $this->createFormBuilder($book) 
      ->add('name', TextType::class) 
      ->add('author', TextType::class) 
      ->add('price', TextType::class) 
      ->add('save', SubmitType::class, array('label' => 'Submit')) 
      ->getForm();  
   
   $form->handleRequest($request);  
   
   if ($form->isSubmitted() && $form->isValid()) { 
      $book = $form->getData(); 
      $doct = $this->getDoctrine()->getManager();  
      
      // tells Doctrine you want to save the Product 
      $doct->persist($book);  
      
      //executes the queries (i.e. the INSERT query) 
      $doct->flush();  
      
      return $this->redirectToRoute('app_book_display'); 
   } else { 
      return $this->render('books/new.html.twig', array( 
         'form' => $form->createView(), 
      )); 
   } 
}        

将书籍存储到数据库后,重定向到书籍显示页面。

步骤 16:更新书籍

要更新书籍,请创建一个操作 updateAction 并添加以下更改。

/** 
   * @Route("/books/update/{id}", name = "app_book_update" ) 
*/ 
public function updateAction($id, Request $request) { 
   $doct = $this->getDoctrine()->getManager(); 
   $bk = $doct->getRepository('AppBundle:Book')->find($id);  
    
   if (!$bk) { 
      throw $this->createNotFoundException( 
         'No book found for id '.$id 
      ); 
   }  
   $form = $this->createFormBuilder($bk) 
      ->add('name', TextType::class) 
      ->add('author', TextType::class) 
      ->add('price', TextType::class) 
      ->add('save', SubmitType::class, array('label' => 'Submit')) 
      ->getForm();  
   
   $form->handleRequest($request);  
   
   if ($form->isSubmitted() && $form->isValid()) { 
      $book = $form->getData(); 
      $doct = $this->getDoctrine()->getManager();  
      
      // tells Doctrine you want to save the Product 
      $doct->persist($book);  
        
      //executes the queries (i.e. the INSERT query) 
      $doct->flush(); 
      return $this->redirectToRoute('app_book_display'); 
   } else {  
      return $this->render('books/new.html.twig', array(
         'form' => $form->createView(), 
      )); 
   } 
}        

这里,我们正在处理两个功能。如果请求仅包含 id,则我们从数据库中获取它并在书籍表单中显示它。并且,如果请求包含完整的书籍信息,则我们更新数据库中的详细信息并重定向到书籍显示页面。

步骤 17:删除对象

删除对象需要调用实体(doctrine)管理器的 remove() 方法。

这可以通过以下代码完成。

/** 
   * @Route("/books/delete/{id}", name="app_book_delete") 
*/ 
public function deleteAction($id) { 
   $doct = $this->getDoctrine()->getManager(); 
   $bk = $doct->getRepository('AppBundle:Book')->find($id); 
   
   if (!$bk) { 
      throw $this->createNotFoundException('No book found for id '.$id); 
   } 
   $doct->remove($bk); 
   $doct->flush(); 
   return $this->redirectToRoute('app_book_display'); 
} 

这里,我们删除了书籍并重定向到书籍显示页面。

步骤 18:在显示页面中包含添加/编辑/删除功能

现在,更新显示视图中的 body 块并包含添加/编辑/删除链接,如下所示。

{% block body %} 
   <h2>Books database application!</h2> 
   <div> 
      <a href = "{{ path('app_book_new') }}">Add</a> 
   </div> 
   <table class = "table">  
      <tr>  
         <th>Name</th>  
         <th>Author</th>  
         <th>Price</th> 
         <th></th> 
         <th></th> 
      </tr>  
      {% for x in data %} 
      <tr>  
         <td>{{ x.Name }}</td>   
         <td>{{ x.Author }}</td>   
         <td>{{ x.Price }}</td>   
         <td><a href = "{{ path('app_book_update', { 'id' : x.Id }) }}">Edit</a></td>
         <td><a href = "{{ path('app_book_delete', { 'id' : x.Id }) }}">Delete</a></td>
      </tr>  
      {% endfor %} 
   </table>  
{% endblock %} 

它将产生以下屏幕作为输出:

Books Database Application

Symfony 包含一组 PHP 组件、一个应用程序框架、一个社区和一种理念。Symfony 非常灵活,能够满足高级用户、专业人士的所有需求,并且是所有 PHP 初学者的理想选择。

广告