Zend Framework - 快速指南
Zend Framework - 简介
PHP Web 框架是一组有助于开发 Web 应用程序的类。Zend 是最流行的 PHP 框架之一。它是一个用于快速开发现代 Web 应用程序的开源 MVC 框架。Zend Framework 有几个松散耦合的组件,因此被称为"组件库"。Zend Framework 提供任何 PHP 堆栈和 Zend 服务器来运行 Zend 框架应用程序。
Zend Studio 是一个包含与 Zend Framework 集成的功能的 IDE。它提供 MVC 视图和代码生成。当前的 Zend Framework 3.0 包括新组件,例如 JSON RPC 服务器、XML 到 JSON 转换器、PSR-7 功能以及与 PHP 7 的兼容性。
Zend Framework 2 是一个开源框架,用于使用 PHP 5.3+ 开发 Web 应用程序和服务。 Zend Framework 2 使用 100% 面向对象代码,并利用了 PHP 5.3 的大部分新功能,即命名空间、Lambda 函数和闭包。
Zend Framework 2 是从 Zend Framework 1 发展而来的,Zend Framework 1 是一个成功的 PHP 框架,下载量超过 1500 万次。 Zend Server 有免费的社区版和商业版。
Zend Framework 功能
Zend Framework 的一些显著功能如下 −
- 纯面向对象的 Web 应用程序框架
- 高级 MVC 实现
- 支持多种数据库,包括 PostgreSQL、SQLite 等,
- 简单的云 API
- 会话管理
- 数据加密
- 灵活的 URI 路由
- Zend 提供 RESTful API 开发支持。
- 代码可重用且更易于维护。
为什么选择 Zend Framework?
Zend Framework 成为 PHP 开发人员使用的首要框架之一的原因在于 - 它提供了干净、稳定的代码,并拥有完整的知识产权。它还使编程变得更容易。它是一个快速、易学且方便的框架。Zend 支持强大的加密工具和密码哈希技术。
Zend 目标
以下是 Zend 框架的目标。
- 灵活性
- 简单高效
- 兼容性
- 可扩展性 − 程序员可以轻松扩展所有框架类。
- 可移植性 − 支持多种环境
Zend 应用程序
以下流行产品是使用 Zend 框架开发的。
- McAfee 公司网站
- IBM 公司网站
- Magento −最受欢迎的购物车网站之一。
Zend Framework 的优势
下面列出了 Zend Framework 的一些优势。
松散耦合 − Zend 提供了删除应用程序中不需要的模块或组件的选项。
性能 − Zend Framework 针对性能进行了高度优化。Zend Framework 3 比其以前的版本快 4 倍。
安全性 − 框架支持行业标准加密。
测试 − PHPUnit 与 Zend 集成,因此您可以轻松测试框架。
在下一章中,我们将学习如何安装 Zend Framework。
Zend Framework - 安装
要安装 Zend Framework,我们必须首先安装 Composer 和最新版本的 PHP,如以下步骤所示。
安装 Composer − Zend 使用 Composer 来管理其依赖项,因此请确保您的机器上安装了 Composer。如果未安装 Composer,请访问 Composer 的官方网站并进行安装。
安装最新版本的 PHP − 要获得 Zend Framework 的最大优势,请安装最新版本的 PHP。 Zend Framework 3 所需的最低版本是 PHP 5.6 或更高版本。
安装 Zend Framework
Zend Framework 可以通过两种方式安装。它们如下 −
- 手动安装
- 基于 Composer 的安装
让我们详细讨论这两种安装方式。
手动安装
通过访问以下链接下载最新版本的 Zend Framework - https://framework.zend.com/downloads/archives
将下载的存档文件的内容提取到您想要保留的文件夹中。一旦您在本地机器上拥有 Zend Framework 的副本,基于 Zend Framework 的 Web 应用程序就可以访问框架类。虽然有几种方法可以实现这一点,但您的 PHP include_path 需要包含分发版中 /library 目录下 Zend Framework 类的路径。此方法仅适用于 Zend Framework 2.4 版及更早版本。
基于 Composer 的安装
要轻松安装 Zend Framework,请使用 Composer 工具。这是安装最新版本 Zend Framework 的首选方法。要安装 Zend Framework 的所有组件,请使用以下 Composer 命令 −
$ composer require zendframework/zendframework
每个 Zend Framework 模块/组件也可以单独安装。例如,要安装 Zend Framework 的 MVC 组件,请使用以下 composer 命令 −
$ composer require zendframework/zend-mvc
Zend Framework - 骨架应用程序
让我们使用 Zend Framework MVC 层和模块系统创建一个骨架应用程序。
使用 Composer 安装
创建新 Zend Framework 项目的最简单方法是使用 Composer。它的定义如下 −
$ cd /path/to/install $ composer create-project -n -sdev zendframework/skeleton-application myapp
您将在屏幕上看到以下结果 −
Installing zendframework/skeleton-application (dev-master 941da45b407e4f09e264f000fb537928badb96ed) - Installing zendframework/skeleton-application (dev-master master) Cloning master Created project in myapp Loading composer repositories with package information Installing dependencies (including require-dev) from lock file - Installing zendframework/zend-component-installer (0.3.0) Loading from cache - Installing zendframework/zend-stdlib (3.0.1) Loading from cache - Installing zendframework/zend-config (2.6.0) Loading from cache - Installing zendframework/zend-loader (2.5.1) Loading from cache - Installing zendframework/zend-eventmanager (3.0.1) Loading from cache - Installing zendframework/zend-view (2.8.0) Loading from cache - Installing container-interop/container-interop (1.1.0) Loading from cache - Installing zendframework/zend-servicemanager (3.1.0) Loading from cache - Installing zendframework/zend-validator (2.8.1) Loading from cache - Installing zendframework/zend-escaper (2.5.1) Loading from cache - Installing zendframework/zend-uri (2.5.2) Loading from cache - Installing zendframework/zend-http (2.5.4) Loading from cache - Installing zendframework/zend-router (3.0.2) Loading from cache - Installing zendframework/zend-modulemanager (2.7.2) Loading from cache - Installing zendframework/zend-mvc (3.0.1) Loading from cache - Installing zendframework/zend-skeleton-installer (0.1.3) Loading from cache - Installing zfcampus/zf-development-mode (3.0.0) Loading from cache zendframework/zend-config suggests installing zendframework/zend-filter (Zend\Filter component) zendframework/zend-config suggests installing zendframework/zend-i18n (Zend\I18n component) zendframework/zend-config suggests installing zendframework/zend-json (Zend\Json to use the Json reader or writer classes) zendframework/zend-view suggests installing zendframework/zend-authentication (Zend\Authentication component) zendframework/zend-view suggests installing zendframework/zend-feed (Zend\Feed component) zendframework/zend-view suggests installing zendframework/zend-filter (Zend\Filter component) zendframework/zend-view suggests installing zendframework/zend-i18n (Zend\I18n component) zendframework/zend-view suggests installing zendframework/zend-json (Zend\Json component) zendframework/zend-view suggests installing zendframework/zend-navigation (Zend\Navigation component) zendframework/zend-view suggests installing zendframework/zend-paginator (Zend\Paginator component) zendframework/zend-view suggests installing zendframework/zend-permissions-acl (Zend\Permissions\Acl component) zendframework/zend-servicemanager suggests installing ocramius/proxy-manager (ProxyManager 1.* to handle lazy initialization of services) zendframework/zend-validator suggests installing zendframework/zend-db (Zend\Db component) zendframework/zend-validator suggests installing zendframework/zend-filter (Zend\Filter component, required by the Digits validator) zendframework/zend-validator suggests installing zendframework/zend-i18n (Zend\I18n component to allow translation of validation error messages as well as to use the various Date validators) zendframework/zend-validator suggests installing zendframework/zend-i18nresources (Translations of validator messages) zendframework/zend-validator suggests installing zendframework/zend-math (Zend\Math component) zendframework/zend-validator suggests installing zendframework/zend-session (Zend\Session component) zendframework/zend-router suggests installing zendframework/zend-i18n (^2.6, if defining translatable HTTP path segments) zendframework/zend-modulemanager suggests installing zendframework/zend-console (Zend\Console component) zendframework/zend-mvc suggests installing zendframework/zend-json ((^2.6.1 || ^3.0) To auto-deserialize JSON body content in AbstractRestfulController extensions, when json_decode is unavailable) zendframework/zend-mvc suggests installing zendframework/zend-mvc-console (zend-mvc-console provides the ability to expose zend-mvc as a console application) zendframework/zend-mvc suggests installing zendframework/zend-mvc-i18n (zendmvc-i18n provides integration with zend-i18n, including a translation bridge and translatable route segments) zendframework/zend-mvc suggests installing zendframework/zend-mvc-pluginfileprg (To provide Post/Redirect/Get functionality around forms that container file uploads) zendframework/zend-mvc suggests installing zendframework/zend-mvc-pluginflashmessenger (To provide flash messaging capabilities between requests) zendframework/zend-mvc suggests installing zendframework/zend-mvc-pluginidentity (To access the authenticated identity (per zend-authentication) in controllers) zendframework/zend-mvc suggests installing zendframework/zend-mvc-plugin-prg (To provide Post/Redirect/Get functionality within controllers) zendframework/zend-mvc suggests installing zendframework/zend-psr7bridge ((^0.2) To consume PSR-7 middleware within the MVC workflow) zendframework/zend-mvc suggests installing zendframework/zend-servicemanager-di (zend-servicemanager-di provides utilities for integrating zend-di and zendservicemanager in your zend-mvc application) Generating autoload files Removing optional packages from composer.json Updating composer.json Removing zendframework/zend-skeleton-installer... - Removing zendframework/zend-skeleton-installer (0.1.3) Removed plugin zendframework/zend-skeleton-installer. Removing from composer.json Complete! > zf-development-mode enable You are now in development mode.
现在应用程序已安装,您可以立即使用 PHP 的内置 Web 服务器 对其进行测试 −
$ cd path/to/install/myapp $ composer serve
然后您将看到以下响应 −
> php -S 0.0.0.0:8080 -t public/ public/index.php
这将在端口 8080 上启动 PHP 内置 CLI 服务器。开发服务器启动后,您可以访问 (http://localhost:8080/) 上的站点。内置 CLI 服务器仅用于开发。
单元测试
要运行骨架单元测试,请在终端中输入以下命令。
$ composer require --dev zendframework/zend-test
它将产生以下响应 −
Using version ^3.0 for zendframework/zend-test ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing zendframework/zend-dom (2.6.0) Loading from cache - Installing zendframework/zend-console (2.6.0) Loading from cache - Installing sebastian/version (2.0.1) Loading from cache - Installing symfony/yaml (v3.2.1) Downloading: 100% - Installing sebastian/resource-operations (1.0.0) Loading from cache - Installing sebastian/recursion-context (2.0.0) Loading from cache - Installing sebastian/object-enumerator (2.0.0) Loading from cache - Installing sebastian/global-state (1.1.1) Loading from cache - Installing sebastian/exporter (2.0.0) Loading from cache - Installing sebastian/environment (2.0.0) Loading from cache - Installing sebastian/diff (1.4.1) Loading from cache - Installing sebastian/comparator (1.2.2) Loading from cache - Installing phpunit/php-text-template (1.2.1) Loading from cache - Installing doctrine/instantiator (1.0.5) Loading from cache - Installing phpunit/phpunit-mock-objects (3.4.3) Downloading: 100% - Installing phpunit/php-timer (1.0.8) Loading from cache - Installing phpunit/php-file-iterator (1.4.2) Loading from cache - Installing sebastian/code-unit-reverse-lookup (1.0.0) Loading from cache - Installing phpunit/php-token-stream (1.4.9) Loading from cache - Installing phpunit/php-code-coverage (4.0.4) Downloading: 100% - Installing webmozart/assert (1.2.0) Loading from cache - Installing phpdocumentor/reflection-common (1.0) Loading from cache - Installing phpdocumentor/type-resolver (0.2.1) Loading from cache - Installing phpdocumentor/reflection-docblock (3.1.1) Loading from cache - Installing phpspec/prophecy (v1.6.2) Loading from cache - Installing myclabs/deep-copy (1.5.5) Loading from cache - Installing phpunit/phpunit (5.7.4) Downloading: 100% - Installing zendframework/zend-test (3.0.2) Loading from cache zendframework/zend-console suggests installing zendframework/zend-filter (To support DefaultRouteMatcher usage) symfony/yaml suggests installing symfony/console (For validating YAML files using the lint command) sebastian/global-state suggests installing ext-uopz (*) phpunit/phpunit-mock-objects suggests installing ext-soap (*) phpunit/php-code-coverage suggests installing ext-xdebug (>=2.4.0) phpunit/phpunit suggests installing phpunit/php-invoker (~1.1) phpunit/phpunit suggests installing ext-xdebug (*) zendframework/zend-test suggests installing zendframework/zend-mvc-console (^1.1.8, to test MVC <-> console integration) Writing lock file Generating autoload files
现在测试支持已启用,因此您可以使用以下命令运行测试。
$ ./vendor/bin/phpunit
Apache Web 服务器
在生产环境中托管基于 Zend Framework 的应用程序非常简单直接。只需在 Apache 配置文件中创建一个 VirtualHost,并将 DocumentRoot 指向 Zend Framework 应用程序的 Public 文件夹即可。
下面给出了示例配置 (myapp) −
<VirtualHost *:80> ServerName myapp.localhost DocumentRoot /path/to/install/myapp/public <Directory /path/to/install/myapp/public> DirectoryIndex index.php AllowOverride All Order allow,deny Allow from all <IfModule mod_authz_core.c> Require all granted </IfModule> </Directory> </VirtualHost>
Zend Framework - MVC 架构
在继续本章之前,让我们先简单了解一下 MVC。模型视图控制器是一种将应用程序逻辑与表示分离的软件方法。实际上,它允许网页包含最少的 PHP 脚本,因为表示与它分开。
MVC 组件的简短描述如下
Model − 模型表示应用程序数据的结构。通常,模型类包含有助于在后端数据库(MySQL、PostgreSQL 等)中检索、插入和更新业务数据的函数。
View − 视图是 MVC 应用程序的表示层。它通过控制器获取模型数据并根据需要显示它。它与 Controller 和 Model 松散耦合,因此,可以更改它而不会影响 Model 和 Controller。
Controller − Controller 是 MVC 架构的主要组件。每个请求首先到达控制器。换句话说,控制器处理所有请求,并充当 Model、View 和任何其他所需资源之间的中介,以处理 HTTP 请求并生成响应。
在下一章中,我们将了解 Zend Framework 的不同概念。
Zend Framework - 概念
Zend Framework 是 60 多个组件的集合。它们彼此松散连接。它们既可以用作独立组件,也可以作为一组组件作为一个单元工作。
Zend Framework 提供了三个最重要的组件,分别是 −
- zend-servicemanager
- zend-eventmanager 和
- zend-modulemanager。
它们为 Zend 组件提供了与其他组件有效集成的能力。
事件管理器 − 它提供了创建基于事件的编程的能力。这有助于创建、注入和管理新事件。
服务管理器 − 它提供了从任何地方轻松使用任何服务(PHP 类)的能力。
模块管理器 −能够将具有类似功能的 PHP 类集合转换为称为模块的单个单元。新创建的模块可以作为单个单元使用、维护和配置。
我们将在后续章节中详细介绍这些概念。
Zend Framework - 服务管理器
Zend Framework 包含一个强大的服务定位器模式实现,称为 zend-servicemanager。Zend Framework 广泛使用服务管理器来实现其所有功能。服务管理器为 Zend Framework 提供了高级抽象。它还与 Zend Framework 的所有其他组件完美集成。
安装服务管理器
可以使用 composer 工具安装服务管理器组件。
composer require zendframework/zend-servicemanager
示例
首先,需要将所有服务注册到服务管理器中。一旦将服务注册到服务器管理器系统中,就可以随时以最小的努力进行访问。服务管理器提供了很多注册服务的选项。一个简单的示例如下 −
use Zend\ServiceManager\ServiceManager; use Zend\ServiceManager\Factory\InvokableFactory; use stdClass; $serviceManager = new ServiceManager([ 'factories' => [stdClass::class => InvokableFactory::class,], ]);
上述代码使用 Factory 选项将 stdClass 注册到系统中。现在,我们可以随时使用服务管理器的 get() 方法获取 stdClass 的实例,如下所示。
use Zend\ServiceManager\ServiceManager; $object = $serviceManager->get(stdClass::class);
get() 方法共享检索到的对象,因此,多次调用 get() 方法返回的对象是同一个实例。为了每次都获取不同的实例,服务管理器提供了另一种方法,即 build() 方法。
use Zend\ServiceManager\ServiceManager; $a = $serviceManager->build(stdClass::class); $b = $serviceManager->build(stdClass::class);
服务管理器注册
服务管理器提供了一组方法来注册组件。一些最重要的方法如下所示 −
- 工厂方法
- 抽象工厂方法
- 初始化方法
- 委托工厂方法
我们将在接下来的章节中详细讨论这些方法。
工厂方法
工厂基本上是任何可调用的或任何实现 FactoryInterface (Zend\ServiceManager\Factory\FactoryInterface) 的类。
FactoryInterface 有一个方法 −
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
FactoryInterface 的参数详细信息如下−
container (ContainerInterface) − 它是 ServiceManager 的基本接口。它提供了获取其他服务的选项。
requestedName − 它是服务名称。
options − 它提供服务所需的其他选项。
让我们创建一个实现 FactoryInterface 的简单类,并了解如何注册该类。
类测试 - 要检索的对象
use stdClass; class Test { public function __construct(stdClass $sc) { // use $sc } }
Test 类依赖于 stdClass。
TestFactory 类 - 用于初始化测试对象的类
class TestFactory implements FactoryInterface { public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { $dep = $container->get(stdClass::class); return new Test($dep); } }
TestFactory 使用容器来检索 stdClass,创建测试类的实例并返回它。
Zend 框架的注册和使用
现在让我们了解如何注册和使用 Zend 框架。
serviceManager $sc = new ServiceManager([ 'factories' => [stdClass::class => InvokableFactory::class, Test::class => TestFactory::class] ]); $test = $sc->get(Test::class);
服务管理器提供了一个名为 InvokableFactory 的特殊工厂来检索任何没有依赖关系的类。例如,可以使用 InvokableFactory 配置 stdClass,因为 stdClass 不依赖于任何其他类。
serviceManager $sc = new ServiceManager([ 'factories' => [stdClass::class => InvokableFactory::class] ]); $stdC = $sc->get(stdClass::class);
无需实现 FactoryInterface 或使用 InvokableFactory 即可检索对象的另一种方法是使用下面给出的内联方法。
$serviceManager = new ServiceManager([ 'factories' => [ stdClass::class => InvokableFactory::class, Test::class => function(ContainerInterface $container, $requestedName) { $dep = $container->get(stdClass::class); return new Test($dep); }, ], ]);
抽象工厂方法
有时,我们可能需要创建对象,而这些对象我们只能在运行时才知道。这种情况可以使用从 FactoryInterface 派生的 AbstractFactoryInterface 来处理。
AbstractFactoryInterface 定义了一个方法来检查是否可以在请求的实例上创建对象。如果可以创建对象,它将使用 FactoryInterface 的 __invokemethod 创建对象并返回它。
AbstractFactoryInterface 的签名如下 −
public function canCreate(ContainerInterface $container, $requestedName)
初始化方法
初始化方法是一种特殊的选项,用于为已创建的服务注入额外的依赖关系。它实现了 InitializerInterface,唯一可用方法的签名如下 −
public function(ContainerInterface $container, $instance) function(ContainerInterface $container, $instance) { if (! $instance instanceof EventManagerAwareInterface) { return; } $instance->setEventManager($container->get(EventManager::class)); }
在上面的例子中,该方法检查实例是否为 EventManagerAwareInterface 类型。如果是 EventManagerAwareInterface 类型,则设置事件管理器对象,否则不设置。由于该方法可能会或可能不会设置依赖项,因此它不可靠并且会产生许多运行时问题。
委托人工厂方法
Zend Framework 通过 DelegatorFactoryInterface 支持委托人模式。它可用于装饰服务。
此函数的签名如下 −
public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null );
此处,$callback 负责修饰服务实例。
惰性服务
惰性服务是那些在创建时不会完全初始化的服务之一。它们只是被引用,并且只在真正需要时才初始化。最好的例子之一是数据库连接,它可能不是在所有地方都需要。它是一种昂贵的资源,并且创建过程也很耗时。Zend 框架提供了从 DelegatorFactoryInterface 派生的 LazyServiceFactory,它可以在 Delegator 概念和第三方代理管理器(称为 ocramius 代理管理器)的帮助下生成惰性服务。
插件管理器
插件管理器扩展了服务管理器并提供实例验证等附加功能。 Zend Framework 广泛使用插件管理器。
例如,所有验证服务都属于 ValidationPluginManager。
配置选项
服务管理器提供了一些选项来扩展服务管理器的功能。它们是 shared、shared_by_default 和 aliases。正如我们前面所讨论的,检索到的对象默认在请求的对象之间共享,我们可以使用 build() 方法获取不同的对象。我们还可以使用 shared 选项来指定要共享的服务。shared_by_default 与 shared 功能相同,只是它适用于所有服务。
$serviceManager = new ServiceManager([ 'factories' => [ stdClass::class => InvokableFactory::class ], 'shared' => [ stdClass::class => false // 不会共享 ], 'shared_by_default' => false, // 不会共享且适用于所有服务 ]);
别名选项可用于为已注册的服务提供备用名称。这既有优点也有缺点。从积极的一面来看,我们可以为服务提供备用的简称。但与此同时,该名称可能会脱离上下文并引入错误。
aliases' => ['std' => stdClass::class, 'standard' => 'std']
Zend Framework - 事件管理器
所有现代应用程序都需要可靠且灵活的事件组件。Zend Framework 提供了一个这样的组件,即 zend-eventmanager。zend-eventmanager 有助于设计高级架构,并支持主题/观察者模式和面向方面编程。
安装事件管理器
可以使用 Composer 安装事件管理器,如下所示 −
composer require zendframework/zend-eventmanager
事件管理器的概念
事件管理器的核心概念如下 −
Event −事件是任意命名的动作,例如 greet。
Listener − 任何 PHP 回调。它们附加到事件并在触发事件时被调用。Listener 的默认签名是 −
function(EventInterface $e)
EventInterface 类 − 用于指定事件本身。它具有设置和获取事件信息的方法,如名称 (set/getName)、目标 (get/setTarget) 和参数 (get/setParams)。
EventManager 类 − EventManager 的实例跟踪应用程序中定义的所有事件及其相应的侦听器。 EventManager 提供了一种方法 attach,用于将侦听器附加到事件,并提供了一种方法 trigger,用于触发任何预定义事件。一旦调用触发器,EventManager 就会调用附加到它的侦听器。
EventManagerAwareInterface − 对于支持基于事件的编程的类,它需要实现 EventManagerAwareInterface。它提供了两种方法,setEventManager 和 getEventManager 来获取和设置事件管理器。
示例
让我们编写一个简单的 PHP 控制台应用程序来了解事件管理器概念。按照以下步骤操作。
创建文件夹"eventapp"。
使用 Composer 安装 zend-eventmanager。
在"eventapp"文件夹中创建一个 PHP 文件 Greeter.php。
创建类 Greeter 并实现 EventManagerAwareInterface。
require __DIR__ . '/vendor/autoload.php'; class Greeter implements EventManagerAwareInterface { // code }
此处,require 用于自动加载所有 Composer 安装的组件。
在 Greeter 类中编写 setEventManager 方法,如下所示 −
public function setEventManager(EventManagerInterface $events) { $events->setIdentifiers([ __CLASS__, get_called_class(),]); $this->events = $events; return $this; }
此方法将当前类设置为给定的事件管理器($events 参数),然后在局部变量 $events 中设置事件管理器。
下一步是在类 Greeter 中编写 getEventManager 方法,如下所示 −
public function getEventManager() { if (null === $this->events) { $this->setEventManager(new EventManager()); } return $this->events; }
该方法从局部变量获取事件管理器。如果不可用,则创建事件管理器的实例并返回它。
在类 Greeter 中编写方法 greet。
public function greet($message) { printf("\"%s\" from class ", $message); $this->getEventManager()->trigger(__FUNCTION__, $this, $message ]); }
此方法获取事件管理器并触发附加到其上的事件。
下一步是创建 Greeter 类的实例,并将侦听器附加到其方法 greet。
$greeter = new Greeter(); $greeter->getEventManager()->attach('greet', function($e) { $event_name = $e->getName(); $target_name = get_class($e->getTarget()); $params_json = json_encode($e->getParams()); printf("\"%s\" event of class \"%s\" is called." . " The parameter supplied is %s ", $event_name, $target_name, $params_json); });
侦听器回调仅打印事件的名称、目标和提供的参数。
Greeter.php 的完整列表如下 −
<?php require __DIR__ . '/vendor/autoload.php'; use Zend\EventManager\EventManagerInterface; use Zend\EventManager\EventManager; use Zend\EventManager\EventManagerAwareInterface; class Greeter implements EventManagerAwareInterface { protected $events; public function setEventManager(EventManagerInterface $events) { $events->setIdentifiers([__CLASS__, get_called_class(), ]); $this->events = $events; return $this; } public function getEventManager() { if (null === $this->events) { $this->setEventManager(new EventManager()); } return $this->events; } public function greet($message) { printf("\"%s\" from class ", $message); $this->getEventManager()->trigger(__FUNCTION__, $this, [$message ]); } } $greeter = new Greeter(); $greeter->greet("Hello"); $greeter->getEventManager()->attach('greet', function($e) { $event_name = $e->getName(); $target_name = get_class($e->getTarget()); $params_json = json_encode($e->getParams()); printf("\"%s\" event of class \"%s\" is called." . " The parameter supplied is %s ", $event_name, $target_name, $params_json); }); $greeter->greet("Hello");
现在,在命令提示符中运行应用程序 php Greeter.php,结果如下 −
"Hello" from class "Hello" from class "greet" event of class "Greeter" is called. The parameter supplied is ["Hello"]
上述示例应用程序仅解释了事件管理器的基本知识。事件管理器提供了许多更高级的选项,例如侦听器优先级、自定义回调原型/签名、短路等。事件管理器在 Zend MVC 框架中得到广泛使用。
Zend Framework - 模块系统
Zend Framework 提供了强大的模块系统。模块系统有三个组件。它们如下 −
模块自动加载器 − 模块自动加载器负责从各种来源定位和加载模块。它也可以加载打包为 Phar 档案 的模块。模块自动加载器的实现位于 myapp/vendor/zendframework/zend-loader/src/ModuleAutoloader.php。
模块管理器 − 一旦模块自动加载器找到模块,模块管理器就会为每个模块触发一系列事件。模块管理器的实现位于 myapp/vendor/zendframework/zendmodulemanager/src/ModuleManager.php。
模块管理器监听器 − 它们可以附加到模块管理器触发的事件。通过附加到模块管理器的事件,它们可以执行从解析和加载模块到为每个模块执行复杂工作的所有操作。
MVC Web 模块系统
Zend Framework 中的 MVC Web 应用程序通常写为模块。单个网站可以包含一个或多个按功能分组的模块。面向 MVC 的模块的推荐结构如下 −
module_root/ Module.php autoload_classmap.php autoload_function.php autoload_register.php config/ module.config.php public/ images/ css/ js/ src/ <module_namespace>/ <code files> test/ phpunit.xml bootstrap.php <module_namespace>/ <test code files> view/ <dir-named-after-module-namespace>/ <dir-named-after-a-controller>/ <.phtml files>
结构与上一章讨论的相同,但这里是通用的。autoload_ 文件 可用作默认机制,用于自动加载模块中可用的类,而无需使用 zend-modulemanager 中提供的高级 模块管理器。
autoload_classmap.php − 返回类名及其对应文件名的数组。
autoload_function.php − 返回 PHP 回调。这可以利用 autoload_classmap.php 返回的类。
autoload_register.php − 注册 autoload_function.php 返回的 PHP 回调。
这些自动加载文件不是必需的,但建议使用。在骨架应用程序中,我们没有使用 autoload_ 文件。
模块类
模块类应命名为 Module,模块类的命名空间应为 Module name。这将有助于 Zend Framework 轻松解析和加载模块。骨架(myapp)应用程序中的 Application 模块代码,myapp/module/Application/src/Module.php 如下: −
namespace Application; class Module { const VERSION = '3.0.2dev'; public function getConfig() { return include __DIR__ . '/../config/module.config.php'; } }
Zend Framework 模块管理器将自动调用 getConfig() 函数并执行必要的步骤。
Zend Framework - 应用程序结构
在本章中,让我们了解 Zend Framework 应用程序的结构。myapp 应用程序的结构如下 −
├── composer.json ├── composer.lock ├── CONDUCT.md ├── config │ ├── application.config.php │ ├── autoload │ │ ├── development.local.php │ │ ├── development.local.php.dist │ │ ├── global.php │ │ ├── local.php.dist │ │ ├── README.md │ │ └── zend-developer-tools.local-development.php │ ├── development.config.php │ ├── development.config.php.dist │ └── modules.config.php ├── CONTRIBUTING.md ├── data │ └── cache │ └── module-classmap-cache.application.module.cache.php ├── docker-compose.yml ├── Dockerfile ├── LICENSE.md ├── module │ └── Application │ ├── config │ ├── src │ ├── test │ └── view ├── phpcs.xml ├── phpunit.xml.dist ├── public │ ├── css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ └── style.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── img │ │ ├── favicon.ico │ │ └── zf-logo-mark.svg │ ├── index.php │ ├── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── jquery-3.1.0.min.js │ └── web.config ├── README.md ├── TODO.md ├── Vagrantfile └── vendor ├── autoload.php ├── bin │ ├── phpunit -> ../phpunit/phpunit/phpunit │ ├── templatemap_generator.php -> ../zendframework/zend- view/bin/templatemap_generator.php │ └── zf-development-mode -> ../zfcampus/zf-development-mode/bin/zf- development-mode ├── composer │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_real.php │ ├── ClassLoader.php │ ├── installed.json │ └── LICENSE ├── container-interop │ └── container-interop ├── doctrine │ └── instantiator ├── myclabs │ └── deep-copy ├── phpdocumentor │ ├── reflection-common │ ├── reflection-docblock │ └── type-resolver ├── phpspec │ └── prophecy ├── phpunit │ ├── php-code-coverage │ ├── php-file-iterator │ ├── php-text-template │ ├── php-timer │ ├── php-token-stream │ ├── phpunit │ └── phpunit-mock-objects ├── sebastian │ ├── code-unit-reverse-lookup │ ├── comparator │ ├── diff │ ├── environment │ ├── exporter │ ├── global-state │ ├── object-enumerator │ ├── recursion-context │ ├── resource-operations │ └── version ├── symfony │ └── yaml ├── webmozart │ └── assert ├── zendframework │ ├── zend-component-installer │ ├── zend-config │ ├── zend-console │ ├── zend-dom │ ├── zend-escaper │ ├── zend-eventmanager │ ├── zend-http │ ├── zend-loader │ ├── zend-modulemanager │ ├── zend-mvc │ ├── zend-router │ ├── zend-servicemanager │ ├── zend-stdlib │ ├── zend-test │ ├── zend-uri │ ├── zend-validator │ └── zend-view └── zfcampus └── zf-development-mode 73 directories, 55 files
Zend Framework 应用程序由不同的文件夹组成。它们如下所示 −
Application − 此目录包含您的应用程序。它将容纳 MVC 系统以及配置、使用的服务和引导文件。
Config − 此目录包含应用程序的配置文件。
Data − 此目录提供了一个存储易失性且可能是临时的应用程序数据的地方。
Module − 模块允许开发人员将一组相关控制器分组为一个逻辑组织的组。
Public − 这是应用程序的文档根目录。它启动 Zend 应用程序。它还包含应用程序的资源,如 JavaScript、CSS、图像等。
Vendor − 此目录包含作曲家依赖项。
应用程序模块的结构
这是应用程序的主目录。Zend Framework 2 引入了强大而灵活的模块系统来有效地组织应用程序。骨架应用程序 (myapp) 的 Application 模块为整个应用程序提供引导、错误和路由配置。Application 模块的结构如下所示 −
├── module │ └── Application │ ├── config │ │ └── module.config.php │ ├── src │ │ ├── Controller │ │ │ └── IndexController.php │ │ └── Module.php │ ├── test │ │ └── Controller │ │ └── IndexControllerTest.php │ └── view │ ├── application │ │ └── index │ │ └── index.phtml │ ├── error │ │ ├── 404.phtml │ │ └── index.phtml │ └── layout │ └── layout.phtml
让我们详细介绍每个模块目录 −
Application − 这是模块的根目录。文件夹的名称将与模块的名称匹配,并且该名称还用作模块内定义的所有类的 PHP 命名空间。它将容纳 MVC 系统以及配置、使用的服务和引导文件。
Config − 模块的独立配置。
Src − 应用程序的主要业务逻辑。
View − 包含设计/演示 (HTML) 文件。例如,index.phtml。
src/Module.php − 它是模块的核心。它充当模块的"前端控制器"。Zend 在处理此模块中的任何 PHP 类之前会先处理 src/Module.php 文件。
Application/config/module.config.php − 它用于路由器配置和自动加载文件。
Application/view/layout − 布局表示多个视图的公共部分。例如,页眉和页脚。默认情况下,布局应存储在 views/layoutsfolder 中。
所有模块都具有与上述 Application 模块相同或相似的结构。
Zend Framework - 创建模块
在本章中,我们将学习如何在 Zend Framework 中创建基于 MVC 的模块。让我们创建一个名为 Tutorial 的模块来了解模块创建过程。
在 –myapp/module/Tutorial/src/ 目录中创建一个名为 Module 的新 PHP 类,并实现 ConfigProviderInterface。
将 Tutorial 设置为 Module 类的命名空间。
在 Module 类中编写一个公共函数 getConfig,并返回 Tutorial 模块的配置文件。
Module 类的完整代码如下 −
<?php namespace Tutorial; use Zend\ModuleManager\Feature\ConfigProviderInterface; class Module implements ConfigProviderInterface { public function getConfig() { return include __DIR__ . '/../config/module.config.php'; } }
使用以下代码在 composer.json 的 autoload 部分下配置 Tutorial 模块。
"autoload": { "psr-4": { "Application\": "module/Application/src/", "Tutorial\": "module/Tutorial/src/" } }
使用 composer update 命令更新应用程序,如下所示。
composer update
composer 命令将对应用程序进行必要的更改,并在命令提示符中显示日志,如下所示 −
Loading composer repositories with package information Updating dependencies (including require-dev) - Removing zendframework/zend-component-installer (0.3.0) - Installing zendframework/zend-component-installer (0.3.1) Downloading: 100% - Removing zendframework/zend-stdlib (3.0.1) - Installing zendframework/zend-stdlib (3.1.0) Loading from cache - Removing zendframework/zend-eventmanager (3.0.1) - Installing zendframework/zend-eventmanager (3.1.0) Downloading: 100% - Removing zendframework/zend-view (2.8.0) - Installing zendframework/zend-view (2.8.1) Loading from cache - Removing zendframework/zend-servicemanager (3.1.0) - Installing zendframework/zend-servicemanager (3.2.0) Downloading: 100% - Removing zendframework/zend-escaper (2.5.1) - Installing zendframework/zend-escaper (2.5.2) Loading from cache - Removing zendframework/zend-http (2.5.4) - Installing zendframework/zend-http (2.5.5) Loading from cache - Removing zendframework/zend-mvc (3.0.1) - Installing zendframework/zend-mvc (3.0.4) Downloading: 100% - Removing phpunit/phpunit (5.7.4) - Installing phpunit/phpunit (5.7.5) Downloading: 100% Writing lock file Generating autoload files
使用以下代码在 /config/ 中创建模块配置文件"module.config.php" −
<?php namespace Tutorial; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\Router\Http\Segment; return [ 'controllers' => [ 'factories' => [Controller\TutorialController::class => InvokableFactory::class,], ], 'view_manager' => [ 'template_path_stack' => ['tutorial' => __DIR__ . '/../view',], ], ];
配置文件分为三部分,具体如下 −
控制器配置 − 指定模块内可用的控制器。
路由配置 − 指定模块中的控制器如何解析为 URL。
视图配置 − 指定与视图引擎相关的配置,例如视图的位置等。
在应用程序级配置文件 – myapp/config/modules.config.php 中配置 Tutorial 模块。
return ['Zend\Router', 'Zend\Validator', 'Application', 'Tutorial'];
通过在应用程序文件夹的根目录中执行 composer serve 来运行应用程序。
我们已经成功添加了一个新模块,但我们仍然需要添加 Controller、Routing 和 Views 才能成功运行 Tutorial 模块。
Zend Framework - 控制器
如前所述,控制器在 Zend MVC 框架中扮演着重要的角色。应用程序中的所有网页都需要由控制器处理。
在 Zend MVC 框架中,控制器是实现 Zend/Stdlib/DispatchableInterface 的对象。DispatchableInterface 有一个方法 dispatch,该方法获取 Request 对象作为输入,执行一些逻辑并返回 Response 对象作为输出。
dispatch(Request $request, Response $response = null)
以下是 Controller 对象返回"Hello World"的一个简单示例 −
use Zend\Stdlib\DispatchableInterface; use Zend\Stdlib\RequestInterface as Request; use Zend\Stdlib\ResponseInterface as Response; class HelloWorld implements DispatchableInterface { public function dispatch(Request $request, Response $response = null) { $response->setContent("Hello World!"); } }
DispatchableInterface 是基础接口,它需要很多其他接口来编写高级控制器。其中一些接口如下 −
InjectApplicationEventInterface − 用于注入事件 (Zend EventManager)
ServiceLocatorAwareInterface − 用于定位服务 (Zend ServiceManager)
EventManagerAwareInterface − 用于管理事件 (Zend EventManager)
考虑到这些因素,Zend Framework 提供了许多实现这些接口的现成控制器。最重要的控制器如下所述。
AbstractActionController
AbstractActionController (Zend/Mvc/Controller/AbstractActionController) 是 Zend MVC 框架中最常用的控制器。它具有编写典型网页所需的所有功能。它允许路由(路由是将请求 URL 匹配到控制器及其方法之一)匹配 action。匹配后,控制器将调用以操作命名的方法。
例如,如果路由 test 匹配,并且路由 test 返回 hello 作为操作,则将调用 helloAction 方法。
让我们使用 AbstractActionController 编写 TutorialController。
通过扩展 AbstractActionController 创建一个名为 TutorialController 的新 PHP 类,并将其放在 module/Tutorial/src/Controller/ 目录中。
将 Tutorial\Controller 设置为命名空间。
编写 indexAction方法。
从indexAction方法返回ViewModel对象。ViewModel对象用于将数据从控制器发送到视图引擎,我们将在后续章节中看到。
完整代码清单如下 −
?php namespace Tutorial\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class TutorialController extends AbstractActionController { public function indexAction() { return new ViewModel(); } }
我们已成功添加新的 TutorialController。
AbstractRestfulController
AbstractRestfulController (Zend\Mvc\Controller\AbstractRestfulController) 检查传入请求的 HTTP 方法,并通过考虑 HTTP 方法来匹配操作(方法)
例如,如果在请求中找到 id 参数,则带有 GET HTTP 方法的请求将匹配 getList() 方法或 get() 方法。
AbstractConsoleController
AbstractConsoleController (Zend\Mvc\Controller\AbstractConsoleController) 与 AbstractActionController 类似,只是它仅在控制台环境中运行,而不是在浏览器中运行。
Zend Framework - 路由
路由将请求 URI映射到特定控制器的方法。在本章中,我们将了解如何在 Zend Framework 中实现路由。
一般来说,任何 URI 都有三个部分 −
- 主机名段,
- 路径段,和
- 查询段。
例如,在 URI / URL − http://www.example.com/index?q=data 中,www.example.com 是主机名段,index 是路径段,q=data 是查询段。通常,路由会根据一组约束检查页面段。如果任何约束匹配,则返回一组值。其中一个主要值是控制器。
在特定情况下,路由还会检查主机段、查询段、请求 HTTP 方法、请求 HTTP 标头等。
Route & RouteStack
路由是路由中的主要对象。Zend Framework 为路由对象提供了一个特殊的接口 RouteInterface。所有路由对象都需要实现 RouteInterface。RouteInterface 的完整列表如下 −
namespace Zend\Mvc\Router; use Zend\Stdlib\RequestInterface as Request; interface RouteInterface { public static function factory(array $options = []); public function match(Request $request); public function assemble(array $params = [], array $options = []); }
主要方法是 match。此 match 方法根据其中定义的约束检查给定的请求。如果找到任何匹配项,它将返回 RouteMatch 对象。此 RouteMatch 对象将匹配请求的详细信息作为参数提供。可以使用 getParams 方法从 RouteObject 中提取这些参数。
RouteObject 的完整列表如下 −
namespace Zend\Mvc\Router; class RouteMatch { public function __construct(array $params); public function setMatchedRouteName($name); public function getMatchedRouteName(); public function setParam($name, $value); public function getParams(); public function getParam($name, $default = null); }
一般来说,典型的 MVC 应用程序有许多路由。每个路由将按 LIFO 顺序进行处理,并匹配和返回单个路由。如果没有路由匹配/返回,则应用程序返回"页面未找到"错误。Zend Framework 提供了一个接口来处理路由,RouteStackInterface。此 RouteStackInterface 具有添加/删除路由的选项。
RouteStackInterface 的完整列表如下 −
namespace Zend\Mvc\Router; interface RouteStackInterface extends RouteInterface { public function addRoute($name, $route, $priority = null); public function addRoutes(array $routes); public function removeRoute($name); public function setRoutes(array $routes); }
Zend 框架提供了 RouteStack 接口的两种实现,如下所示 −
- SimpleRouteStack
- TreeRouteStack
路由类型
Zend 框架为"Zend\Mvc\Router\Http"命名空间下的所有情况提供了大量现成的路由对象。选择并使用适合特定情况的路由对象就足够了。
可用的路由如下 −
Hostname − 用于匹配 URI 的主机部分。
Literal − 用于匹配精确 URI。
Method −用于匹配传入请求的 HTTP 方法。
Part − 用于使用自定义逻辑匹配 URI 路径段的一部分。
Regex − 用于通过 Regex Pattern 匹配 URI 路径段。
Schema − 用于匹配 URI Schema,例如 http、https 等。
Segment − 用于通过将 URI 路径拆分为多个段来匹配 URI 路径。
让我们看看如何编写最常用的文字和段路由。路由通常在每个模块的配置文件中指定 - module.config.php。
文字路由
通常,路由按 LIFO 顺序查询。文字路由用于对 URI 路径进行精确匹配。
其定义如下所示 −
$route = Literal::factory(array( 'route' => '/path', 'defaults' => array('controller' => 'Application\Controller\IndexController', 'action' => 'index',), ));
上述路由与请求 URL 中的 /path 匹配,并返回 index 作为 action 和 IndexController 作为控制器。
分段路由
只要您的 URL 应该包含可变参数,就使用分段路由。
描述如下 −
$route = Segment::factory(array( 'route' => '/:controller[/:action]', 'constraints' => array( 'controller' => '[a-zA-Z][a-zA-Z0-9_-]+', 'action' => '[a-zA-Z][a-zA-Z0-9_-]+', ), 'defaults' => array( 'controller' => 'Application\Controller\IndexController', 'action' => 'index',), ));
此处,段用冒号表示,后跟字母数字字符。如果保留一个段是可选的,则将其括在括号中。每个段可能具有与之关联的约束。每个约束都是一个正则表达式。
在教程模块中配置路由
让我们在教程模块中添加一个段路由。更新教程模块配置文件 - module.config.php,可在 myapp/module/Tutorial/config 中找到。
<?php namespace Tutorial; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\Router\Http\Segment; return [ 'controllers' => [ 'factories' => [ Controller\TutorialController::class => InvokableFactory::class, ], ], 'router' => [ 'routes' => [ 'tutorial' => [ 'type' => Segment::class, 'options' => [ 'route' => '/tutorial[/:action[/:id]]', 'constraints' => [ 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', 'id' => '[0-9]+', ], 'defaults' => [ 'controller' => Controller\TutorialController::class, 'action' => 'index', ], ], ], ], ], 'view_manager' => [ 'template_path_stack' => ['tutorial' => __DIR__ . '/../view',], ], ];
我们已成功为 Tutorial 模块添加了路由。我们距离完成 Tutorial 模块只差一步。我们需要为模块添加 View,我们将在后续章节中学习。
Zend Framework - 视图层
视图层是 MVC 应用程序的表示层。它将应用程序逻辑与表示逻辑分开。在典型的 PHP Web 应用程序中,所有业务逻辑和设计都是混合在一起的。混合可以加快小型项目的开发速度。但是,在涉及大量高级架构的大型项目中,混合会失败。要更改 Web 应用程序的设计,开发人员还需要处理业务逻辑。这可能会造成灾难性的后果,导致业务逻辑中断。
Zend Framework 提供了一个经过深思熟虑、干净、灵活且可扩展的视图层。视图层可作为单独的模块 Zend/View 使用,并与 Zend/Mvc 模块完美集成。 Zend View Layer 被分成多个组件,彼此之间可以很好地交互。
其各个组件如下 −
变量容器 − 保存视图层的数据。
视图模型 − 保存变量容器和设计模板。
渲染器 − 处理来自视图模型的数据和模板并输出设计表示,可能是最终的 html 输出。
解析器 − 以渲染器可以使用的方式解析视图模型中可用的模板。
视图 (Zend\View\View) −将请求映射到渲染器,然后将渲染器映射到响应。
渲染策略 − 由视图使用,将请求映射到渲染器。
响应策略 − 由视图使用,将渲染器映射到响应。
视图层 View 处理 ViewModel,使用 Resolver 解析模板,使用 Rendering Strategy 渲染模板,最后使用 Response Renderer 输出模板。
视图层配置
与控制器一样,视图层可以在名为 - module.config.php 的模块配置文件中进行配置。主要配置是指定模板的放置位置。这可以通过在"module.config.php"中添加以下配置来实现。
'view_manager' => [ 'template_path_stack' => ['tutorial' => __DIR__ . '/../view',], ]
默认情况下,View 层对其所有组件都有默认行为。例如,ViewModel 通过"lowercase-module-name/lowercase-controller-name/lowercase-action-name"规则解析模板根中控制器操作的模板名称。但是,这可以通过 ViewModel 的 setTemplate() 方法覆盖。
控制器和 View 层
默认情况下,控制器不需要向 View 层发送任何数据。将模板写在适当的位置就足够了。
例如,在我们的示例 TutorialController 中,模板需要放置在 myapp/module/Tutorial/view/tutorial/tutorial/index.phtml。index.phtml 引用基于 PHP 的模板,它将由 PHPRenderer 呈现。还有其他渲染器,例如用于 json 输出的 JsonRenderer 和用于 rss 和 atom 输出的 FeedRenderer。
完整列表如下 −
<?php namespace Tutorial\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class TutorialController extends AbstractActionController { public function indexAction() { } }
Zend 应用程序模板
<div class = "row content"> <h3>This is my first Zend application</h3> </div>
最后,我们成功完成了 Tutorial 模块,我们可以使用 url - http://localhost:8080/tutorial 访问它。
将数据传递到视图层
将数据发送到视图层的最简单方法是使用 ViewModel 参数。更改后的 indexAction 方法如下 −
public function indexAction() { $view = new ViewModel([ 'message' => 'Hello, Tutorial' ]); return $view; }
现在,按如下方式更改 index.phtml 文件 −
<div class = "row content"> <h3>This is my first Zend application</h3> <h4><?php echo $this->message?></h4> </div>
视图助手
视图助手用于编写模板中使用的小型原子函数。Zend 框架提供了一个接口 Zend\View\Helper\HelperInterface 来编写标准视图助手。
HelperInterface 只有两种方法,
setView() − 此方法接受 Zend\View\Renderer\RendererInterface 实例/实现。
getView() − 它用于检索该实例。
HelperInterface 的完整代码清单如下 −
namespace Zend\View\Helper; use Zend\View\Renderer\RendererInterface as Renderer; interface HelperInterface { /** * Set the View object * * @param Renderer $view * @return HelperInterface */ public function setView(Renderer $view); /** * Get the View object * * @return Renderer */ public function getView(); }
要在视图脚本中使用帮助程序,请使用 $this->helperName() 访问它。
内置帮助程序
Zend Framework 提供了许多内置帮助程序函数,可用于各种目的。 zend-mvc 中可用的部分视图助手如下 −
URL
URL 助手用于生成与应用程序中定义的路由匹配的 URL。
URL 助手的定义是 −
$this->url($name, $params, $options, $reuseMatchedParameters)
例如,在教程模块中,路由名为 tutorial,它有两个参数 action 和 id。我们可以使用 URL 助手生成两个不同的 URL,如下所示 −
<a href = "<? = $this->url('tutorial'); ?>">Tutorial Index</a> <a href = "<? = $this->url('tutorial', ['action' => 'show', 'id' =>10]); ?>"> Details of Tutorial #10 </a>
结果将如下所示 −
<a href = "/tutorial">Tutorial Index</a> <a href = "/tutorial/show/10"> Details of Tutorial #10</a>
占位符
占位符助手用于在视图脚本和视图实例之间持久保存内容。它提供了最初设置数据然后在后续阶段使用它的选项。
例如,我们可以设置 公司名称,然后在所有其他地方使用它。
<?php $this->placeholder('companyname')->set("TutorialsPoint") ?> <?= $this->placeholder('companyname'); ?>
占位符提供了一些高级选项,可以从 PHP 数组和对象生成复杂内容。它还可以选择捕获模板本身的某些部分。
例如,以下代码捕获中间的模板结果并将其存储在 productlist 占位符中。
类 - 产品
class Product { public $name; public $description; }
控制器
$p1 = new Product(); $p1->name = 'Car'; $p1->description = 'Car'; $p2 = new Product(); $p2->name = 'Cycle'; $p2->description = 'Cycle'; $view = new ViewModel(['products' => $products]);
模板
<!-- start capture --> <?php $this->placeholder('productlist')->captureStart(); foreach ($this->products as $product): ?> <div> <h2><?= $product->name ?></h2> <p><?= $product->description ?></p> </div> <?php endforeach; ?> <?php $this->placeholder('productlist')->captureEnd() ?> <!-- end capture --> <?= $this->placeholder('productlist') ?>
结果
<div class = "foo"> <h2>Car</h2> <p>Car</p> </div> <div class = "foo"> <h2>Cycle</h2> <p>Cycle</p> </div>
Doctype
Doctype 助手用于生成各种 html 文档类型。它是 Placeholder 助手的具体实现。可以在引导文件和配置文件中设置文档类型。
基本用法如下所示 −
应用程序引导文件
use Zend\View\Helper\Doctype; $doctypeHelper = new Doctype(); $doctypeHelper->doctype('XHTML5');
模块配置
// module/Application/config/module.config.php: return [ /* ... */ 'view_manager' => [ 'doctype' => 'html5', /* ... */ ], ];
模板
<?php echo $this->doctype() ?>
HeadTitle
HeadTitle 助手用于生成 html 标题元素。它是 Placeholder 助手的具体实现。Zend 提供了一个在模块配置文件中设置标题的选项,并且可以在任何级别设置,如站点、模块、控制器、操作等。HeadTitle 的部分代码如下 −
模块
headTitleHelper->append($action); $headTitleHelper->append($controller); $headTitleHelper->append($module); $headTitleHelper->append($siteName);
模板
<?= $this->headTitle() ?>
结果
action - controller - module - Zend Framework
HeadMeta
HeadMeta 助手用于生成 html 元标记。它是 Placeholder 助手的具体实现。
模板 −
<?php $this->headMeta()->appendName('keywords', 'turorialspoint, zend framework, php'); echo $this->headMeta() ?>
结果
<meta name = "keywords" content = "tutorialspoint, zend framework, php" />
HeadLink
HeadLink 帮助程序用于生成 html 链接以包含外部资源。它是 Placeholder 帮助程序的具体实现。
模板
<?php // 在视图脚本中设置链接: $this->headLink(['rel' => 'icon', 'href' => '/img/favicon.ico'], 'PREPEND') ->appendStylesheet('/styles/site.css') ->prependStylesheet('/styles/mystyle.css', 'screen', true, ['id' => 'mystyle']); // 从布局渲染链接: echo $this->headLink(); ?>
结果
<link href = "/styles/mystyle.css" media = "screen" rel = "stylesheet" type = "text/css" id = "mystyle"> <link href = "/img/favicon.ico" rel = "icon"> <link href = "/styles/site.css" media = "screen" rel = "stylesheet" type = "text/css">
HeadStyle
The HeadStyle helper is used to generate inline CSS styles. It is concrete implementation of the Placeholder helper.
模板
<?php $this->headStyle()->appendStyle($styles); ?> <?php echo $this->headStyle() ?>
HeadScript
HeadScript 用于生成内联脚本或包含外部脚本。它是 Placeholder 助手的具体实现。
模板
<? $this->headScript()->appendFile('/js/sample.js');?> <?php echo $this->headScript() ?>
InlineScript
InlineScript 用于在 html 模板的 head 和 body 部分生成脚本。它派生自 HeadScript。
HTMLList
HTMLList 用于生成有序和无序列表。HTMLList 的定义如下 −
定义
htmlList($items, $ordered, $attribs, $escape)
模板
$items = [ '2015', ['March', 'November'], '2016', ]; echo $this->htmlList($items);
结果
<ul> <li>2015 <ul> <li>March</li> <li>November</li> </ul> </li> <li>2016</li> </ul>
Cycle
Cycle 用于在循环环境中生成替代方案。它具有分配、下一个和上一个函数。
控制器
$view = new ViewModel(['message' => 'Hello, Tutorial', 'data' => array('One', 'Two')]);
模板
<?php $this->cycle()->assign(['#F0F0F0', '#FFF'], 'colors'); ?> <table> <?php foreach ($this->data as $datum): ?> <tr style = "background-color: <?= $this->cycle()->setName('colors')>next() ?>"> <td><?= $this->escapeHtml($datum) ?></td> </tr> <?php endforeach ?> </table>
结果
<table> <tr style = "background-color: #F0F0F0"> <td>One</td> </tr> <tr style = "background-color: #FFF"> <td>Two</td> </tr> </table>
其他几个重要的内置助手如下 −
BasePath − BasePath 用于生成应用程序根目录的公共文件夹的路径。
Partial − Partial 用于在其自己的变量范围内呈现特定模板。
PartialLoop − PartialLoop 与 Partial 类似,但用于循环环境。
Identity − Identity 用于从身份验证服务中检索已登录用户的身份。
JSON − JSON 用于 restful 环境中,输出为 JSON 格式。它发出正确的 HTTP 标头并禁用布局概念。
Zend Framework 中仍有许多可用的帮助程序,例如 i18n 帮助程序、表单帮助程序、分页帮助程序、导航帮助程序等。
创建视图帮助程序
Zend Framework 提供了一个内置的 AbstractHelper,它实现了 HelperInterface 来编写视图帮助程序。
编写新帮助程序所涉及的步骤如下 −
步骤 1 − 扩展 Zend\View\Helper\AbstractHelper 类。
步骤 2 −重写 __invoke() 函数。
步骤 3 − 在 module.config.php 文件 中设置配置。
步骤 4 − 在视图脚本中使用视图助手。
现在让我们创建一个 TestHelper
在 myapp/module/Tutorial/src/View 目录 创建 Helper 文件夹。在 Helper 目录中写入 TestHelper,即 TestHelper.php。
完整清单如下 −
<?php namespace Tutorial\View\Helper; use Zend\View\Helper\AbstractHelper; class TestHelper extends AbstractHelper { public function __invoke() { $output = "I am from test helper"; return htmlspecialchars($output, ENT_QUOTES, 'UTF-8'); } }
在 module.config.php 中设置配置。
'view_helpers' => [ 'aliases' => [ 'testHelper' => View\Helper\TestHelper::class, ], 'factories' => [ View\Helper\TestHelper::class => InvokableFactory::class, ], ],
在 about 视图脚本中使用新创建的 TestHelper。
<?= $this->testHelper() ?>
Zend Framework - 布局
布局表示多个视图的公共部分,例如页眉和页脚。默认情况下,布局应存储在 view/layout 文件夹中。
布局配置在 module.config.php 中的 view_manager 部分下定义。
骨架应用程序的默认配置如下 −
'view_manager' => array( 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => array( 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'application/index/index' => __DIR__ . '/../view/application/index/index.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ), 'template_path_stack' => array( __DIR__ . '/../view', ),
此处,template_map 用于指定布局。如果未找到布局,则将返回错误。让我们看一下骨架应用程序的主要布局。
Layout.phtml
<?= $this->doctype() ?> <html lang = "en"> <head> <meta charset = "utf-8"> <?= $this->headTitle('ZF Skeleton Application')->setSeparator(' - ')> setAutoEscape(false) ?> <?= $this->headMeta() ->appendName('viewport', 'width = device-width, initial-scale = 1.0') ->appendHttpEquiv('X-UA-Compatible', 'IE = edge') ?> <!-- Le styles --> <?= $this->headLink(['rel' => 'shortcut icon', 'type' => 'image/vnd.microsoft.icon', 'href' => $this->basePath() . '/img/favicon.ico']) ->prependStylesheet($this->basePath('css/style.css')) ->prependStylesheet($this->basePath('css/bootstraptheme.min.css')) ->prependStylesheet($this->basePath('css/bootstrap.min.css')) ?> <!-- Scripts --> <?= $this->headScript() ->prependFile($this->basePath('js/bootstrap.min.js')) ->prependFile($this->basePath('js/jquery-3.1.0.min.js')) ?> </head> <body> <nav class = "navbar navbar-inverse navbar-fixed-top" role = "navigation"> <div class = "container"> <div class = "navbar-header"> <button type = "button" class = "navbar-toggle" data- toggle = "collapse" data-target = ".navbar-collapse"> <span class = "icon-bar"></span> <span class = "icon-bar"></span> <span class = "icon-bar"></span> </button> <a class = "navbar-brand" href = "<?= $this->url('home') ?>"> <img src = "<?= $this->basePath('img/zf-logo-mark.svg') ?> " height = "28" alt = "Zend Framework <?= \Application\Module:: VERSION ?>"/> Skeleton Application </a> </div> <div class = "collapse navbar-collapse"> <ul class = "nav navbar-nav"> <li class = "active"><a href = "<?= $this->url('home') ?>">Home</a></li> </ul> </div> </div> </nav> <div class = "container"> <?= $this->content ?> <hr> <footer> <p>© 2005 - <?= date('Y') ?> by Zend Technologies Ltd. All rights reserved.</p> </footer> </div> <?= $this->inlineScript() ?> </body> </html>
当您分析布局时,它主要使用我们在上一章中讨论过的视图助手。仔细观察,布局使用了一个特殊变量 $this->content。此变量很重要,因为它将被实际请求页面的视图脚本(模板)替换。
创建新布局
让我们为我们的教程模块创建一个新布局。
首先,让我们在"public/css"目录下创建一个 tutorial.css 文件。
body { background-color: lightblue; } h1 { color: white; text-align: center; }
在 /myapp/module/Tutorial/view/layout/ 创建一个新布局文件 newlayout.phtml,并从现有布局中复制内容。然后,使用布局 head 部分内的 HeadLink 辅助类添加 tutorial.css 样式表。
<?php echo $this->headLink()->appendStylesheet('/css/tutorial.css');?>
使用 URL 帮助程序在导航部分添加新的 about 链接。
<li><a href = "<?= $this->url('tutorial', ['action' => 'about']) ?>">About</a></li>
此布局页面是教程模块应用程序的常用页面。更新教程模块配置文件的 view_manager 部分。
'view_manager' => array( 'template_map' => array( 'layout/layout' => __DIR__ . '/../view/layout/newlayout.phtml'), 'template_path_stack' => array('tutorial' => __DIR__ . '/../view',), )
在 TutorialController 中添加 aboutAction 函数。
public function aboutAction() { }
在 myapp/module/Tutorial/view/tutorial/tutorial/ 处添加 about.phtml,内容如下。
<h2>About page</h2>
现在,您已准备好最终运行应用程序 − http://localhost:8080/tutorial/about。
Zend Framework - 模型和数据库
在本章中,我们将讨论 Zend Framework 的各种模型和数据库。
Zend Framework 中的模型
模型定义应用程序的逻辑数据表示。例如,在购物车应用程序中,产品、客户、购物车和订单都是模型。它们定义其所包含实体的属性。模型的一些概念如下 −
控制器与模型通信并要求它们检索所需的信息。然后,控制器将检索到的信息传递给视图。最后,视图将模型呈现为用户可用的演示数据。
模型直接与视图交互的情况非常少见,但有时可能会发生。
模型可以相互通信,并且不是独立的。它们彼此之间存在关系。这些关系使控制器能够更轻松、更快速地获取信息,因为它不必与不同的模型交互;模型可以自己完成这些操作。
让我们看一个简单的模型 - MyModel
<?php namespace Tutorial\Model; class Book { public $id; public $author; public $title; }
Zend Framework 中的数据库
Zend Framework 提供了一个简单且功能丰富的类 Zend\Db\TableGateway\TableGateway,用于从数据库表中查找、插入、更新和删除数据。
让我们通过以下步骤了解如何通过 Zend Framework 中的 PHP 的 PDO 驱动程序连接 MySqlservice。
步骤 1:在 MySQL 中创建数据库
在本地 MySQL 服务器中创建数据库教程。我们可以使用 phpmyadmin 或任何其他 MySQL GUI 工具来实现此目的。让我们在命令提示符中使用 MySQL 客户端。连接到 mysql 服务器并运行以下命令来创建 tutorial 数据库。
create database tutorials
步骤 2:在 tutorials db 中创建表
现在让我们使用以下 SQL 命令在 tutorials db 中创建一个数据库 book。
use tutorials; CREATE TABLE book ( id int(11) NOT NULL auto_increment, author varchar(100) NOT NULL, title varchar(100) NOT NULL, PRIMARY KEY (id) );
步骤 3:在 book 表中填充数据
使用示例数据填充 book 表。使用以下 SQL 命令。
INSERT INTO book (author, title) VALUES ('Dennis Ritchie', 'C Programming'); INSERT INTO book (author, title) VALUES ('James gosling', 'Java Programming'); INSERT INTO book (author, title) VALUES ('Rasmus Lerdorf', 'Programming PHP');
步骤 4:更新数据库连接
使用必要的数据库驱动器信息更新全局配置文件 – myapp/config/autoload/global.php。
<?php return array( 'db' => array( 'driver' => 'Pdo', 'dsn' => 'mysql:dbname = tutorials;host = localhost', 'driver_options' => array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ), ), 'service_manager' => array( 'factories' => array( 'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory', ), ), );
步骤 5:更新数据库凭据
在本地配置文件中更新数据库凭据,即 – myapp/config/autoload/local.php。这样,我们可以分离本地和实时数据库连接凭据。
<?php return array( 'db' => array( 'username' => '<user_name>', 'password' => '<password>', ), );
步骤 6:为 Book 创建模型
让我们在模块 src 目录中创建一个模型 Book。通常,模型分组在 Model 文件夹下 - /myapp/module/Tutorial/src/Model/Book.php。
<?php namespace Tutorial\Model; class Book { public $id; public $author; public $title; }
步骤 7:在 book 模型中实现 exchangeArray
TableGateway 通过 exchangeArray 函数与模型交互。exchangeArray 函数的标准参数是存储为 PHP 数组的数据库结果集。使用 exchangeArrayfunction,模型的属性可以轻松与相应的数据库表同步。
更新模型 Book,如下所示 −
<?php namespace Tutorial\Model; class Book { public $id; public $author; public $title; public function exchangeArray($data) { $this->id = (!empty($data['id'])) ? $data['id'] : null; $this->Author = (!empty($data['author'])) ? $data['author'] : null; $this->Title = (!empty($data['title'])) ? $data['title'] : null; } }
步骤 8:使用 TableGateway 获取书籍
创建一个类 BookTable,从数据库获取书籍信息。在 Model 文件夹本身中创建类 BookTable。
<?php namespace Tutorial\Model; use Zend\Db\TableGateway\TableGatewayInterface; class BookTable { protected $tableGateway; public function __construct(TableGatewayInterface $tableGateway) { $this->tableGateway = $tableGateway; } public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } }
我们使用 TableGateway 类的 select() 方法从数据库中获取图书信息。但是,我们在代码中没有使用对表 - book 的任何引用。TableGateway 本质上是通用的,它可以通过使用某些配置从任何表中获取数据。通常,这些配置是在 module.config.php 文件中完成的,我们将在后续步骤中讨论该文件。
步骤 9:配置 BookTable 类
使用 getServiceConfig() 方法更新教程模块 Module.php。
<?php namespace Tutorial; use Zend\Db\Adapter\AdapterInterface; use Zend\Db\ResultSet\ResultSet; use Zend\Db\TableGateway\TableGateway; use Zend\ModuleManager\Feature\ConfigProviderInterface; class Module implements ConfigProviderInterface { public function getConfig() { return include __DIR__ . '/../config/module.config.php'; } public function getServiceConfig() { return [ 'factories' => [ Model\BookTable::class => function ($container) { $tableGateway = $container->get(Model\BookTableGateway::class); $table = new Model\BookTable($tableGateway); return $table; }, Model\BookTableGateway::class => function ($container) { $dbAdapter = $container->get(AdapterInterface::class); $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Model\Book()); return new TableGateway('book', $dbAdapter, null, $resultSetPrototype); }, ], ]; } }
在这里,我们使用服务管理器注册了 BookTable 类。BookTable 类用于获取图书信息,通过注册,我们可以在任何需要的地方访问它。由于注册的服务是共享的,因此它们可以提高性能、减少内存消耗等。
另一项 Model\BookTableGateway::class 是专门用于 Book 模型的 TableGateway 对象,是 BookTable 的依赖项。
步骤 10:更新 TutorialController 配置
我们需要教程控制器中的 BookTable 服务来获取图书信息。要获取 BookTable 服务,请将其注册为 TutorialController 中的构造函数依赖项。
此构造函数依赖项有助于在控制器本身处于初始化阶段时获取 BookTable 服务。更新教程模块配置 module.config.php 的控制器部分,如下所示。
'controllers' => [ 'factories' => [ Controller\TutorialController::class => function($container) { return new Controller\TutorialController( $container->get(Model\BookTable::class) ); }, ], ],
步骤 11:更新教程控制器
这可以通过遵循以下三个步骤来完成。
- 添加以 BookTable 作为参数的构造函数。
private $table; public function __construct(BookTable $table) { $this->table = $table; }
使用 BookTable 的 fetchAll() 方法获取图书信息并将其注册到视图中。
public function indexAction() { $view = new ViewModel([ 'data' => $this->table->fetchAll(), ]); return $view; }
在视图脚本中显示图书信息。
<table class = "table"> <tr> <th>Author</th> <th>Title</th> <th> </th> </tr> <?php foreach ($data as $sampledata) : ?> <tr> <td><?php echo $this->escapeHtml($data->author);?></td> <td><?php echo $this->escapeHtml($data->title);?></td> </tr> <?php endforeach ?> </table>
步骤 12:运行应用程序
通过运行 − http://localhost:8080/tutorial 检查应用程序。
Zend Framework - 不同的数据库
如上一章所述,Zend 框架提供了一种使用 数据库驱动程序 概念访问数据库的通用方法。使用数据库完全取决于驱动程序信息,因此,连接不同的数据库只需更改驱动程序信息即可。
现在让我们按照以下步骤更改 book 示例以连接到 postgresql 数据库。
步骤 1 − 使用以下命令在本地 postgresql 数据库中创建数据库、教程 −
CREATE DATABASE tutorials
步骤 2 − 添加 book 表。移至新数据库并执行表创建脚本。
\c tutorials CREATE TABLE book ( id SERIAL NOT NULL, author varchar(100) NOT NULL, title varchar(100) NOT NULL, PRIMARY KEY (id) );
步骤 3 − 使用以下脚本添加示例图书信息 −
INSERT INTO book (author, title) VALUES ('Dennis Ritchie', 'C Programming'); INSERT INTO book (author, title) VALUES ('James gosling', 'Java Programming'); INSERT INTO book (author, title) VALUES ('Rasmus Lerdorf', 'Programming PHP');
步骤 4 − 更改 global.config 文件中的驱动程序信息。
<?php return array ( 'db' => array ( 'driver' => 'Pdo', 'dsn' => 'pgsql:dbname = tutorials;host = localhost', 'driver_options' => array ( ), ), );
步骤 5 − 更改 local.config 文件中的数据库凭据。
return array ( 'db' => array( 'username' => '<username>', 'password' => '<password>', ), );
步骤 6 − 最后,运行应用程序 http://localhost:8080/tutorial。结果与 MySQL 应用程序相同。
Zend Framework - 表单和验证
Zend Framework 提供了一个单独的组件 zend-form,以加速表单创建和验证过程。它连接模型和视图层。它提供了一组表单元素,用于从预定义模型创建完整的 html 表单,一个 InputFilter 类,用于根据表单验证模型,以及将表单中的数据绑定到模型和反之亦然的选项。
安装表单组件
可以使用 Composer 命令安装 Zend 表单组件,如下所示 −
composer require zendframework/zend-form
Zend 表单框架有三个子组件来管理表单。它们如下详细解释 −
元素 − 用于定义映射到模型中属性的单个 html 输入控件。
字段集 − 用于以嵌套方式对元素和其他 字段集 进行分组。
表单 − 用于创建 html 表单,由元素和字段集组成。
Zend 表单通常在 module//src/Form 目录下创建。
示例
现在让我们创建一个简单的表单,将 book 添加到数据库中。为此,我们应遵循以下步骤 −
步骤 1:创建 BookForm
在 *myapp/module/Tutorial/src/Form" 目录下创建"BookForm.php"。在文件中添加以下更改 −
<?php namespace Tutorial\Form; use Zend\Form\Form; class BookForm extends Form { public function __construct($name = null) { parent::__construct('book'); $this->add(array( 'name' => 'id', 'type' => 'Hidden', )); $this->add(array( 'name' => 'author', 'type' => 'Text', 'options' => array( 'label' => 'Author', ), )); $this->add(array( 'name' => 'title', 'type' => 'Text', 'options' => array( 'label' => 'Title', ), )); $this->add(array( 'name' => 'submit', 'type' => 'Submit', 'attributes' => array( 'value' => 'Go', 'id' => 'submitbutton', ), )); } }
Form 类提供 add 方法 来映射模型及其相应的表单详细信息。我们通过扩展 Form 类创建了 BookForm,并为 Book 模型添加了表单详细信息。
步骤 2:更新书籍模型 Book.php
使用下面指定的过滤器和验证更新模型 'Book' −
<?php namespace Tutorial\Model; use Zend\InputFilter\InputFilterInterface; use Zend\InputFilter\InputFilterAwareInterface; use Zend\InputFilter\InputFilter; class Book implements InputFilterAwareInterface { public $id; public $author; public $title; protected $inputFilter; public function setInputFilter(InputFilterInterface $inputFilter) { throw new \Exception("Not used"); } public function getInputFilter() { if (!$this->inputFilter) { $inputFilter = new InputFilter(); $inputFilter->add(array( 'name' => 'id', 'required' => true, 'filters' => array( array('name' => 'Int'), ), )); $inputFilter->add(array( 'name' => 'author', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), )); $inputFilter->add(array( 'name' => 'title', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), )); $this->inputFilter = $inputFilter; } return $this->inputFilter; } public function exchangeArray($data) { $this->id = (!empty($data['id'])) ? $data['id'] : null; $this->author = (!empty($data['author'])) ? $data['author'] : null; $this->title = (!empty($data['title'])) ? $data['title'] : null; } }
每个模型都应该实现InputFilterAwareInterface。InputFilterAwareInterface 提供了两个方法,setInputFilter() 和 getInputFilter()。
getInputFilter 用于获取模型的验证详细信息。Zend 框架提供了一组丰富的过滤器和验证器来验证表单。书籍模型中使用的一些过滤器和验证器如下 −
StripTags − 删除不需要的 HTML。
StringTrim − 删除不必要的空格。
StringLength 验证器 −确保用户输入的字符数不超过指定限制。
步骤 3:更新 BookTable 类
包含 saveBook 方法以将书籍添加到数据库。
BookTable.php
<?php namespace Tutorial\Model; use Zend\Db\TableGateway\TableGatewayInterface; class BookTable { protected $tableGateway; public function __construct(TableGatewayInterface $tableGateway) { $this->tableGateway = $tableGateway; } public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } public function getBook($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array('id' => $id)); $row = $rowset->current(); if (!$row) { throw new \Exception("Could not find row $id"); } return $row; } public function saveBook(Book $book) { $data = array ( 'author' => $book->author, 'title' => $book->title, ); $id = (int) $book->id; if ($id == 0) { $this->tableGateway->insert($data); } else { if ($this->getBook($id)) { $this->tableGateway->update($data, array('id' => $id)); } else { throw new \Exception('Book id does not exist'); } } } }
步骤 4:更新 TutorialController 类
在教程控制器中添加新操作 addAction – myapp/module/Tutorial/src/Controller/TutorialController.php。
public function addAction() { $form = new BookForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if ($request->isPost()) { $book = new Book(); $form->setInputFilter($book->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { $book->exchangeArray($form->getData()); $this->bookTable->saveBook($book); // 重定向至教程列表 return $this->redirect()->toRoute('tutorial'); } } return array('form' => $form); }
addAction 方法执行以下过程 −
获取请求对象。
检查请求的 http 方法是否为 post 方法。
如果请求的 http 方法不是 post,则仅呈现模板 add.phtml
如果请求的 http 方法不是 post,则设置 inputfilter,获取请求数据并将其设置到输入文件中。
使用 Form 类的 isValid() 方法检查表单是否有效。
如果表单无效,则再次呈现模板, add.phtml
如果表单有效,它会将书籍保存到数据库并重定向到主页。
步骤 5:添加 add.phtml 模板
在 myapp/module/Tutorial/view/tutorial/tutorial/add.phtml 下创建一个模板 – add.phtml
Add.phtml
<?php $title = 'Add new Book'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php if(!empty($form)) { $form->setAttribute('action', $this->url('tutorial', array('action' => 'add'))); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get('id')); echo $this->formRow($form->get('author'))."<br>"; echo $this->formRow($form->get('title'))."<br>"; echo $this->formSubmit($form->get('submit')); echo $this->form()->closeTag(); }
在这里,我们使用 Form 实例 $form 呈现书籍表单。
步骤 6:运行应用程序
现在,我们可以运行应用程序 - http://localhost:8080/tutorial/add。
表单页面
验证错误页面
Zend Framework - 文件上传
文件上传是表单编程中的主要概念之一。Zend 框架通过 zend-form 和 zend-inputfilter 组件提供上传文件所需的所有项目。
FileInput 类
zend-inputfilter 组件提供 Zend\InputFilter\FileInput 类来处理 html 文件输入元素 - <input type = 'file' />。FileInput 与其他输入过滤器类似,但有一些例外。它们如下 −
由于 PHP 将上传文件的详细信息保存在 $_FILES 全局数组中,因此 FileInput 仅通过 $_FILES 收集上传文件的信息。
在 FileInput 类处理数据之前需要进行验证。它与其他输入过滤器的行为相反。
Zend\Validator\File\UploadFile 是默认使用的验证器。UploadFile 验证文件输入的详细信息。
要在表单中添加文件上传类型,我们需要使用输入类型 File。部分代码如下 −
$form->add(array( 'name' => 'imagepath', 'type' => 'File', 'options' => array('label' => 'Picture',), ));
文件上传中使用的另一个类是 Zend\Filter\File\RenameUpload。RenameUpload 用于将上传的文件移动到我们想要的位置。使用文件过滤器的部分类如下 −
$file = new FileInput('imagepath'); $file->getValidatorChain()->attach(new UploadFile()); $file->getFilterChain()->attach( new RenameUpload([ 'target' => './public/tmpuploads/file', 'randomize' => true, 'use_upload_extension' => true ])); $inputFilter->add($file);
这里RenameUpload的选项如下 −
target − 上传文件的目标路径。
randomize − 添加随机字符串,防止上传文件重复。
use_upload_extension −将上传文件的文件扩展名附加到目标。
文件上传 - 工作示例
让我们修改教程模块并包含图片上传功能。
修改数据库表
让我们通过执行以下 SQL 命令 − 将 imagepath 列添加到 book 表
ALTER TABLE `book` ADD `imagepath` VARCHAR(255) NOT NULL AFTER 'imagepath';
更新 BookForm.php
添加文件输入元素以在书籍表单中上传图片 - myapp/module/Tutorial/src/Model/BookForm.php。
在 BookForm 类的 __constructmethod 中包含以下代码。
$this->add(array( 'name' => 'imagepath', 'type' => 'File', 'options' => array ('label' => 'Picture',), ));
更新 Book.php
在 Book 类中执行以下更改 – myapp/module/Tutorial/src/Model/Book.php。
为图片添加新属性 imagepath。
public $imagepath;
更新 getInputFilter 方法,如下所示 −
为文件输入元素添加 FileInput 过滤器。
设置 UploadFile 验证以验证文件输入元素。
配置 RenameUpload 以将上传的文件移动到正确的目标位置。
部分代码清单如下 −
$file = new FileInput('imagepath'); $file->getValidatorChain()->attach(new UploadFile()); $file->getFilterChain()->attach( new RenameUpload([ 'target' => './public/tmpuploads/file', 'randomize' => true, 'use_upload_extension' => true ])); $inputFilter->add($file);
更新 exchangeArray 方法以包含 imagepath 属性。imagepath 可能来自表单或数据库。如果 imagepath 来自表单,则格式将为具有以下规范的数组 −
array(1) { ["imagepath"] => array(5) { ["name"] => string "myimage.png" ["type"] => string "image/png" ["tmp_name"] => string "public/tmpuploads/file_<random_string>.<image_ext>" ["error"] => int <error_number> ["size"] => int <size> } }
如果图像路径来自数据库,它将是一个简单的字符串。解析图像路径的部分代码清单如下 −
if(!empty($data['imagepath'])) { if(is_array($data['imagepath'])) { $this->imagepath = str_replace("./public", "", $data['imagepath']['tmp_name']); } else { $this->imagepath = $data['imagepath']; } } else { $data['imagepath'] = null; }
Book 模型的完整列表如下 −
<?php namespace Tutorial\Model; use Zend\InputFilter\InputFilterInterface; use Zend\InputFilter\InputFilterAwareInterface; use Zend\Filter\File\RenameUpload; use Zend\Validator\File\UploadFile; use Zend\InputFilter\FileInput; use Zend\InputFilter\InputFilter; class Book implements InputFilterAwareInterface { public $id; public $author; public $title; public $imagepath; protected $inputFilter; public function setInputFilter(InputFilterInterface $inputFilter) { throw new \Exception("Not used"); } public function getInputFilter() { if (!$this->inputFilter) { $inputFilter = new InputFilter(); $inputFilter->add(array( 'name' => 'id', 'required' => true, 'filters' => array( array('name' => 'Int'), ), )); $inputFilter->add(array( 'name' => 'author', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), )); $inputFilter->add(array( 'name' => 'title', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), )); $file = new FileInput('imagepath'); $file->getValidatorChain()->attach(new UploadFile()); $file->getFilterChain()->attach( new RenameUpload([ 'target' => './public/tmpuploads/file', 'randomize' => true, 'use_upload_extension' => true ])); $inputFilter->add($file); $this->inputFilter = $inputFilter; } return $this->inputFilter; } public function exchangeArray($data) { $this->id = (!empty($data['id'])) ? $data['id'] : null; $this->author = (!empty($data['author'])) ? $data['author'] : null; $this->title = (!empty($data['title'])) ? $data['title'] : null; if(!empty($data['imagepath'])) { if(is_array($data['imagepath'])) { $this->imagepath = str_replace("./public", "", $data['imagepath']['tmp_name']); } else { $this->imagepath = $data['imagepath']; } } else { $data['imagepath'] = null; } } }
更新 BookTable.php
我们已经更新了 BookForm 和 Book 模型。现在,我们更新 BookTable 并修改 saveBook 方法。这足以将 imagepath 条目包含在数据数组 $data 中。
部分代码清单如下 −
$data = array('author' => $book->author, 'title' => $book->title, 'imagepath' => $book->imagepath );
BookTable 类的完整代码清单如下 −
<?php namespace Tutorial\Model; use Zend\Db\TableGateway\TableGatewayInterface; class BookTable { protected $tableGateway; public function __construct(TableGatewayInterface $tableGateway) { $this->tableGateway = $tableGateway; } public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } public function getBook($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array('id' => $id)); $row = $rowset->current(); if (!$row) { throw new \Exception("Could not find row $id"); } return $row; } public function saveBook(Book $book) { $data = array ( 'author' => $book->author, 'title' => $book->title, 'imagepath' => $book->imagepath ); $id = (int) $book->id; if ($id == 0) { $this->tableGateway->insert($data); } else { if ($this->getBook($id)) { $this->tableGateway->update($data, array('id' => $id)); } else { throw new \Exception('Book id does not exist'); } } } }
更新 TutorialController.php 中的 addAction:文件上传信息将在 $_FILES 全局数组中可用,可以使用 Request 的 getFiles() 方法访问。因此,合并已发布的数据和文件上传信息,如下所示。
$post = array_merge_recursive( $request->getPost()->toArray(), $request->getFiles()->toArray() );
addAction() 方法的完整列表如下 −
public function addAction() { $form = new BookForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if ($request->isPost()) { $book = new Book(); $form->setInputFilter($book->getInputFilter()); $post = array_merge_recursive( $request->getPost()->toArray(), $request->getFiles()->toArray() ); $form->setData($post); if ($form->isValid()) { $book->exchangeArray($form->getData()); $this->bookTable->saveBook($book); // 重定向到教程列表 return $this->redirect()->toRoute('tutorial'); } } return array('form' => $form); }
更新 add.phtml 的视图
最后,更改"add.phtml"并包含 imagepath 文件输入元素,如下所示 −
echo $this->formRow($form->get('imagepath'))."<br>";
完整清单如下 −
<?php $title = 'Add new Book'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php if(!empty($form)) { $form->setAttribute('action', $this->url('tutorial', array('action' => 'add'))); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get('id')); echo $this->formRow($form->get('author'))."<br>"; echo $this->formRow($form->get('title'))."<br>"; echo $this->formRow($form->get('imagepath'))."<br>"; echo $this->formSubmit($form->get('submit')); echo $this->form()->closeTag(); }
运行应用程序
最后,在 http://localhost:8080/tutorial/add 运行应用程序并添加新记录。
结果将如以下屏幕截图所示 −
表单页面
索引页
Zend Framework - Ajax
AJAX 是 Web 编程中的一项现代技术。它提供了在网页中异步发送和接收数据的选项,而无需刷新页面。Zend 框架通过 zend-view 和 zend-json 组件提供了使用 json 模型的选项。让我们在本章中学习 Zend AJAX 编程。
安装 json 组件
可以使用 Composer 命令安装 Zend json 组件,如下所示 −
composer require zendframework/zend-json
概念
Zend 框架提供了两种方法来轻松编写支持 AJAX 的 Web 应用程序。它们如下 −
Request 对象中的 isXmlHttpRequest() 方法 – 如果发出 AJAX 请求,请求对象的 isXmlHttpRequest() 方法返回 true,否则返回 false。此方法用于在服务器端正确处理 AJAX 请求。
if ($request->isXmlHttpRequest()) { // Ajax 请求 } else { // 正常请求 }
Zend/View/Model/JsonModel – JsonModel 是 ViewModel 的替代方案,专门用于 AJAX 和 REST API 场景。 JsonModel 和 JsonStrategy(将在模块的视图管理器块中配置)将模型数据编码为 Json 并将其作为响应而不是视图 (phtml) 返回。
AJAX – 工作示例
让我们在教程模块中添加一个新的 ajax 页面 ajax 并异步获取书籍信息。为此,我们应该遵循以下步骤。
步骤 1:在模块配置中添加 JsonStrategy
更新教程模块配置文件中的视图管理器块 – myapp/module/Tutorial/config/module.config.php。然后,JsonStrategy 将与 JsonModel 一起编码并发送 json 数据。
'view_manager' => [ 'template_map' => array ('layout/layout' => __DIR__ . '/../view/layout/newlayout.phtml'), 'template_path_stack' => [ 'tutorial' => __DIR__ . '/../view', ], 'strategies' => array('ViewJsonStrategy',), ],
步骤 2:在 TutorialController.php 中添加 ajaxAction 方法
在 TutorialController.php 中添加 ajaxAction 方法,代码如下 −
public function ajaxAction() { $data = $this->bookTable->fetchAll(); $request = $this->getRequest(); $query = $request->getQuery(); if ($request->isXmlHttpRequest() || $query->get('showJson') == 1) { $jsonData = array(); $idx = 0; foreach($data as $sampledata) { $temp = array( 'author' => $sampledata->author, 'title' => $sampledata->title, 'imagepath' => $sampledata->imagepath ); $jsonData[$idx++] = $temp; } $view = new JsonModel($jsonData); $view->setTerminal(true); } else { $view = new ViewModel(); } return $view; }
此处,ajaxAction 将检查传入请求是否为 AJAX。如果传入请求为 AJAX,则将创建 JsonModel。否则,将创建普通的 ViewModel。
在这两种情况下,书籍信息都将从数据库中提取并填充到模型中。如果模型是 JsonModel,则将调用 JsonStrategy,并将数据编码为 json 并作为响应返回。
$query->get('showJson') == 1 用于调试目的。只需在 url 中添加 showJson=1,页面就会显示 json 数据。
步骤 3:添加 ajax.phtml
现在,为 ajaxAction 方法添加视图脚本 ajax.phtml。此页面将有一个带有标签的链接 - 加载书籍信息。
单击该链接将执行 AJAX 请求,该请求将以 Json 数据的形式获取书籍信息,并以格式化的表格形式显示书籍信息。AJAX 处理是使用 JQuery 完成的。
完整的代码清单如下 −
<a id = "loadbook" href = "#">Load book information</a> </br> </br> <table class = "table"> <tbody id = "book"> </tbody> </table> <script language = "javascript"> $(document).ready(function(){ $("#loadbook").on("click", function(event){ $.ajax({ url: '/tutorial/ajax', type: 'POST', dataType: 'json', async: true, success: function(data, status) { var e = $('<tr><th>Author</th><th>Title</th><th>Picture</th></tr>'); $('#book').html(''); $('#book').append(e); for(i = 0; i < data.length; i++) { book = data[i]; var e = $('<tr><td id = "author"></td><td id = "title"></td> <td id="imagepath"><img src = ""/></td></tr>'); $('#author', e).html(book['author']); $('#title', e).html(book['title']); $('#imagepath img', e).attr('src', book['imagepath']); $('#book').append(e); } }, error : function(xhr, textStatus, errorThrown) { alert('Ajax request failed.'); } }); }); }); </script>
步骤 4:运行应用程序
最后,运行应用程序 − http://localhost:8080/tutorial/ajax 并单击"加载图书信息"链接。
结果将如下所示 −
Ajax 页面 −
包含图书信息的 Ajax 页面
包含调试信息的 Ajax 页面
Zend 框架 - Cookie 管理
Cookie 是 Web 应用程序中非常重要的概念。它提供了在有限时间内保留用户数据(通常是浏览器本身中的一小段信息)的选项。
Cookie 用于维护 Web 应用程序的状态。Zend 框架在 zend-http 组件内提供了一个 cookie 模块。此 zend-http 提供了 HTTP 抽象及其实现。
安装 HTTP 组件
可以使用 Composer 轻松安装 HTTP 组件,如以下代码中所示。
composer require zendframework/zend-http
概念
zend-http 提供了 Zend\Http\Cookies 类来管理 cookie。它与 Zend\Http\Client 类一起使用,用于向 Web 服务器发送请求。可以按照以下代码所示初始化 Cookie −
use Zend\Http\Cookies $c = new Cookies();
当 HTTP 客户端 (Zend\Http\Client) 首次向 Web 服务器发送 URI 请求时,它没有任何 Cookie。一旦 Web 服务器收到请求,它将 Cookie 作为 HTTP Header, Set-Cookie 包含在其响应对象中,并将其发送到 HTTP 客户端。HTTP 客户端将从 http 响应中提取 Cookie,并在后续请求中将其作为相同的 HTTP Header 重新发送。通常,每个 cookie 都会映射到一个域和该域的一个路径。
Cookies 类中可用的方法如下 −
addCookie(uri) − 用于将 cookie 添加到给定 URI 的请求对象中。
getCookie(cookieName, $cookieForm) − 用于获取给定 URI $uri 中可用的 cookie $cookieName。第三个参数是 cookie 的返回方式,可以是字符串或数组。
fromResponse(uri) − 用于从给定 URI 的响应对象中提取 cookie。
addCookiesFromResponse −它与 fromResponse 相同,但它将其提取并再次添加到给定 URI 的请求对象中。
isEmpty() − 它用于查找给定的 Cookie 对象是否有任何 cookie。
reset() − 它用于清除给定 URI 中的所有 cookie。
在下一章中,我们将讨论 Zend Framework 中的会话管理。
Zend Framework - 会话管理
会话是 Web 应用程序中非常重要的概念。它提供了在有限时间内将用户数据保留在 Web 服务器中的选项。Zend Framework 提供了一个单独的组件 zend-session 来处理会话信息。
安装会话组件
可以使用 Composer 安装会话组件,如下所示 −
composer require zendframework/zend-session
会话组件
Zend Framework 提供了六个组件来处理会话管理。所有这些组件都已在下面进行了说明 −
Zend\Session\Container −读取和写入会话信息的主要 API。
Zend\Session\SessionManager − 它用于管理会话的整个生命周期。
Zend\Session\Storage − 它用于指定会话数据将如何存储在内存中。
Zend\Session\SaveHandler − 它用于将会话数据存储和检索到物理位置,如 RDBMS、Redis、MangoDB 等。
Zend\Session\Validator − 它用于通过交叉检查初始和后续请求的远程地址和用户代理来保护会话免遭劫持。
Zend\Session\Config\SessionConfig −它用于配置会话的行为方式。
默认配置足以处理会话。使用上述组件,可以轻松处理会话的各个方面。
会话组件示例
让我们遵循以下几点来创建一个新页面,以了解 Zend 框架中的会话。默认情况下,创建一个 Container 类的实例来管理会话就足够了。
在 TutorialController 中创建一个新操作 sessionAction。
初始化 Container 对象。
$c = new Container();
检查任意键 count 是否存在。如果密钥不可用,则用值 1 初始化 count。如果可用,则按以下代码所示增加该值。
if (!isset($c->count)) { $c->count = 0; } else { $c->count++; }
在 ViewModel 中注册 count。
在 myapp/module/Tutorial/view/tutorial/tutorial/session.phtml 中为 sessionAction、session.phtml 创建模板文件,然后渲染 count 值。
刷新页面将增加 session 中 count 的值。完整列表如下 −
TutorialController.php
public function sessionAction() { $c = new Container(); if (!isset($c->count)) { $c->count = 0; } else { $c->count++; } $view = new ViewModel([ 'count' => $c->count, ]); return $view; }
session.pthml
Session data, COUNT = <?= $this->count ?>
示例结果
Session data, Count = 5
Zend Framework - 身份验证
身份验证是任何 Web 应用程序中最重要的必备功能之一。Zend Framework 提供了一个单独的组件来处理身份验证,该组件称为 zend-authentication。
安装身份验证组件
可以使用以下 Composer 命令安装身份验证组件。
composer require zendframework/zend-authentication
概念
通常,开发人员会编写一个 php 函数来根据数据源验证用户详细信息。身份验证完成后,身份验证详细信息将保留以供后续请求使用。 Zend Framework 概括了这个概念并提供了两个类,下面将对此进行解释 −
类 1 Zend\Authentication\Adaptor\AdaptorInterface
此类提供了一个方法 authenticate 来编写身份验证逻辑。authenticate 方法返回 Zend\Authentication\Result 类的实例。
此 Result 对象保存身份验证状态;如果身份验证成功,则保存身份;如果身份验证失败,则保存错误消息。authenticate 接口和结果类的签名如下 −
AdaptorInterface
namespace Zend\Authentication\Adaptor; public function authenticate() { // code }
结果类
namespace Zend\Authentication; class Result { public function __construct($code, $identity, array $messages = []); }
Zend Framework 提供了一个默认实现,用于根据数据库、ldap、http basic 和 digest 凭据进行身份验证。Adaptor 进行身份验证,但不会为任何未来请求保留详细信息。
Class 2 Zend\Authentication\AuthenticationService
AuthenticationService 是主要组件,它使用已配置的适配器进行身份验证。身份验证完成后,它会保留身份验证详细信息并提供方法,hasIdentity() 用于检查身份是否可用,getIdentity() 用于获取身份验证详细信息,clearIdentity() 用于清除身份验证详细信息。
使用此 AuthenticationService 的部分代码清单如下 −
$adap = new Adapter($username, $password); $auth = new AuthenticationService(); $result = $auth->authenticate($adap); if($result->isValid) { $identity = $auth->getIdentity(); } else { // process $result->getMessages() } // clear $auth->clearIdentity();
与授权相关的内容被打包为两个单独的模块,分别是 - zend-permissions-acl 和 zend-permissions-rbac。zend-permissions-acl 基于访问控制列表,而 zend-permissions-rbac 基于基于角色的访问控制列表。它们提供 ACL 和 RBAC 概念的高级抽象,并有助于编写企业级应用程序。
Zend Framework - 电子邮件管理
Zend Framework 提供了一个名为 zend-mail 的独立组件来发送电子邮件。zend-mail 组件还提供了读取和写入带有附件的电子邮件消息(文本和 html 格式)的选项。在 Zend 中发送电子邮件配置起来更加容易和简单。
在本章中,我们将介绍电子邮件概念、基本设置、高级设置(如 SMTP 传输等)。
安装邮件组件
可以使用以下 Composer 命令安装邮件组件。
composer require zendframework/zend-mail
基本电子邮件配置
基本电子邮件由一个或多个收件人、主题、正文和发件人组成。 Zend 提供了 Zend\Mail\Message 类来创建新的电子邮件消息。要使用 zend-mail 发送电子邮件,您必须指定至少一个收件人以及邮件正文。
创建新邮件消息的部分代码如下 −
use Zend\Mail; $mail = new Mail\Message(); $mail->setSubject('Zend email sample'); $mail->setBody('This is content of the mail message'); $mail->setFrom('sender@example.com', "sender-name"); $mail->addTo('recipient@test.com', "recipient-name");
Zend 提供了 Zend\Mail\Sendmail 类来发送邮件消息。Sendmail 使用 php 原生邮件功能 mail 发送邮件消息,我们可以使用 php 配置文件配置传输层。
使用 Sendmail 的部分编码如下 −
$transport = new Mail\Transport\Sendmail(); $transport->send($mail);
zend-mail 提供了许多传输层,每个传输层可能需要许多附加参数,例如用户名、密码等
电子邮件管理方法
一些值得注意的电子邮件管理方法如下 −
isValid −没有"发件人"地址的消息无效。
isValid() : bool
setEncoding − 设置消息编码。
setEncoding(string $encoding) : void
getEncoding − 获取消息编码。
getEncoding() : string
setHeaders −撰写标头。
setHeaders(Zend\Mail\Headers $headers) : void
getHeaders − 访问标头集合。
getHeaders() : Zend\Mail\Headers
setFrom − 设置(覆盖)发件人地址。它包含一个键/值对,其中键是人类可读的名称,值是电子邮件地址。
setFrom( string|AddressInterface|array|AddressList|Traversable $emailOrAddressList, string|null $name ) : void
addFrom − 添加 'From' 地址。
addFrom( string|AddressInterface|array|AddressList|Traversable $emailOrAddressOrList, string|null $name ) : void
getFrom − 检索 'From' 发件人列表。
getFrom() : AddressList setTo - Overwrite the address list in the To recipients. setTo( string|AddressInterface|array|AddressList|Traversable $emailOrAddressList, null|string $name ) : void
setSubject − 设置邮件主题标头值。
setSubject(string $subject) :void
setBody − 设置邮件正文。
setBody(null|string|Zend\Mime\Message|object $body) : void
SMTP 传输层
zend-mail 提供通过 Zend\Mail\Transport\Smtpclass 使用 SMTP 服务器发送电子邮件的选项。它与 Sendmail 类似,只是它有一些额外的选项来配置 SMTP 主机、端口、用户名、密码等。
部分代码如下 −
use Zend\Mail\Transport\Smtp as SmtpTransport; use Zend\Mail\Transport\SmtpOptions; $transport = new SmtpTransport(); $options = new SmtpOptions([ 'name' => 'localhost', 'host' =>'smtp.gmail.com', 'port' => 465, ]); $transport->setOptions($options);
此处,
name − SMTP 主机的名称。
host − 远程主机名或 IP 地址。
port −远程主机正在监听的端口。
邮件概念 - 示例
让我们按照以下几点编写一个简单的 php 控制台应用程序来理解邮件概念。
创建一个文件夹"mailapp"。
使用 composer 工具安装 zend-mail。
在"mailapp"文件夹中创建一个 php 文件 Mail.php。
使用 Zend\Mail\Message 创建消息。
$message = new Message(); $message->addTo('user1@gmail.com'); $message->addFrom('user2@gmail.com'); $message->setSubject('Hello!'); $message->setBody("My first Zend-mail application!");
创建 SMTP 传输层并添加必要的配置。
// 使用 LOGIN 身份验证设置 SMTP 传输 $transport = new SmtpTransport(); $options = new SmtpOptions([ 'name' => 'localhost', 'host' => 'smtp.gmail.com', // 或任何 SMTP 服务器 'port' => 465, // SMTP 服务器正在监听的端口 'connection_class' => 'login', 'connection_config' => [ username' => '<your username>', 'password' => '<your password>', 'ssl' => 'ssl'], ]); $transport->setOptions($options);
使用 send 方法发送电子邮件。
$transport->send($message);
完整清单 Mail.php 如下 −
<?php require __DIR__ . '/vendor/autoload.php'; use Zend\Mail\Message; use Zend\Mail\Transport\Smtp as SmtpTransport; use Zend\Mail\Transport\SmtpOptions; $message = new Message(); $message->addTo('user1@gmail.com'); $message->addFrom('user2@gmail.com'); $message->setSubject('Hello!'); $message->setBody("My first Zend-mail application!"); // 使用 LOGIN 身份验证设置 SMTP 传输 $transport = new SmtpTransport(); $options = new SmtpOptions([ 'name' => 'localhost', 'host' => 'smtp.gmail.com', // 或任何 SMTP 服务器 'port' => 465, // SMTP 服务器正在监听的端口 'connection_class' => 'login', 'connection_config' => [ 'username' => '<your username>', 'password' => '<your password>', 'ssl' => 'ssl'], ]); $transport->setOptions($options); $transport->send($message);
现在,在命令提示符中运行应用程序 php Mail.php。这将按照应用程序中的配置发送邮件。
Zend Framework - 单元测试
一般来说,我们可以使用高级调试工具或使用echo和die等简单命令来调试 PHP 应用程序。在 Web 场景中,我们需要测试业务逻辑以及表示层。可以通过输入相关测试数据来测试 Web 应用程序中的表单,以确保表单按预期工作。
可以使用浏览器手动测试网站的设计。可以使用单元测试自动执行这些类型的测试过程。单元测试在大型项目中必不可少。这些单元测试将有助于自动化测试过程并在出现问题时提醒开发人员。
设置 PHPUnit
Zend 框架与 PHPUnit 单元测试框架集成。要为 Zend 框架编写单元测试,我们需要设置 PHPUnit,这可以通过使用以下 Composer 命令轻松完成。
$ composer require --dev phpunit/phpunit
执行上述命令后,您将获得如以下代码块所示的响应。
Zend Framework - 单元测试
一般来说,我们可以使用高级调试工具或使用echo和die等简单命令来调试 PHP 应用程序。在 Web 场景中,我们需要测试业务逻辑以及表示层。可以通过输入相关测试数据来测试 Web 应用程序中的表单,以确保表单按预期工作。
可以使用浏览器手动测试网站的设计。可以使用单元测试自动执行这些类型的测试过程。单元测试在大型项目中必不可少。这些单元测试将有助于自动化测试过程并在出现问题时提醒开发人员。
设置 PHPUnit
Zend 框架与 PHPUnit 单元测试框架集成。要为 Zend 框架编写单元测试,我们需要设置 PHPUnit,这可以通过使用以下 Composer 命令轻松完成。
$ composer require --dev phpunit/phpunit
执行上述命令后,您将获得如以下代码块所示的响应。
使用 ^5.7 版 phpunit/phpunit ./composer.json 已更新 使用包信息加载 composer 存储库 更新依赖项(包括 require-dev) 无需安装或更新 编写锁定文件 生成自动加载文件
现在,当您打开"composer.json"文件时,您将看到以下更改
现在,当您打开"composer.json"文件时,您将看到以下更改 −
"require-dev": { "phpunit/phpunit": "^5.7" }
TestCase 和断言
Zend 框架提供了辅助类来对控制器进行单元测试。TestCase 是 PHPUnit 框架中用于编写测试用例的主要组件,Zend 框架提供了 TestCase 的抽象实现,称为 AbstractHttpControllerTestCase。
此 AbstractHttpControllerTestCase 提供了各种 Assert 方法,可按功能分组。它们如下 −
请求断言 − 用于断言 http 请求。例如,assertControllerName。
CSS 选择断言 −用于使用 HTML DOM 模型检查响应 HTML。
XPath 断言 − 基于 XPath 的 CSS 选择断言的替代方案。
重定向断言 − 用于检查页面重定向。
响应标头断言 − 用于检查响应标头,如状态代码 (assertResponseStatusCode)
创建测试目录
可以为每个模块单独编写单元测试。所有测试相关的代码都需要在模块根目录下的 test 文件夹中创建。
例如,要为 Tutorial 模块下的 TutorialController 编写测试,测试类需要放在 myapp/module/Tutorial/test/Controller/ 目录下。
示例
让我们编写一个测试类来对 TutorialController 进行单元测试。
首先,我们应该编写一个名为 TutorialControllerTest 的类并将其扩展为 AbstractHttpControllerTestCase。
下一步是编写 Setup 方法来设置测试环境。这可以通过调用 setApplicationConfig 方法并传递我们的主要应用程序配置文件 myapp/config/application.config.php 来完成
public function setUp() { $configOverrides = []; $this->setApplicationConfig(ArrayUtils::merge( include __DIR__ . '/../../../../config/application.config.php', $configOverrides )); parent::setUp(); }
根据需求编写一个或多个方法并调用各种断言方法。
$this->assertMatchedRouteName('tutorial');
我们已经编写了测试类,完整清单如下 −
<?php namespace TutorialTest\Controller; use Tutorial\Controller\TutorialController; use Zend\Stdlib\ArrayUtils; use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase; class TutorialControllerTest extends AbstractHttpControllerTestCase { public function setUp() { $configOverrides = []; $this->setApplicationConfig(ArrayUtils::merge( include __DIR__ . '/../../../../config/application.config.php', $configOverrides )); parent::setUp(); } public function testIndexActionCanBeAccessed() { $this->dispatch('/tutorial', 'GET'); $this->assertResponseStatusCode(200); $this->assertModuleName('tutorial'); $this->assertControllerName(TutorialController::class); $this->assertControllerClass('TutorialController'); $this->assertMatchedRouteName('tutorial'); } }
现在,打开命令提示符,转到应用程序根目录并执行 vendor 文件夹中的 phpunit 可执行文件。
cd /path/to/app ./vendor/bin/phpunit ./vendor/bin/phpunit module/ Tutorial/test/Controller/TutorialControllerTest.php
结果将如下面的代码块所示 −
PHPUnit 5.7.5 by Sebastian Bergmann and contributors. .1 / 1 (100%) Time: 96 ms, Memory: 8.00MB OK (1 test, 5 assertions)
Zend Framework - 错误处理
为了确保系统平稳运行,需要有效处理系统故障。Zend Framework 带有默认错误捕获,可在错误发生时打印并记录错误。此错误处理程序还用于捕获异常。
当调试为真时,错误处理程序显示错误,当调试为假时,错误处理程序记录错误。Zend Framework 有多个异常类,内置异常处理将捕获任何未捕获的异常并呈现有用的页面。
默认错误处理
我们可以在应用程序配置文件 myapp/module/Application/config/module.config.php 中配置默认错误设置。
部分代码示例如下 −
'view_manager' => [ 'display_not_found_reason' => true, 'display_exceptions' => true, 'doctype' => 'HTML5', 'not_found_template' => 'error/404', 'exception_template' => 'error/index', 'template_map' => [ 'layout/layout' => __DIR__ . '/../view/layout/layout.phtml', 'application/index/index' => __DIR__ . '/../view/application/index/index.phtml', 'error/404' => __DIR__ . '/../view/error/404.phtml', 'error/index' => __DIR__ . '/../view/error/index.phtml', ], 'template_path_stack' => [ __DIR__ . '/../view', ], ],
这里的display_exception、not_found_template、exception_template、error/404和error/index是错误相关的配置项,不言自明。
其中最重要的一项是error/index。这是系统发生异常时显示的模板。我们可以修改这个模板myapp/module/Application/view/error/index.phtml来控制要显示的错误数量。
Zend Framework - 工作示例
在本章中,我们将学习如何在 Zend Framework 中创建一个完整的基于 MVC 的员工应用程序。请按照以下步骤操作。
步骤 1:Module.php
首先,我们应该在 – myapp/module/Employee/src/ 目录中创建一个 Employee 模块,然后实现 ConfigProviderInterface 接口。
Module 类的完整代码如下 −
<?php namespace Employee; use Zend\ModuleManager\Feature\ConfigProviderInterface; class Module implements ConfigProviderInterface { public function getConfig() { return include __DIR__ . '/../config/module.config.php'; } }
步骤 2:composer.json
使用以下代码在 autoload 部分下的 composer.json 中配置 Tutorial 模块。
"autoload": { "psr-4": { "Application\": "module/Application/src/", "Tutorial\": "module/Tutorial/src/", "Employee\": "module/Employee/src/" } }
现在,使用 composer update 命令更新应用程序。
composer update
Composer 命令将对应用程序进行必要的更改,并显示日志,如下面的命令提示符所示。
Loading composer repositories with package information Updating dependencies (including require-dev) - Removing zendframework/zend-component-installer (0.3.0) - Installing zendframework/zend-component-installer (0.3.1) Downloading: 100% - Removing zendframework/zend-stdlib (3.0.1) - Installing zendframework/zend-stdlib (3.1.0) Loading from cache - Removing zendframework/zend-eventmanager (3.0.1) - Installing zendframework/zend-eventmanager (3.1.0) Downloading: 100% - Removing zendframework/zend-view (2.8.0) - Installing zendframework/zend-view (2.8.1) Loading from cache - Removing zendframework/zend-servicemanager (3.1.0) - Installing zendframework/zend-servicemanager (3.2.0) Downloading: 100% - Removing zendframework/zend-escaper (2.5.1) - Installing zendframework/zend-escaper (2.5.2) Loading from cache - Removing zendframework/zend-http (2.5.4) - Installing zendframework/zend-http (2.5.5) Loading from cache - Removing zendframework/zend-mvc (3.0.1) - Installing zendframework/zend-mvc (3.0.4) Downloading: 100% - Removing phpunit/phpunit (5.7.4) - Installing phpunit/phpunit (5.7.5) Downloading: 100% Writing lock file Generating autoload files
步骤 3:员工模块的 module.config.php
使用以下代码在 myapp/module/Employee/config 下创建模块配置文件"module.config.php"。
<?php namespace Employee; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\Router\Http\Segment; return [ 'controllers' => [ 'factories' => [ Controller\EmployeeController::class => InvokableFactory::class, ], ], 'view_manager' => [ 'template_path_stack' => ['employee' => __DIR__ . '/../view',], ], ];
现在,在应用程序级配置文件 myapp/config/modules.config.php 中配置 Employee 模块。
return ['Zend\Router', 'Zend\Validator', 'Application', 'Tutorial', 'Employee'];
步骤 4:EmployeeController
通过扩展 AbstractActionController 创建一个新的 PHP 类 EmployeeController,并将其放在 myapp/module/Employee/src/Controller 目录中。
完整的代码清单如下 −
<?php namespace Employee\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class EmployeeController extends AbstractActionController { public function indexAction() { return new ViewModel(); } }
步骤 5:路由器配置
让我们在员工模块中添加一个段路由。更新员工模块配置文件 module.config.php,位于 myapp/module/Employee/config。
<?php namespace Employee; use Zend\ServiceManager\Factory\InvokableFactory; use Zend\Router\Http\Segment; return [ 'controllers' => [ 'factories' => [ Controller\EmployeeController::class => InvokableFactory::class, ], ], 'router' => [ 'routes' => [ 'employee' => [ 'type' => Segment::class, 'options' => [ 'route' => '/employee[/:action[/:id]]', 'constraints' => [ 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', 'id' => '[0-9]+', ], 'defaults' => [ 'controller' => Controller\EmployeeController::class, 'action' => 'index', ], ], ], ], ], 'view_manager' => [ 'template_path_stack' => [ 'employee' => __DIR__ . '/../view', ], ], ];
我们已成功为员工模块添加了路由。下一步是为员工应用程序创建视图脚本。
步骤 6:创建 ViewModel
在 myapp/module/Employee/view/employee/employee 目录下创建一个名为"index.phtml"的文件。
在文件中添加以下更改 −
<div class = "row content"> <h3>This is my first Zend application</h3> </div> Move to "EmployeeController.php" file and edit the following changes, <?php namespace Employee\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class EmployeeController extends AbstractActionController { public function indexAction() { return new ViewModel(); } }
最后,我们成功完成了 Employee 模块。我们可以使用以下 URL &minus; http://localhost:8080/employee 访问它。
结果
在下一步中,我们将在员工应用程序中执行添加、编辑和删除数据操作。要执行这些操作,我们首先应该创建一个数据库模型。它将在下一步中描述。
步骤 7:创建模型
让我们在模块src 目录中创建一个模型 Employee。通常,模型分组在 Model 文件夹下 (myapp/module/Employee/src/Model/Employee.php)
<?php namespace Employee\Model; class Employee { public $id; public $emp_name; public $emp_job; }
步骤 8:MySQL 表
使用以下命令在本地 MYSQL 服务器中创建一个名为 tutorials 的数据库 −
create database tutorials;
让我们使用以下 SQL 命令在数据库中创建一个名为 employee 的表 −
use tutorials; CREATE TABLE employee ( id int(11) NOT NULL auto_increment, emp_name varchar(100) NOT NULL, emp_job varchar(100) NOT NULL, PRIMARY KEY (id) );
使用以下查询将数据插入 employee 表中 −
INSERT INTO employee (emp_name, emp_job) VALUES ('Adam', 'Tutor'); INSERT INTO employee (emp_name, emp_job) VALUES ('Bruce', 'Programmer'); INSERT INTO employee (emp_name, emp_job) VALUES ('David', 'Designer');
步骤 9:更新数据库配置
使用必要的数据库驱动器信息更新全局配置文件 myapp/config/autoload/global.php。
return [ 'db' => [ 'driver' => 'Pdo', 'dsn' => 'mysql:dbname = tutorials;host=localhost', 'driver_options' => [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''], ], ];
现在,更新本地配置文件中的数据库凭据 – myapp/config/autoload/local.php。这样,我们可以分离本地和实时数据库连接凭据。
<?php return array( 'db' => array('username' => '<user_name>', 'password' => '<password>',), );
步骤 10:实现 exchangeArray
在 Employee 模型中实现 exchangeArray 函数。
<?php namespace Employee\Model; class Employee { public $id; public $emp_name; public $emp_job; public function exchangeArray($data) { $this->id = (!empty($data['id'])) ? $data['id'] : null; $this->emp_name = (!empty($data['emp_name'])) ? $data['emp_name'] : null; $this->emp_job = (!empty($data['emp_job'])) ? $data['emp_job'] : null; } }
步骤 11:使用 TableGateway 获取员工数据
在 Model 文件夹本身中创建类 EmployeeTable。它在以下代码块中定义。
<?php namespace Employee\Model; use Zend\Db\TableGateway\TableGatewayInterface; class EmployeeTable { protected $tableGateway; public function __construct(TableGatewayInterface $tableGateway) { $this->tableGateway = $tableGateway; } public function fetchAll() { $resultSet = $this->tableGateway->select(); return $resultSet; } }
步骤 12:配置 EmployeeTable 类
使用 getServiceConfig() 方法更新 Module.php 中的员工服务
<?php namespace Employee; use Zend\Db\Adapter\AdapterInterface; use Zend\Db\ResultSet\ResultSet; use Zend\Db\TableGateway\TableGateway; use Zend\ModuleManager\Feature\ConfigProviderInterface; class Module implements ConfigProviderInterface { public function getConfig() { return include __DIR__ . '/../config/module.config.php'; } public function getServiceConfig() { return [ 'factories' => [ Model\EmployeeTable::class => function ( $container) { $tableGateway = $container>get( Model\EmployeeTableGateway::class); $table = new Model\EmployeeTable($tableGateway); return $table; }, Model\EmployeeTableGateway::class => function ($container) { $dbAdapter = $container->get(AdapterInterface::class); $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Model\Employee()); return new TableGateway('employee', $dbAdapter, null, $resultSetPrototype); }, ], ]; } }
步骤 13:在控制器中添加员工服务
更新 − myapp/module/config/module.config.php 中员工模块配置的控制器部分,如下所示。
'controllers' => [ 'factories' => [ Controller\EmployeeController::class => function($container) { return new Controller\EmployeeController( $container->get(Model\EmployeeTable::class) ); }, ], ]
步骤 14:为 EmployeeController 添加构造函数
添加以 EmployeeTable 作为参数的构造函数并编辑以下更改。
<?php namespace Employee\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Employee\Model\Employee; use Employee\Model\EmployeeTable; class EmployeeController extends AbstractActionController { private $table; public function __construct(EmployeeTable $table) { $this->table = $table; } public function indexAction() { $view = new ViewModel([ 'data' => $this->table->fetchAll(), ]); return $view; } }
步骤 15:在视图脚本"index.phtml"中显示员工信息
移至文件 − index.phtml 并进行以下更改 −
<?php $title = 'Employee application'; $this->headTitle($title); ?> <table class="table"> <tr> <th>Employee Name</th> <th>Employee Job</th> <th>Edit/Delete operations</th> </tr> <?php foreach ($data as $empdata) : ?> <tr> <td><?php echo $this->escapeHtml($empdata->emp_name);?></td> <td><?php echo $this->escapeHtml($empdata->emp_job);?></td> <td> <a href="<?php echo $this->url('employee', array('action'=>'edit', 'id' =>$empdata->id));?>">Edit</a> <a href="<?php echo $this->url('employee', array('action'=>'delete', 'id' => $empdata->id));?>">Delete</a> </td> </tr> <?php endforeach; ?> </table>
现在我们已经成功创建了数据库模型,并且可以在应用程序中获取记录。
使用 URL − http://localhost:8080/employee 请求应用程序。
结果
下一步介绍员工模块中的插入、编辑和删除数据操作。
步骤 16:创建员工表单
在 myapp/module/Employee/src/Form 目录中创建一个名为 EmployeeForm.php 的文件。它在下面的代码块中描述。
<?php namespace Employee\Form; use Zend\Form\Form; class EmployeeForm extends Form { public function __construct($name = null) { / / we want to ignore the name passed parent::__construct('employee'); $this->add(array( 'name' => 'id', 'type' => 'Hidden', )); $this->add(array( 'name' => 'emp_name', 'type' => 'Text', 'options' => array( 'label' => 'Name', ), )); $this->add(array( 'name' => 'emp_job', 'type' => 'Text', 'options' => array( 'label' => 'Job', ), )); $this->add(array( 'name' => 'submit', 'type' => 'Submit', 'attributes' => array( 'value' => 'Go', 'id' => 'submitbutton', ), )); } }
步骤 17:更新员工模型
更新员工模型并实现 InputFilterAwareInterface。移至目录 myapp/module/Employee/src/Employee/Model 并在 Employee.phpfile 中添加以下更改。
<?php namespace Employee\Model; // 添加这些导入语句 use Zend\InputFilter\InputFilter; use Zend\InputFilter\InputFilterAwareInterface; use Zend\InputFilter\InputFilterInterface; class Employee implements InputFilterAwareInterface { public $id; public $emp_name; public $emp_job; protected $inputFilter; public function exchangeArray($data) { $this->id = (isset($data['id'])) ? $data['id'] : null; $this->emp_name = (isset($data['emp_name'])) ? $data['emp_name'] : null; $this->emp_job = (isset($data['emp_job'])) ? $data['emp_job'] : null; } // 向这些方法添加内容: public function setInputFilter(InputFilterInterface $inputFilter) { throw new \Exception("Not used"); } public function getInputFilter() { if (!$this->inputFilter) { $inputFilter = new InputFilter(); $inputFilter->add(array( 'name' => 'id', 'required' => true, 'filters' => array( array('name' => 'Int'), ), )); $inputFilter->add(array( 'name' => 'emp_name', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array('name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 50, ), ), ), )); $inputFilter->add(array( 'name' => 'emp_job', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array('name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 50, ), ), ), )); $this->inputFilter = $inputFilter; } return $this->inputFilter; } }
步骤 18:在 Employee Controller 中添加 addAction
在 EmployeeController 类中添加以下更改。
<?php use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Employee\Model\Employee; use Employee\Model\EmployeeTable; use Employee\Form\EmployeeForm; public function addAction() { $form = new EmployeeForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if ($request->isPost()) { $employee = new Employee(); $form->setInputFilter($employee->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { $employee->exchangeArray($form->getData()); $this->table->saveEmployee($employee); // Redirect to list of employees return $this->redirect()->toRoute('employee'); } } return array('form' => $form); }
步骤 19:在 EmployeeTable 类中添加保存功能
在 EmployeeTable 类中添加以下两个函数 - myapp/module/Employee/src/Model/EmployeeTable.php
public function getEmployee($id) { $id = (int) $id; $rowset = $this->tableGateway->select(array('id' => $id)); $row = $rowset->current(); if (!$row) { throw new \Exception("Could not find row $id"); } return $row; } public function saveEmployee(Employee $employee) { $data = array ( 'emp_name' => $employee->emp_name, 'emp_job' => $employee->emp_job, ); $id = (int) $employee->id; if ($id == 0) { $this->tableGateway->insert($data); } else { if ($this->getEmployee($id)) { $this->tableGateway->update($data, array('id' => $id)); } else { throw new \Exception('Employee id does not exist'); } } }
步骤 20:为 AddAction 方法创建视图脚本 Add.phtml
在 − myapp/module/view/employee/employee 中的"Add.phtml"文件中添加以下更改。
<?php $title = 'Add new employee'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php $form->setAttribute('action', $this->url('employee', array('action' => 'add'))); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get('id')); echo $this->formRow($form->get('emp_name'))."<br>"; echo $this->formRow($form->get('emp_job'))."<br>"; echo $this->formSubmit($form->get('submit')); echo $this->form()->closeTag(); Request the application using the url, http://localhost:8080/employee/add
结果
添加数据后,将重定向到主页。
步骤 21:编辑员工记录
让我们在员工模块中执行编辑数据操作。在 Employeecontroller.php 中更新以下更改。
public function editAction() { $id = (int) $this->params()->fromRoute('id', 0); if (!$id) { return $this->redirect()->toRoute('employee', array( 'action' => 'add' )); } try { $employee = $this->table->getEmployee($id); } catch (\Exception $ex) { return $this->redirect()->toRoute('employee', array( 'action' => 'index' )); } $form = new EmployeeForm(); $form->bind($employee); $form->get('submit')->setAttribute('value', 'Edit'); $request = $this->getRequest(); if ($request->isPost()) { $form->setInputFilter($employee->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { $this->table->saveEmployee($employee); // Redirect to list of employees return $this->redirect()->toRoute('employee'); } } return array('id' => $id, 'form' => $form,); }
在这里,我们查找匹配的路由中的 id,然后加载员工详细信息以进行编辑操作。
步骤 22:Employee.php
现在在"Employee.php"文件中添加以下更改,该文件位于 − myapp/module/Employee/src/Employee/Model/ 目录中。
public function getArrayCopy() { return get_object_vars($this); }
在这里,Zend\Stdlib\Hydrator\ArraySerializable 期望在模型中找到两个方法:getArrayCopy() 和 exchangeArray()。
其中,exchangeArray() 用于迭代。此函数用于绑定员工表中的数据。
现在,我们需要为 editAction() 创建一个视图脚本。
步骤 23:创建 Edit.phtml
在 module/Employee/view/employee/employee/edit.phtml 中创建一个视图脚本文件
<?php $title = 'Edit employee records'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> <?php $form = $this->form; $form->setAttribute('action', $this->url( 'employee', array('action' => 'edit', 'id' => $this->id,) )); $form->prepare(); echo $this->form()->openTag($form); echo $this->formHidden($form->get('id')); echo $this->formRow($form->get('emp_name'))."<br>"; echo $this->formRow($form->get('emp_job'))."<br>"; echo $this->formSubmit($form->get('submit')); echo $this->form()->closeTag();
编辑员工详细信息如下图所示。
编辑数据后,将重定向到主页。
步骤 24:添加 deleteEmployee 方法
在 EmployeeTable 类中添加 deleteEmployee 方法 – myapp/module/Employee/src/Model/EmployeeTable.php
public function deleteEmployee($id) { $this->tableGateway->delete(['id' => (int) $id]); }
步骤 25:删除员工记录
现在让我们在员工模块中执行删除数据操作。在 EmployeeController 类中添加以下方法 deleteAction。
public function deleteAction() { $id = (int) $this->params()->fromRoute('id', 0); if (!$id) { return $this->redirect()->toRoute('employee'); } $request = $this->getRequest(); if ($request->isPost()) { $del = $request->getPost('del', 'No'); if ($del == 'Yes') { $id = (int) $request->getPost('id'); $this->table->deleteEmployee($id); } return $this->redirect()->toRoute('employee'); } return array( 'id' => $id, 'employee' => $this->table->getEmployee($id) ); }
此处,deleteEmployee() 方法根据员工的 id 删除员工,并重定向到员工列表页面(主页)。
现在让我们为 deleteAction() 方法创建相应的视图脚本。
步骤 26:创建视图脚本
在 − myapp/module/Employee/view/employee/employee/delete.phtml 中创建名为 delete.phtml 的文件,并在其中添加以下代码。
<?php $title = 'Delete an employee record'; $this->headTitle($title); ?> <h1><?php echo $this->escapeHtml($title); ?></h1> '<?php echo $this->escapeHtml($employee->emp_name); ?>' by '<?php echo $this->escapeHtml($employee->emp_job); ?&'? <?php $url = $this->url('employee', array('action' => 'delete', 'id' => $this->id,)); ?> <form action ="<?php echo $url; ?>" method = "post"> <div> <input type = "hidden" name = "id" value = "<?php echo (int) $employee->id; ?>" /> <input type = "submit" name = "del" value = "Yes" /> <input type = "submit" name = "del" value = "No" /> </div> </form>
现在,使用主页中的编辑链接删除任何员工,结果将如以下屏幕截图所示。
结果
我们已成功完成员工模块,并实现了所有必要的功能。
结论
在当前竞争环境中,Zend 框架被开发人员置于首位。它为 PHP 语言中的任何程序或任何类型的应用程序提供抽象。它是一个成熟的框架,支持现代 PHP 语言功能。它有趣、专业、不断发展并与当前技术保持同步。