Yii - 快速指南


Yii - 概述

Yii [ji:]框架是一个开源 PHP 框架,用于快速开发的现代 Web 应用程序。它是围绕模型-视图-控制器复合模式构建的。

Yii 提供安全和专业的功能来快速创建强大的项目。Yii 框架具有基于组件的架构和完整可靠的缓存支持。因此,它适合构建各种Web应用程序:论坛、门户、内容管理系统、RESTful服务、电子商务网站等等。它还有一个名为 Gii 的代码生成工具,其中包括完整的 CRUD(创建-读取-更新-删除)界面生成器。

核心特点

Yii 的核心特征如下:

  • Yii 实现了 MVC 架构模式。
  • 它提供了关系数据库和 NoSQL 数据库的功能。
  • Yii 绝不会仅仅为了遵循某些设计模式而过度设计事物。
  • 它具有极强的可扩展性。
  • Yii 提供多层缓存支持。
  • Yii 提供 RESTful API 开发支持。
  • 它具有高性能。

总的来说,如果您需要的只是底层数据库的简洁接口,那么 Yii 是正确的选择。目前,Yii 有两个版本:1.1 和 2.0。

版本 1.1 现已处于维护模式,版本 2 采用了最新技术,包括用于包分发的 Composer 实用程序、PSR 级别 1、2 和 4,以及许多 PHP 5.4+ 功能。版本 2 将是未来几年的主要开发工作。

Yii 是一个纯粹的 OOP(面向对象编程)框架。因此,它需要 OOP 的基础知识。Yii 框架还使用 PHP 的最新功能,例如特征和命名空间。如果你理解这些概念,你会更容易学习 Yii 2.0。

环境

Yii2 的主要要求是PHP 5.4+Web 服务器。Yii 是一个功能强大的控制台工具,它可以管理数据库迁移、资产编译和其他内容。建议通过命令行访问您开发应用程序的计算机。

出于开发目的,我们将使用 -

  • Linux 薄荷 17.1
  • PHP 5.5.9
  • PHP 内置网络服务器

安装前检查

要检查您的本地计算机是否适合使用最新的 Yii2 版本,请执行以下操作 -

步骤 1 - 安装最新的 php 版本。

sudo apt-get install php5 

步骤 2 - 安装最新的 mysql 版本。

sudo apt-get install mysql-server

步骤 3 - 下载 Yii2 基本应用程序模板。

composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic

步骤 4 - 要启动 PHP 内置服务器,请在basic文件夹中运行。

php -S localhost:8080

有一个有用的脚本,requirements.php。它检查您的服务器是否满足运行应用程序的要求。您可以在应用程序的根文件夹中找到此脚本。

要求 PHP 脚本

如果您在 Web 浏览器的地址栏中输入http://localhost:8080/requirements.php,页面将如下图所示 -

运行需求 PHP 脚本

Yii - 安装

开始使用 Yii2 最直接的方法是使用 Yii2 团队提供的基本应用程序模板。该模板也可以通过 Composer 工具获得。

步骤 1 - 在硬盘中找到合适的目录,然后通过以下命令下载 Composer PHAR(PHP 存档)。

curl -sS https://getcomposer.org/installer | php

步骤 2 - 然后将此存档移至 bin 目录。

mv composer.phar /usr/local/bin/composer

步骤 3 - 安装 Composer 后,您可以安装 Yii2 基本应用程序模板。运行这些命令。

composer global require "fxp/composer-asset-plugin:~1.1.1" 
composer create-project --prefer-dist yiisoft/yii2-app-basic helloworld 

第一个命令安装 Composer 资产插件,该插件管理npm和 Bower 依赖项。第二个命令将 Yii2 基本应用程序模板安装在名为helloworld的目录中。

步骤 4 - 现在打开helloworld目录并启动 PHP 内置的 Web 服务器。

php -S localhost:8080 -t web

步骤 5 - 然后在浏览器中打开http://localhost:8080 。您可以看到欢迎页面。

欢迎页面

Yii - 创建页面

现在我们将在您的应用程序中创建一个“Hello world”页面。要创建页面,我们必须创建操作和视图。

操作在控制器中声明。最终用户将收到操作的执行结果。

步骤1 - 在现有的SiteController中声明speak动作,该动作在类文件controllers/ SiteController.php中定义。

<?php 
   namespace app\controllers; 
   use Yii; 
   use yii\filters\AccessControl; 
   use yii\web\Controller; 
   use yii\filters\VerbFilter; 
   use app\models\LoginForm; 
   use app\models\ContactForm; 
   class SiteController extends Controller { 
      /* other code */ 
      public function actionSpeak($message = "default message") { 
         return $this->render("speak",['message' => $message]); 
      } 
   } 
?>

我们将发言动作定义为名为actionSpeak的方法。在 Yii 中,所有操作方法都以单词 action 为前缀。这就是框架区分行动方法和非行动方法的方式。如果操作 ID 需要多个单词,那么它们将通过破折号连接起来。因此,操作 ID add-post 对应于操作方法actionAddPost

在上面给出的代码中,“out”函数采用 GET 参数$message。我们还调用一个名为“render”的方法来渲染一个名为speak的视图文件。我们将消息参数传递给视图。渲染结果是一个完整的HTML页面。

视图是一个生成响应内容的脚本。对于说话动作,我们创建一个打印消息的说话视图。当调用 render 方法时,它会查找名为view/controllerID/vewName.php 的PHP 文件。

步骤2 - 因此,在views/site文件夹中创建一个名为speak.php的文件,其中包含以下代码。

<?php 
   use yii\helpers\Html; 
?> 
<?php echo Html::encode($message); ?> 

请注意,我们在打印之前对消息参数进行 HTML 编码,以避免XSS攻击。

步骤 3 - 在 Web 浏览器中输入以下内容http://localhost:8080/index.php?r=site/speak&message=hello%20world

您将看到以下窗口 -

说出 PHP 文件

URL 中的“r”参数代表路由。路由的默认格式是controllerID/actionID。在我们的例子中,站点/发言路线将由SiteController类和发言操作解析。

Yii - 应用程序结构

整个代码库中只有一个文件夹可供 Web 服务器公开使用。这是网络目录。Web 服务器无法访问 Web 根目录之外的其他文件夹。

注意- 所有项目依赖项都位于composer.json文件中。Yii2 有一些重要的包,Composer 已经将其包含在您的项目中。这些包如下 -

  • Gii – 代码生成工具
  • 调试控制台
  • Codeception 测试框架
  • SwiftMailer 库
  • Twitter Bootstrap UI 库

前三个包仅在开发环境中有用。

Yii2的应用程序结构精确、清晰。它包含以下文件夹 -

  • Assets - 此文件夹包含网页中引用的所有 .js 和 .css 文件。

  • 命令- 此文件夹包含可以从终端使用的控制器。

  • Config - 此文件夹包含用于管理数据库、应用程序和应用程序参数的配置文件。

  • 邮件- 此文件夹包含邮件布局。

  • 模型- 此文件夹包含应用程序中使用的模型。

  • Runtime - 该文件夹用于存储运行时数据。

  • 测试- 此文件夹包含所有测试(验收、单元、功能)。

  • Vendor - 此文件夹包含 Composer 管理的所有第三方包。

  • Views - 此文件夹用于由控制器显示的视图。布局文件夹是页面模板。

  • Web - 来自网络的入口点。

应用结构

以下是应用程序结构的图示。

应用结构

Yii2 – 对象

以下列表包含所有 Yii2 的对象 -

模型、视图和控制器

模型用于数据表示(通常来自数据库)。视图用于显示数据。控制器用于处理请求并生成响应。

成分

要创建可重用的功能,用户可以编写自己的组件。组件只是包含逻辑的对象。例如,组件可以是重量转换器。

应用组件

这些对象在整个应用程序中仅实例化一次。组件和应用程序组件之间的主要区别在于后者在整个应用程序中只能有一个实例。

小部件

小部件是包含逻辑和渲染代码的可重用对象。例如,小部件可以是图库滑块。

过滤器

过滤器是在执行控制器操作之前或之后运行的对象。

模块

您可以将模块视为可重用的子应用程序,其中包含模型、视图、控制器等。

扩展

扩展是可以由 Composer 管理的包。

Yii - 入口脚本

入口脚本负责启动请求处理周期。它们只是用户可以访问的 PHP 脚本。

下图显示了应用程序的结构 -

入口脚本结构

Web 应用程序(以及控制台应用程序)具有单个入口脚本。最终用户向入口脚本发出请求。然后入口脚本实例化应用程序实例并将请求转发给它们。

控制台应用程序的入口脚本通常存储在项目基本路径中,并命名为yii.php。Web 应用程序的入口脚本必须存储在可通过 Web 访问的目录下。它通常被称为index.php

入口脚本执行以下操作 -

  • 定义常量。
  • 注册 Composer 自动加载器。
  • 包含 Yii 文件。
  • 加载配置。
  • 创建并配置应用程序实例。
  • 处理传入的请求。

以下是基本应用程序模板的入口脚本 -

<?php
   //defining global constants
   defined('YII_DEBUG') or define('YII_DEBUG', true);
   defined('YII_ENV') or define('YII_ENV', 'dev');
 
   //register composer autoloader
   require(__DIR__ . '/../vendor/autoload.php');
   //include yii files
   require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
  
   //load application config
   $config = require(__DIR__ . '/../config/web.php');
  
   //create, config, and process reques
   (new yii\web\Application($config))->run();
?>

以下是控制台应用程序的入口脚本 -

#!/usr/bin/env php
<?php
   /** 
   * Yii console bootstrap file. 
   * @link http://www.yiiframework.com/ 
   * @copyright Copyright (c) 2008 Yii Software LLC 
   * @license http://www.yiiframework.com/license/ 
   */
   //defining global constants
   defined('YII_DEBUG') or define('YII_DEBUG', true);
  
   //register composer autoloader
   require(__DIR__ . '/vendor/autoload.php');
   require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
  
   //load config
   $config = require(__DIR__ . '/config/console.php');
  
   //apply config the application instance 
   $application = new yii\console\Application($config);  

   //process request
   $exitCode = $application->run();
   exit($exitCode);
?>

定义全局常量的最佳位置是入口脚本。Yii 常量支持三个 -

  • YII_DEBUG - 定义是否处于调试模式。如果设置为 true,那么我们将看到更多日志数据和详细错误调用堆栈。

  • YII_ENV - 定义环境模式。默认值是产品。可用值为 prod、dev 和 test。它们在配置文件中用于定义例如不同的数据库连接(本地和远程)或其他值。

  • YII_ENABLE_ERROR_HANDLER - 指定是否启用默认的 Yii 错误处理程序。

要定义全局常量,使用以下代码 -

//defining global constants 
defined('YII_DEBUG') or define('YII_DEBUG', true); 
which is equivalent to: 
if(!defined('YII_DEBUG')) { 
   define('YII_DEBUG', true); 
} 

注意- 全局常量应在入口脚本的开头定义,以便在包含其他 PHP 文件时生效。

Yii - 控制器

控制器负责处理请求并生成响应。用户发出请求后,控制器将分析请求数据,将其传递给模型,然后将模型结果插入到视图中,并生成响应。

了解行动

控制器包括动作。它们是用户可以请求执行的基本单位。一个控制器可以有一个或多个动作。

让我们看一下基本应用程序模板的SiteController -

<?php 
   namespace app\controllers; 
   use Yii; 
   use yii\filters\AccessControl; 
   use yii\web\Controller; 
   use yii\filters\VerbFilter; 
   use app\models\LoginForm; 
   use app\models\ContactForm; 
   class SiteController extends Controller { 
      public function behaviors() { 
         return [ 
            'access' => [ 
               'class' => AccessControl::className(), 
               'only' => ['logout'], 
               'rules' => [ 
                  [ 
                     'actions' => ['logout'], 
                     'allow' => true, 
                     'roles' => ['@'], 
                  ], 
               ], 
            ], 
            'verbs' => [
               'class' => VerbFilter::className(), 
               'actions' => [ 
                  'logout' => ['post'], 
               ], 
            ], 
         ]; 
      } 
      public function actions() { 
         return [ 
            'error' => [ 
               'class' => 'yii\web\ErrorAction', 
            ], 
            'captcha' => [ 
               'class' => 'yii\captcha\CaptchaAction', 
               'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, 
            ], 
         ]; 
      } 
      public function actionIndex() { 
         return $this->render('index'); 
      } 
      public function actionLogin() { 
         if (!\Yii::$app->user->isGuest) { 
            return $this->goHome(); 
         } 
         $model = new LoginForm(); 
         if ($model->load(Yii::$app->request->post()) && $model->login()) { 
            return $this->goBack(); 
         } 
         return $this->render('login', [ 
            'model' => $model, 
         ]); 
      }
      public function actionLogout() { 
         Yii::$app->user->logout();  
         return $this->goHome(); 
      } 
      public function actionContact() { 
         //load ContactForm model 
         $model = new ContactForm(); 
         //if there was a POST request, then try to load POST data into a model 
         if ($model->load(Yii::$app->request->post()) && $model>contact(Yii::$app->params
            ['adminEmail'])) { 
            Yii::$app->session->setFlash('contactFormSubmitted');  
            return $this->refresh(); 
         } 
         return $this->render('contact', [ 
            'model' => $model, 
         ]); 
      } 
      public function actionAbout() { 
         return $this->render('about'); 
      } 
      public function actionSpeak($message = "default message") { 
         return $this->render("speak",['message' => $message]); 
      } 
   } 
?>

使用 PHP 内置服务器运行基本应用程序模板,然后转到 Web 浏览器http://localhost:8080/index.php?r=site/contact。您将看到以下页面 -

运行基本应用程序

当您打开此页面时,将执行SiteController的联系操作。该代码首先加载ContactForm模型。然后它渲染联系人视图并将模型传递到其中。

联系表格型号

如果您填写表格并单击提交按钮,您将看到以下内容 -

提交表格

请注意,这次执行了以下代码 -

if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app>params ['adminEmail'])) { 
   Yii::$app->session->setFlash('contactFormSubmitted'); 
   return $this->refresh(); 
} 

如果存在 POST 请求,我们会将 POST 数据分配给模型并尝试发送电子邮件。如果成功,我们会设置一条闪现消息,其中包含“感谢您与我们联系。我们会尽快回复您。” 并刷新页面。

了解路线

在上面的示例中,在 URL http://localhost:8080/index.php?r=site/contact 中,路由为site/contact。将执行SiteController中的联系操作 ( actionContact ) 。

路线由以下部分组成 -

  • moduleID - 如果控制器属于模块,则这部分路由存在。

  • controllerID(上例中的站点)- 一个唯一的字符串,用于在同一模块或应用程序内的所有控制器中标识控制器。

  • actionID(上例中的联系人)- 一个唯一的字符串,用于标识同一控制器内所有操作中的操作。

路由的格式为controllerID/actionID。如果控制器属于模块,则其格式如下:moduleID/controllerID/actionID

Yii - 使用控制器

Web 应用程序中的控制器应该从yii\web\Controller或其子类扩展。在控制台应用程序中,它们应该从 yii\console\Controller 或其子类扩展。

让我们在控制器文件夹中创建一个示例控制器。

步骤 1 - 在Controllers文件夹中,使用以下代码创建一个名为ExampleController.php的文件。

<?php 
   namespace app\controllers; 
   use yii\web\Controller; 
   class ExampleController extends Controller { 
      public function actionIndex() { 
         $message = "index action of the ExampleController"; 
         return $this->render("example",[ 
            'message' => $message 
         ]); 
      } 
   } 
?>

步骤 2 - 在views/example文件夹中创建一个示例视图。在该文件夹中,使用以下代码创建一个名为example.php的文件。

<?php 
   echo $message; 
?>

每个应用程序都有一个默认控制器。对于 Web 应用程序,站点是控制器,而对于控制台应用程序,站点是帮助。因此,当打开http://localhost:8080/index.php URL 时,站点控制器将处理该请求。您可以在应用程序配置中更改默认控制器。

考虑给定的代码 -

'defaultRoute' => 'main'

步骤 3 - 将上述代码添加到以下config/web.php

<?php 
   $params = require(__DIR__ . '/params.php'); 
   $config = [ 
      'id' => 'basic', 
      'basePath' => dirname(__DIR__), 
      'bootstrap' => ['log'], 
      'components' => [ 
         'request' => [ 
            // !!! insert a secret key in the following (if it is empty) - this is
               //required by cookie validation 
            'cookieValidationKey' => 'ymoaYrebZHa8gURuolioHGlK8fLXCKjO', 
         ], 
         'cache' => [ 
            'class' => 'yii\caching\FileCache', 
         ], 
         'user' => [ 
            'identityClass' => 'app\models\User', 
            'enableAutoLogin' => true, 
         ], 
         'errorHandler' => [ 
            'errorAction' => 'site/error', 
         ], 
         'mailer' => [ 
            'class' => 'yii\swiftmailer\Mailer', 
            // send all mails to a file by default. You have to set 
            // 'useFileTransport' to false and configure a transport 
            // for the mailer to send real emails. 
            'useFileTransport' => true, 
         ], 
         'log' => [ 
            'traceLevel' => YII_DEBUG ? 3 : 0, 
            'targets' => [ 
               [ 
                  'class' => 'yii\log\FileTarget',
                  'levels' => ['error', 'warning'], 
               ], 
            ], 
         ], 
         'db' => require(__DIR__ . '/db.php'), 
      ], 
      //changing the default controller 
      'defaultRoute' => 'example', 
      'params' => $params, 
   ]; 
   if (YII_ENV_DEV) { 
      // configuration adjustments for 'dev' environment 
      $config['bootstrap'][] = 'debug'; 
      $config['modules']['debug'] = [ 
         'class' => 'yii\debug\Module', 
      ]; 
      $config['bootstrap'][] = 'gii'; 
      $config['modules']['gii'] = [ 
         'class' => 'yii\gii\Module', 
      ]; 
   } 
   return $config; 
?> 						  

步骤 4 -在 Web 浏览器的地址栏中输入http://localhost:8080/index.php ,您将看到默认控制器是示例控制器。

控制器示例

- 控制器 ID 应包含小写英文字母、数字、正斜杠、连字符和下划线。

要将控制器 ID 转换为控制器类名称,您应该执行以下操作 -

  • 取出所有用连字符分隔的单词的第一个字母,并将其转换为大写。
  • 删除连字符。
  • 将正斜杠替换为反斜杠。
  • 添加控制器后缀。
  • 添加控制器命名空间。

例子

  • 页面变为app\controllers\PageController

  • post-article 变为app\controllers\PostArticleController

  • user/post-article 变为app\controllers\user\PostArticleController

  • userBlogs/post-article 变为app\controllers\userBlogs\PostArticleController

Yii - 使用动作

要在控制器类中创建操作,您应该定义一个名称以单词“action”开头的公共方法。操作的返回数据表示要发送给最终用户的响应。

步骤 1 - 让我们在ExampleController中定义 hello-world 操作。

<?php 
   namespace app\controllers; 
   use yii\web\Controller; 
   class ExampleController extends Controller { 
      public function actionIndex() { 
         $message = "index action of the ExampleController"; 
         return $this->render("example",[ 
            'message' => $message 
         ]); 
      } 
      public function actionHelloWorld() { 
         return "Hello world!"; 
      } 
   } 
?>

步骤 2 -在 Web 浏览器的地址栏中输入http://localhost:8080/index.php?r=example/hello-world 。您将看到以下内容。

你好世界行动

操作 ID 通常是动词,例如创建、更新、删除等。这是因为操作通常旨在执行资源的特定更改。

操作 ID 应仅包含以下字符 - 小写英文字母、数字、连字符和下划线。

有两种类型的操作:内联操作和独立操作。

内联操作在控制器类中定义。动作的名称是从动作 ID 中派生出来的:

  • 将操作 ID 的所有单词中的第一个字母变为大写。
  • 删除连字符。
  • 添加操作前缀。

例子-

  • 索引变为actionIndex。
  • hello-world(如上例所示)变为actionHelloWorld。

如果您计划在不同位置重用相同的操作,则应将其定义为独立操作。

创建独立的操作类

要创建独立的操作类,您应该扩展 yii\base\Action 或子类,并实现run()方法。

步骤 1 - 在项目根目录中创建一个组件文件夹。在该文件夹内创建一个名为GreetingAction.php的文件,其中包含以下代码。

<?php 
   namespace app\components;
   use yii\base\Action;
   class GreetingAction extends Action {
      public function run() {
         return "Greeting";
      }
   }
?>

我们刚刚创建了一个可重用的动作。要在我们的ExampleController中使用它,我们应该通过重写 actions() 方法在操作映射中声明我们的操作。

步骤2 -以这种方式修改ExampleController.php文件。

<?php
   namespace app\controllers;
   use yii\web\Controller;
   class ExampleController extends Controller {
      public function actions() {
         return [
            'greeting' => 'app\components\GreetingAction',
         ];
      }
      public function actionIndex() {
         $message = "index action of the ExampleController";
         
         return $this->render("example",[
            'message' => $message
         ]);
      }
      public function actionHelloWorld() {
         return "Hello world!";
      }
   }
?>

actions ()方法返回一个数组,其值是类名,键是操作 ID。

步骤 3 - 转到http://localhost:8080/index.php?r=example/greeting。您将看到以下输出。

问候语示例

步骤 4 - 您还可以使用操作将用户重定向到其他 URL。将以下操作添加到ExampleController.php

public function actionOpenGoogle() {
   // redirect the user browser to http://google.com
   return $this->redirect('http://google.com');
} 

现在,如果您打开http://localhost:8080/index.php?r=example/open-google,您将被重定向到http://google.com

操作方法可以采用参数,称为操作参数。它们的值是使用参数名称作为键从$_GET检索的。

步骤 5 - 将以下操作添加到我们的示例控制器中。

public function actionTestParams($first, $second) {
   return "$first $second";
}

步骤 6 - 在 Web 浏览器的地址栏中输入 URL http://localhost:8080/index.php?r=example/testparams&first=hello&second=world,您将看到以下输出。

运行 Hello World 示例

每个控制器都有一个默认操作。当路由仅包含控制器 ID 时,表示请求默认操作。默认情况下,操作是索引。您可以轻松地在控制器中覆盖此属性。

步骤7 - 以这种方式修改我们的ExampleController

<?php
   namespace app\controllers;
   use yii\web\Controller;
   class ExampleController extends Controller {
      public $defaultAction = "hello-world";
      /* other actions */
   }
?>

步骤 8 - 现在,如果您访问http://localhost:8080/index.php?r=example,您将看到以下内容。

运行 Hello World 示例 1

为了满足请求,控制器将经历以下生命周期 -

  • yii\base\Controller:调用init()方法。

  • 控制器根据动作ID创建动作。

  • 控制器依次调用Web 应用程序、模块和控制器的beforeAction()方法。

  • 控制器运行该操作。

  • 控制器依次调用Web 应用程序、模块和控制器的afterAction()方法。

  • 应用程序将操作结果分配给响应。

要点

控制者应该 -

  • 变得很瘦。每个操作应该只包含几行代码。
  • 使用视图进行响应。
  • 不嵌入 HTML。
  • 访问请求数据。
  • 调用模型的方法。
  • 不处理请求数据。这些应该在模型中进行处理。

Yii - 模型

模型是表示业务逻辑和规则的对象。要创建模型,您应该扩展yii\base\Model类或其子类。

属性

属性代表业务数据。它们可以像数组元素或对象属性一样被访问。每个属性都是模型的可公开访问的属性。要指定模型拥有哪些属性,您应该重写yii\base\Model::attributes()方法。

让我们看一下基本应用程序模板的ContactForm模型。

<?php
   namespace app\models;
   use Yii;
   use yii\base\Model;
   /**
   * ContactForm is the model behind the contact form.
   */
   class ContactForm extends Model {
      public $name;
      public $email;
      public $subject;
      public $body;
      public $verifyCode;
      /**
      * @return array the validation rules.
      */
      public function rules() {
         return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],
            // email has to be a valid email address
            ['email', 'email'],
            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha'],
         ];
      }
      /**
      * @return array customized attribute labels
      */
      public function attributeLabels() {
         return [
            'verifyCode' => 'Verification Code',
         ];
      }
      /**
      * Sends an email to the specified email address using the information 
         collected by this model.
      * @param  string  $email the target email address
      * @return boolean whether the model passes validation
      */
      public function contact($email) {
         if ($this->validate()) {
            Yii::$app->mailer->compose()
               ->setTo($email)
               ->setFrom([$this->email => $this->name])
               ->setSubject($this->subject)
               ->setTextBody($this->body)
               ->send();
            return true;
         }
         return false;
      }
   }
?>

步骤 1 -使用以下代码在SiteController中创建一个名为actionShowContactModel的函数。

public function actionShowContactModel() { 
   $mContactForm = new \app\models\ContactForm(); 
   $mContactForm->name = "contactForm"; 
   $mContactForm->email = "user@gmail.com"; 
   $mContactForm->subject = "subject"; 
   $mContactForm->body = "body"; 
   var_dump($mContactForm); 
}

在上面的代码中,我们定义了ContactForm模型,设置属性,并将模型显示在屏幕上。

步骤 2 - 现在,如果您在 Web 浏览器的地址栏中输入http://localhost:8080/index.php?r=site/show-contact-model,您将看到以下内容。

显示联系方式 查看

如果您的模型从yii\base\Model扩展,那么它的所有成员变量(公共和非静态)都是属性。ContactForm模型中有五个属性- name、email、subject、body、verifyCode,您可以轻松添加新属性。

属性标签

您经常需要显示与属性关联的标签。默认情况下,属性标签由yii\base\Model::generateAttributeLabel()方法自动生成。要手动声明属性标签,您可以覆盖yii\base\Model::attributeLabels()方法。

步骤 1 - 如果您打开http://localhost:8080/index.php?r=site/contact,您将看到以下页面。

联系表

请注意,属性标签与其名称相同。

步骤 2 - 现在,按以下方式修改ContactForm模型中的attributeLabels函数。

public function attributeLabels() {
   return [
      'name' => 'name overridden',
      'email' => 'email overridden',
      'subject' => 'subject overridden',
      'body' => 'body overridden',
      'verifyCode' => 'verifyCode overridden',
   ];
}

步骤 3 - 如果您再次打开http://localhost:8080/index.php?r=site/contact,您会注意到标签已更改,如下图所示。

联系方式已更改

应用场景

您可以在不同的场景中使用模型。例如,当客人想要发送联系表单时,我们需要所有模型属性。当用户想做同样的事情时,他已经登录了,所以我们不需要他的名字,因为我们可以轻松地从数据库中获取它。

为了声明场景,我们应该重写scenario()函数。它返回一个数组,其键是场景名称,值是活动属性。活动属性是要验证的属性。它们也可以被大规模分配

步骤 1 -按以下方式修改ContactForm模型。

<?php
   namespace app\models;
   use Yii;
   use yii\base\Model;
   /**
   * ContactForm is the model behind the contact form.
   */
   class ContactForm extends Model {
      public $name;
      public $email;
      public $subject;
      public $body;
      public $verifyCode;
      const SCENARIO_EMAIL_FROM_GUEST = 'EMAIL_FROM_GUEST';
      const SCENARIO_EMAIL_FROM_USER = 'EMAIL_FROM_USER';
      public function scenarios() {
         return [
            self::SCENARIO_EMAIL_FROM_GUEST => ['name', 'email', 'subject', 
               'body', 'verifyCode'],
            self::SCENARIO_EMAIL_FROM_USER => ['email' ,'subject', 'body', 
               'verifyCode'],
         ];
      }
      /**
      * @return array the validation rules.
      */
      public function rules() {
         return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],
            // email has to be a valid email address
            ['email', 'email'],
            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha'],
         ];
      }
      /**
      * @return array customized attribute labels
      */
      public function attributeLabels() {
         return [
            'name' => 'name overridden',
            'email' => 'email overridden',
            'subject' => 'subject overridden',
            'body' => 'body overridden',
            'verifyCode' => 'verifyCode overridden',
         ];
      }
      /**
      * Sends an email to the specified email address using the information 
         collected by this model.
      * @param  string  $email the target email address
      * @return boolean whether the model passes validation
      */
      public function contact($email) {
         if ($this -> validate()) {
            Yii::$app->mailer->compose()
               ->setTo($email)
               ->setFrom([$this->email => $this->name]) 
               ->setSubject($this->subject) 
               ->setTextBody($this->body)
               ->send();
            return true;
         }
         return false;
      }
   }
?>

我们添加了两个场景。一个用于访客,另一个用于经过身份验证的用户。当用户通过身份验证时,我们不需要他的名字。

步骤 2 - 现在,修改SiteControlleractionContact函数。

public function actionContact() {
   $model = new ContactForm();
   $model->scenario = ContactForm::SCENARIO_EMAIL_FROM_GUEST;
   if ($model->load(Yii::$app->request->post()) && $model->
      contact(Yii::$app->params ['adminEmail'])) {
         Yii::$app->session->setFlash('contactFormSubmitted');  
         return $this->refresh();
   }
   return $this->render('contact', [
      'model' => $model,
   ]);
}

步骤 3 -在网络浏览器中输入http://localhost:8080/index.php?r=site/contact 。您会注意到,当前所有模型属性都是必需的。

所需的模型属性

步骤 4 - 如果您更改actionContact中模型的场景(如以下代码所示),您会发现不再需要 name 属性。

$model->scenario = ContactForm::SCENARIO_EMAIL_FROM_USER;

改变场景

大量作业

大规模赋值是通过一行代码从多个输入属性创建模型的便捷方法。

代码行是 -

$mContactForm = new \app\models\ContactForm; 
$mContactForm->attributes = \Yii::$app->request->post('ContactForm');

上面给出的代码行相当于 -

$mContactForm = new \app\models\ContactForm; 
$postData = \Yii::$app->request->post('ContactForm', []); 
$mContactForm->name = isset($postData['name']) ? $postData['name'] : null; 
$mContactForm->email = isset($postData['email']) ? $postData['email'] : null; 
$mContactForm->subject = isset($postData['subject']) ? $postData['subject'] : null; 
$mContactForm->body = isset($postData['body']) ? $postData['body'] : null;

前者干净得多。请注意,大量赋值仅适用于安全属性它们只是在scene()函数中列出的当前场景属性。

数据导出

模型通常需要以不同的格式导出。要将模型转换为数组,请修改SiteControlleractionShowContactModel函数-

public function actionShowContactModel() {
   $mContactForm = new \app\models\ContactForm();
   $mContactForm->name = "contactForm";
   $mContactForm->email = "user@gmail.com";
   $mContactForm->subject = "subject";
   $mContactForm->body = "body";
   var_dump($mContactForm->attributes);
} 

在地址栏中输入http://localhost:8080/index.php?r=site/show-contact-model ,您将看到以下内容 -

数据导出

要将模型转换为JSON格式,请按以下方式修改actionShowContactModel函数 -

public function actionShowContactModel() {
   $mContactForm = new \app\models\ContactForm();
   $mContactForm->name = "contactForm";
   $mContactForm->email = "user@gmail.com";
   $mContactForm->subject = "subject";
   $mContactForm->body = "body";
   return \yii\helpers\Json::encode($mContactForm);
}

浏览器输出-

{
   "name":"contactForm",
   "email":"user@gmail.com",
   "subject":"subject",
   "body":"body ",
   "verifyCode":null
}

要点

在设计良好的应用程序中,模型通常比控制器快得多。模型应该 -

  • 包含业务逻辑。
  • 包含验证规则。
  • 包含属性。
  • 不嵌入 HTML。
  • 不直接访问请求。
  • 没有太多的场景。

Yii - 小部件

小部件是可重用的客户端代码,其中包含 HTML、CSS 和 JS。该代码包含最少的逻辑,并包装在yii\base\Widget对象中。我们可以轻松地在任何视图中插入并应用该对象。

步骤 1 - 要查看正在运行的小部件,请使用以下代码在SiteController中创建一个actionTestWidget函数。

public function actionTestWidget() { 
   return $this->render('testwidget'); 
}

在上面的例子中,我们只是返回了一个名为“testwidget”的视图

步骤2 - 现在,在views/site文件夹中,创建一个名为testwidget.php的视图文件。

<?php 
   use yii\bootstrap\Progress; 
?> 
<?= Progress::widget(['percent' => 60, 'label' => 'Progress 60%']) ?>

步骤 3 - 如果您访问http://localhost:8080/index.php?r=site/test-widget,您将看到进度条小部件。

进度条

使用小部件

要在View中使用小部件,您应该调用yii\base\Widget::widget()函数。该函数采用一个配置数组来初始化小部件。在前面的示例中,我们插入了一个进度条,其中包含配置对象的百分比和标记参数。

有些小部件会占用一块内容。它应该包含在yii\base\Widget::begin()yii\base\Widget::end()函数之间。例如,以下小部件显示联系表单 -

<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?> 
   <?= $form->field($model, 'name') ?> 
   <?= $form->field($model, 'email') ?> 
   <?= $form->field($model, 'subject') ?> 
   <?= $form->field($model, 'body')->textArea(['rows' => 6]) ?> 
   <?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [ 
      'template' =>
         '<div class="row">
            <div class = "col-lg-3">{image}</div>
            <div class = "col-lg-6">{input}</div>
         </div>', 
   ]) ?> 
   <div class = "form-group"> 
      <?= Html::submitButton('Submit', ['class' => 'btn btn-primary',
         'name' => 'contact-button']) ?> 
   </div> 
<?php ActiveForm::end(); ?> 

创建小部件

要创建小部件,您应该从yii\base\Widget扩展。然后你应该重写yii\base\Widget::init()yii\base\Widget::run()函数。run ()函数应该返回渲染结果。init ()函数应该规范小部件属性。

步骤 1 - 在项目根目录中创建一个组件文件夹。在该文件夹中,使用以下代码创建一个名为FirstWidget.php的文件。

<?php 
   namespace app\components; 
   use yii\base\Widget; 
   class FirstWidget extends Widget { 
      public $mes; 
      public function init() { 
         parent::init(); 
         if ($this->mes === null) { 
            $this->mes = 'First Widget'; 
         } 
      }  
      public function run() { 
         return "<h1>$this->mes</h1>"; 
      } 
   } 
?>

步骤 2 -按以下方式修改testwidget视图。

<?php 
   use app\components\FirstWidget; 
?> 
<?= FirstWidget∷widget() ?>

步骤 3 - 转到http://localhost:8080/index.php?r=site/test-widget。您将看到以下内容。

第一个小部件

步骤 4 - 要包含begin()end()调用之间的内容,您应该修改FirstWidget.php文件。

<?php
   namespace app\components;
   use yii\base\Widget;
   class FirstWidget extends Widget {
      public function init() {
         parent::init();
         ob_start();
      }
      public function run() {
         $content = ob_get_clean();
         return "<h1>$content</h1>";
      }
   }
?> 

步骤 5 - 现在 h1 标签将包围所有内容。请注意,我们使用ob_start()函数来缓冲输出。修改 testwidget 视图,如以下代码所示。

<?php
   use app\components\FirstWidget;
?>
<?php FirstWidget::begin(); ?>
   First Widget in H1
<?php FirstWidget::end(); ?>

您将看到以下输出 -

H1 中的第一个小部件

要点

小部件应该 -

  • 按照 MVC 模式创建。您应该将表示层保留在视图中,并将逻辑保留在小部件类中。

  • 设计为独立的。最终开发人员应该能够将其设计成视图。

Yii - 模块

模块是具有自己的模型、视图、控制器和可能的其他模块的实体。它实际上是应用程序内的应用程序。

步骤 1 -在项目根目录中创建一个名为“modules”的文件夹。在 module 文件夹中,创建一个名为hello的文件夹。这将是我们的 Hello 模块的基本文件夹。

步骤 2 - 在hello文件夹内,使用以下代码创建文件Hello.php 。

<?php
   namespace app\modules\hello;
   class Hello extends \yii\base\Module {
      public function init() {
         parent::init();
      }
   }
?>

我们刚刚创建了一个模块类。它应该位于模块的基本路径下。每次访问模块时,都会创建相应模块类的实例。init ()函数用于初始化模块的属性。

步骤 3 - 现在,在 hello 文件夹中添加两个目录 - 控制器和视图。将CustomController.php文件添加到控制器的文件夹中。

<?php
   namespace app\modules\hello\controllers;
   use yii\web\Controller;
   class CustomController extends Controller {
      public function actionGreet() {
         return $this->render('greet');
      }
   }
?>

创建模块时,约定是将控制器类放入模块基本路径的控制器目录中。我们刚刚定义了actionGreet函数,它只返回一个问候视图。

模块中的视图应放置在模块基本路径的views 文件夹中。如果视图由控制器渲染,它们应该位于与controllerID对应的文件夹中。将自定义文件夹添加到视图文件夹。

步骤 4 - 在自定义目录中,使用以下代码创建一个名为greet.php的文件。

<h1>Hello world from custom module!</h1>

我们刚刚为我们的actionGreet创建了一个视图。要使用这个新创建的模块,我们应该配置应用程序。我们应该将模块添加到应用程序的 module 属性中。

步骤 5 - 修改config/web.php文件。

<?php
   $params = require(__DIR__ . '/params.php');
   $config = [
      'id' => 'basic',
      'basePath' => dirname(__DIR__),
      'bootstrap' => ['log'],
      'components' => [
         'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is
               //required by cookie validation
            'cookieValidationKey' => 'ymoaYrebZHa8gURuolioHGlK8fLXCKjO',
         ],
         'cache' => [
            'class' => 'yii\caching\FileCache',
         ],
         'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,
         ],
         'errorHandler' => [
            'errorAction' => 'site/error',
         ],
         'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            // send all mails to a file by default. You have to set
            // 'useFileTransport' to false and configure a transport
            // for the mailer to send real emails.
            'useFileTransport' => true,
         ],
         'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
               [
                  'class' => 'yii\log\FileTarget',
                  'levels' => ['error', 'warning'],
               ],
            ],
         ],
         'db' => require(__DIR__ . '/db.php'),
      ],
      'modules' => [
         'hello' => [
            'class' => 'app\modules\hello\Hello', 
         ],
      ],
      'params' => $params,
   ];
   if (YII_ENV_DEV) {
      // configuration adjustments for 'dev' environment
      $config['bootstrap'][] = 'debug';
      $config['modules']['debug'] = [
         'class' => 'yii\debug\Module',
      ];
      $config['bootstrap'][] = 'gii';
      $config['modules']['gii'] = [
         'class' => 'yii\gii\Module',
      ];
   }
   return $config;
?>

模块控制器的路由必须以模块 ID 开头,后跟控制器 ID 和操作 ID。

步骤 6 - 要在我们的应用程序中运行 actionGreet 我们应该使用以下路由。

hello/custom/greet

其中 hello 是模块 ID,custom 是控制器 ID,greet 是操作 ID

步骤 7 - 现在,输入http://localhost:8080/index.php?r=hello/custom/greet,您将看到以下输出。

定制模块

要点

模块应该 -

  • 用于大型应用程序。您应该将其功能分为几组。每个功能组都可以开发为一个模块。

  • 可重复使用。一些常用的功能,例如SEO管理或博客管理,可以开发为模块,以便您可以在将来的项目中轻松重用它们。

Yii - 视图

视图负责向最终用户呈现数据。在 Web 应用程序中,视图只是包含 HTML 和 PHP 代码的 PHP 脚本文件。

创建视图

步骤 1 - 让我们看一下基本应用程序模板的“关于”视图。

<?php
   /* @var $this yii\web\View */
   use yii\helpers\Html;
   $this->title = 'About';
   $this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-about">
   <h1><?= Html::encode($this->title) ?></h1>
   <p>
      This is the About page. You may modify the following file to customize its content:
   </p>
   <code><?= __FILE__ ?></code>
</div>

$ this变量指的是管理和呈现此视图模板的视图组件。

这就是“关于”页面的样子 -

关于页面

为了避免 XSS 攻击,对来自最终用户的数据进行编码和/或过滤非常重要。您应该始终通过调用yii\helpers\Html::encode()对纯文本进行编码,并通过调用yii\helpers\HtmlPurifier 对HTML 内容进行编码。

步骤 2 -按以下方式修改“关于”视图。

<?php
   /* @var $this yii\web\View */
   use yii\helpers\Html;
   use yii\helpers\HtmlPurifier;
   $this->title = 'About';
   $this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-about">
   <h1><?= Html::encode($this->title) ?></h1>
   <p>
      This is the About page. You may modify the following file to customize its content:
   </p>
   <p>
      <?= Html::encode("<script>alert('alert!');</script><h1>ENCODE EXAMPLE</h1>>") ?>
   </p>
   <p>
      <?= HtmlPurifier::process("<script>alert('alert!');</script><h1> HtmlPurifier EXAMPLE</h1>") ?>
   </p>
   <code><?= __FILE__ ?></code>
</div>

步骤 3 - 现在输入http://localhost:8080/index.php?r=site/about。您将看到以下屏幕。

关于视图

请注意, Html::encode()函数内的 javascript 代码显示为纯文本。HtmlPurifier::process()调用也是如此。仅显示 h1 标签。

视图遵循这些约定 -

  • 由控制器呈现的视图应放入@app/views/controllerID文件夹中。

  • 在小部件中呈现的视图应放入widgetPath/views 文件夹中。

要在控制器中渲染视图,您可以使用以下方法 -

  • render() - 渲染视图并应用布局。

  • renderPartial() - 渲染没有布局的视图。

  • renderAjax() - 渲染没有布局的视图,但注入所有注册的 js 和 css 文件。

  • renderFile() - 在给定文件路径或别名中渲染视图。

  • renderContent() - 渲染静态字符串并应用布局。

要在另一个视图中渲染一个视图,您可以使用以下方法 -

  • render() - 渲染视图。

  • renderAjax() - 渲染没有布局的视图,但注入所有注册的 js 和 css 文件。

  • renderFile() - 在给定文件路径或别名中渲染视图。

步骤 4 - 在views/site文件夹中,创建两个视图文件:_part1.php 和 _part2.php

_part1.php -

<h1>PART 1</h1>

_part2.php -

<h1>PART 2</h1>

步骤 5 - 最后,在“关于”视图中渲染这两个新创建的视图。

<?php
   /* @var $this yii\web\View */
   use yii\helpers\Html;
   $this->title = 'About';
   $this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-about">
   <h1><?= Html::encode($this->title) ?></h1>
   <p>
      This is the About page. You may modify the following file to customize its content:
   </p>
   <?= $this->render("_part1") ?>
   <?= $this->render("_part2") ?>
   <code><?= __FILE__ ?></code>
</div>

您将看到以下输出 -

创建视图文件

渲染视图时,您可以使用视图名称或视图文件路径/别名来定义视图。视图名称通过以下方式解析 -

  • 视图名称可以省略扩展名。例如,about 视图对应about.php 文件。

  • 如果视图名称以“/”开头,那么如果当前活动模块是forum,并且视图名称是comment/post,则路径将为@app/modules/forum/views/comment/post。如果没有活动模块,路径将为@app/views/comment/post。

  • 如果视图名称以“//”开头,则对应的路径为@app/views/ViewName。例如,//site/contact 对应于@app/views/site/contact.php。

  • 如果视图名称是 contact,上下文控制器是 SiteController,则路径将为 @app/views/site/contact.php。

  • 如果价格视图在商品视图中呈现,则价格将解析为@app/views/invoice/price.php(如果在@app/views/invoice/goods.php 中呈现)。

访问视图中的数据

要访问视图中的数据,您应该将数据作为第二个参数传递给视图渲染方法。

步骤 1 - 修改SiteControlleractionAbout

public function actionAbout() {
   $email = "admin@support.com";
   $phone = "+78007898100";
   return $this->render('about',[
      'email' => $email,
      'phone' => $phone
   ]);
}

在上面给出的代码中,我们传递两个变量$email$phone以在“关于”视图中呈现。

步骤 2 - 更改关于视图代码。

<?php
   /* @var $this yii\web\View */
   use yii\helpers\Html;
   $this->title = 'About';
   $this->params['breadcrumbs'][] = $this->title;
?>
<div class = "site-about">
   <h1><?= Html::encode($this->title) ?></h1>
   <p>
      This is the About page. You may modify the following file to customize its content:
   </p>
   <p>
      <b>email:</b> <?= $email ?>
   </p>
   <p>
      <b>phone:</b> <?= $phone ?>
   </p>
   <code><?= __FILE__ ?></code>
</div>

我们刚刚添加了从SiteController收到的两个变量。

步骤 3 -在网络浏览器中输入 URL http://localhost:8080/index.php?r=site/about ,您将看到以下内容。

更改关于查看代码

Yii - 布局

布局表示多个视图的公共部分,例如页眉和页脚。默认情况下,布局应存储在views/layouts文件夹中。

让我们看一下基本应用程序模板的主要布局 -

<?php
   /* @var $this \yii\web\View */
   /* @var $content string */
   use yii\helpers\Html;
   use yii\bootstrap\Nav;
   use yii\bootstrap\NavBar;
   use yii\widgets\Breadcrumbs;
   use app\assets\AppAsset;
   AppAsset::register($this);
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang = "<?= Yii::$app->language ?>">
   <head>
      <meta charset = "<?= Yii::$app->charset ?>">
      <meta name = "viewport" content = "width = device-width, initial-scale = 1">
      <?= Html::csrfMetaTags() ?>
      <title><?= Html::encode($this->title) ?></title>
      <?php $this->head() ?>
   </head>
   <body>
      <?php $this->beginBody() ?>
         <div class = "wrap">
            <?php
               NavBar::begin([
                  'brandLabel' => 'My Company',
                  'brandUrl' => Yii::$app->homeUrl,
                  'options' => [
                     'class' => 'navbar-inverse navbar-fixed-top',
                  ],
               ]);
               echo Nav::widget([
                  'options' => ['class' => 'navbar-nav navbar-right'],
                  'items' => [
                     ['label' => 'Home', 'url' => ['/site/index']],
                     ['label' => 'About', 'url' => ['/site/about']],
                     ['label' => 'Contact', 'url' => ['/site/contact']],
                     Yii::$app->user->isGuest ?
                        ['label' => 'Login', 'url' => ['/site/login']] :
                        [
                           'label' => 'Logout (' . Yii::$app->user->identity->username.')',
                           'url' => ['/site/logout'],
                           'linkOptions' => ['data-method' => 'post']
                        ],
                  ],
               ]);
               NavBar::end();
            ?>
            <div class = "container">
               <?= Breadcrumbs::widget([
                  'links' => isset($this->params['breadcrumbs']) ? $this>params
                     ['breadcrumbs'] : [],
               ]) ?>
               <?= $content ?>
            </div>
         </div>
         <footer class = "footer">
            <div class = "container">
               <p class = "pull-left">© My Company <?= date('Y') ?></p&g