Yii2应用结构笔记

入口脚本

入口脚本主要完成以下工作

  • 定义全局常量;
  • 注册 Composer 自动加载器;
  • 包含 Yii 类文件;
  • 加载应用配置;
  • 创建一个应用实例并配置;
  • 调用 yii\base\Application::run() 来处理请求。
run();

应用

1、应用主题

Application

$app = (new yii\web\Application($config));

2、应用主体配置

$config = require __DIR__ . '/../config/web.php';

3、应用主体属性

config/web.php

 'basic',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => 'oG32SGLA71IQQdd50xmTNWMNyPEdiVHR',
        ],
        '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' => $db,
        /*
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],
        */
    ],
    'params' => $params,
];

if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = [
        'class' => 'yii\debug\Module',
        // uncomment the following to add your IP if you are not connecting from localhost.
        //'allowedIPs' => ['127.0.0.1', '::1'],
    ];

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
        // uncomment the following to add your IP if you are not connecting from localhost.
        //'allowedIPs' => ['127.0.0.1', '::1'],
    ];
}

return $config;

4、生命周期

当运行 入口脚本 处理请求时, 应用主体会经历以下生命周期:

  1. 入口脚本加载应用主体配置数组。

  2. 入口脚本创建一个应用主体实例:

    • 调用 preInit() 配置几个高级别应用主体属性, 比如 basePath。
    • 注册 error handler 错误处理方法。
    • 配置应用主体属性。
    • 调用 init() 初始化,该函数会调用 bootstrap() 运行引导启动组件。
  3. 入口脚本调用

yii\base\Application::run()

运行应用主体:

  • 触发 EVENT_BEFORE_REQUEST 事件。
  • 处理请求:解析请求 路由 和相关参数; 创建路由指定的模块、控制器和动作对应的类,并运行动作。
  • 触发 EVENT_AFTER_REQUEST 事件。
  • 发送响应到终端用户。
  1. 入口脚本接收应用主体传来的退出状态并完成请求的处理。
image-20201021190341519.png

basic\vendor\yiisoft\yii2\base\Application.php

public function __construct($config = [])
{
    
    //入口脚本创建一个应用主体实例:
    Yii::$app = $this;
    static::setInstance($this);

    
    $this->state = self::STATE_BEGIN;
    
    //调用 preInit()配置几个高级别应用主体属性, 比如 basePath
    $this->preInit($config);

    //注册 error handler错误处理方法
    $this->registerErrorHandler($config);

    //配置应用主体属性。
    Component::__construct($config);
}


public function init()
{
    $this->state = self::STATE_INIT;
    $this->bootstrap();
}

basic\vendor\yiisoft\yii2\base\BaseObject.php

public function __construct($config = [])
{
    if (!empty($config)) {
        Yii::configure($this, $config);
    }
    //调用init() 初始化,该函数会调用 bootstrap()运行引导启动组件。
    $this->init();//这里调用base\Application.php的init
}

应用组件

可以使用 \Yii::$app->db 来获取到已注册到应用的 DB connection, 使用 \Yii::$app->cache 来获取到已注册到应用的 primary cache。

第一次使用以上表达式时候会创建应用组件实例, 后续再访问会返回此实例,无需再次创建。

应用组件可以是任意对象,可以在 应用主体配置配置 yii\base\Application::$components 属性。

[
    'components' => [
        // 使用类名注册 "cache" 组件
        'cache' => 'yii\caching\ApcCache',

        // 使用配置数组注册 "db" 组件
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=demo',
            'username' => 'root',
            'password' => '',
        ],

        // 使用函数注册"search" 组件
        'search' => function () {
            return new app\components\SolrService;
        },
    ],
]

控制器

控制器是 MVC 模式中的一部分, 是继承yii\base\Controller类的对象,负责处理请求和生成响应。 具体来说,控制器从应用主体 接管控制后会分析请求数据并传送到模型, 传送模型结果到视图,最后生成输出响应信息。

控制器Id可包含子目录前缀,例如 sub/test 代表 controller namespace 控制器命名空间下 sub子目录中 test 控制器。 子目录前缀可为英文大小写字母、数字、下划线、正斜杠,其中正斜杠用来区分多级子目录(如panels/admin`)。

image-20201022104124043.png

独立动作

暂时不知道有啥用?

动作参数

 string(1) "1" }
    public function actionView(array $id, $message)
    {
        var_dump($id,$message);
    }
}

默认动作

每个控制器都有一个由 yii\base\Controller::$defaultAction属性指定的默认操作, 当路由只包含控制器ID, 会使用所请求的控制器的默认操作。

控制器的生命周期

处理一个请求时,应用主体 会根据请求 路由创建一个控制器, 控制器经过以下生命周期来完成请求:

  1. 在控制器创建和配置后,yii\base\Controller::init() 方法会被调用。

  2. 控制器根据请求操作ID创建一个操作对象:

  • 如果操作ID没有指定,会使用default action ID默认操作ID;
  • 如果在action map找到操作ID, 会创建一个独立操作;
  • 如果操作ID对应操作方法,会创建一个内联操作;
  • 否则会抛出yii\base\InvalidRouteException异常。
  1. 控制器按顺序调用应用主体、模块(如果控制器属于模块)、 控制器的beforeAction()

方法;

  • 如果任意一个调用返回false,后面未调用的beforeAction()会跳过并且操作执行会被取消; action execution will be cancelled.
  • 默认情况下每个 beforeAction() 方法会触发一个 beforeAction 事件,在事件中你可以追加事件处理操作;
  1. 控制器执行操作:
  • 请求数据解析和填入到操作参数;
  1. 控制器按顺序调用控制器、模块(如果控制器属于模块)、应用主体的afterAction()

方法;

  • 默认情况下每个 afterAction() 方法会触发一个 afterAction 事件, 在事件中你可以追加事件处理操作;
  1. 应用主体获取操作结果并赋值给响应.

模型

  • 属性

可以像访问对象属性一样访问模型属性

也可以像访问数组下标一样访问模型的属性

$model = new \app\models\ContactForm;

// "name" 是ContactForm模型的属性
$model->name = 'example';
echo $model->name;

// 像访问数组单元项一样访问属性
$model['name'] = 'example';
echo $model['name'];
  • 定义属性
namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
}
  • 属性标签

默认情况下,属性标签通过yii\base\Model::generateAttributeLabel()方法自动从属性名生成. 它会自动将驼峰式大小写变量名转换为多个首字母大写的单词, 例如 username 转换为 UsernamefirstName 转换为 First Name

echo  $model->getAttributeLabel('verifyCode');
//以上代码输出为
//Verify Code

//你也可以在当前模型中自定义标签,重写该方法即可
public function attributeLabels()
{
    return [
        'verifyCode' => 'Verification Code',
    ];
}

//这样再次执行 echo  $model->getAttributeLabel('verifyCode')
//输出将会是
//Verification Code
  • 验证规则
$model = new \app\models\ContactForm;

// 用户输入数据赋值到模型属性
$model->attributes = \Yii::$app->request->post();

if ($model->validate()) {
    // 所有输入数据都有效 all inputs are valid
} else {
    // 验证失败:$errors 是一个包含错误信息的数组
    $errors = $model->errors;
    var_dump($errors);die;
}
  • 块赋值

块赋值只用一行代码将用户所有输入填充到一个模型,非常方便, 它直接将输入数据对应填充到 yii\base\Model::attributes() 属性

$model->attributes = \Yii::$app->request->post();
  • 安全属性
  • 非安全属性
  • 数据导出
  • 字段

视图

1、创建视图


    




field($model, 'name') ?>

field($model, 'email') ?>

field($model, 'age') ?>


'btn btn-primary']) ?>

2、安全

当创建生成HTML页面的视图时,在显示之前将用户输入数据进行转码和过滤非常重要, 否则,你的应用可能会被 跨站脚本 攻击。

使用yii\helpers\Html::encode()进行转码



3、渲染视图

在控制器中 app\controllers\PostController 调用 $this->render('view'), 实际上渲染 @app/views/post/view.php 视图文件,当在该视图文件中调用 $this->render('_overview') 会渲染 @app/views/post/_overview.php 视图文件。

4、视图中访问数据

推送和拉取

推送:通过render等渲染视图方法的第二个参数传递数据

拉取:拉取方式可让视图从view component视图组件或其他对象中主动获得数据(如Yii::$app), 在视图中使用如下表达式$this->context可获取到控制器ID, 可让你在report视图中获取控制器的任意属性或方法, 如以下代码获取控制器ID。


context->id ?> //输出当前控制器ID
context->behaviors());echo '
';?>

5、布局

布局是一种特殊的视图,代表多个视图的公共部分, 例如,大多数Web应用共享相同的页头和页尾, 在每个视图中重复相同的页头和页尾,更好的方式是将这些公共放到一个布局中, 渲染内容视图后在合适的地方嵌入到布局中。

可配置yii\base\Application::$layoutyii\base\Controller::$layout 使用其他布局文件, 前者管理所有控制器的布局,后者覆盖前者来控制单个控制器布局。 例如,如下代码使 post 控制器渲染视图时使用 @app/views/layouts/post.php 作为布局文件, 假如 layout 属性没改变, 控制器默认使用 @app/views/layouts/main.php 作为布局文件。

namespace app\controllers;

use yii\web\Controller;

class PostController extends Controller
{
    public $layout = 'post';
    
    // ...
}

嵌套布局????

设置页面标题

title = 'My page title';
?>
<?= Html::encode($this->title) ?>

注册Meta标签

registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);
?>

注册链接标签

$this->registerLinkTag([
    'title' => 'Live News for Yii',
    'rel' => 'alternate',
    'type' => 'application/rss+xml',
    'href' => 'http://www.yiiframework.com/rss.xml/',
]);

//上述代码会转为

6、渲染静态页面

如果Web站点包含很多静态页面,多次重复相似的代码显得很繁琐, 为解决这个问题,可以使用一个在控制器中称为 yii\web\ViewAction 的独立动作

如:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    //独立动作  如果
    public function actions()
    {
        return [
            'page' => [
                'class' => 'yii\web\ViewAction',
            ],
        ];
    }
}

如果在在@app/views/site/pages目录下创建名为about的视图,可以通过以下url来显示

index.php?r=site/page&view=index

GET 中 view 参数告知 yii\web\ViewAction 动作请求哪个视图,然后操作在 @app/views/site/pages目录下寻找该视图。可配置 yii\web\ViewAction::$viewPrefix 修改搜索视图的目录,默认是pages

最佳实践 ¶

视图负责将模型的数据展示用户想要的格式,总之,视图

  • 应主要包含展示代码,如HTML, 和简单的PHP代码来控制、格式化和渲染数据;
  • 不应包含执行数据查询代码,这种代码放在模型中;
  • 应避免直接访问请求数据,如 $_GET, $_POST,这种应在控制器中执行, 如果需要请求数据,应由控制器推送到视图。
  • 可读取模型属性,但不应修改它们。

为使模型更易于维护,避免创建太复杂或包含太多冗余代码的视图, 可遵循以下方法达到这个目标:

  • 使用 布局 来展示公共代码(如,页面头部、尾部);
  • 将复杂的视图分成几个小视图,可使用上面描述的渲染方法将这些 小视图渲染并组装成大视图;
  • 创建并使用 小部件 作为视图的数据块;
  • 创建并使用助手类在视图中转换和格式化数据。

模块 Modules

模块是独立的软件单元,由模型,视图, 控制器和其他支持组件组成, 终端用户可以访问在应用主体中已安装的模块的控制器, 模块被当成小应用主体来看待,和应用主体不同的是, 模块不能单独部署,必须属于某个应用主体。

Modules目录结构
——------------------------------------------------
forum/
    Module.php                   模块类文件
    controllers/                 包含控制器类文件
        DefaultController.php    default 控制器类文件
    models/                      包含模型类文件
    views/                       包含控制器视图文件和布局文件
        layouts/                 包含布局文件
        default/                 包含 DefaultController 控制器视图文件
            index.php            index 视图文件

官方文档:https://www.yiichina.com/doc/guide/2.0/structure-modules

更详细

  • 使用模块

要在应用中使用模块,只需要将模块加入到应用主体配置modules属性的列表中, 如下代码的应用主体配置 使用 forum模块:

[
    'modules' => [
        'forum' => [
            'class' => 'app\modules\forum\Module',
            // ... 模块其他配置 ...
        ],
    ],
]
  • 路由

和访问应用的控制器类似,路由 也用在模块中控制器的寻址, 模块中控制器的路由必须以模块 ID 开始,接下来为控制器 ID 和操作 ID。 例如,假定应用使用一个名为 forum 模块, 路由forum/post/index 代表模块中 post 控制器的 index 操作, 如果路由只包含模块ID,默认为 default 的 [yii\base\Module::defaultRoute-detail) 属性来决定使用哪个控制器/操作, 也就是说路由 forum 可能代表 forum 模块的 default 控制器。

实际操作

1、新建模块目录Modules,在该目录下新建模块目录forum

image-20201023152257590.png

forum就是一个模块了

2、新建如下

image-20201023152421174.png

3、新建模块的控制器

PostController

4、新建Module.php

每个模块都有一个继承 yii\base\Module 的模块类, 该类文件直接放在模块的 base path 目录下, 并且能被 自动加载。当一个模块被访问, 和 应用主体实例 类似会创建该模块类唯一实例,模块实例用来帮模块内代码共享数据和组件。

params['foo'] = 'bar';
        // ...  其他初始化代码 ...
        
        //如果 init() 方法包含很多初始化模块属性代码, 可将他们保存在配置 并在 init() 中使用以下代码加载:
        
        // 从config.php 加载配置来初始化模块
        \Yii::configure($this, require __DIR__ . '/config.php');
    }
}

5、config.php配置文件包含以下内容

 [
        // list of component configurations
    ],
    'params' => [
        // list of parameters
    ],
];

过滤器???中间件??

小部件

例如 一个jui部件的使用

先安装jui

composer require --prefer-dist yiisoft/yii2-jui

//./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 2 installs, 0 updates, 0 removals
  - Installing bower-asset/jquery-ui (1.12.1): Downloading (100%)
  - Installing yiisoft/yii2-jui (2.0.7): Downloading (100%)
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating autoload files
28 packages you are using are looking for funding.
Use the `composer fund` command to find out more!




 'date']) ?>

效果:

image-20201023161426951.png

使用小部件

1、在SiteController中新增动作

public function actionForm()
{
    $model = new Form();
    //将表单 交给Form模型进行验证
    if($model->load(Yii::$app->request->post()) && $model->validate())
    {
        //todo...
        echo '
';
        var_dump(Yii::$app->request->post());die;
    }
    //显示表单
    return $this->render('form',['model'=>$model]);

}

2、新增form模型,并且定义三个属性

3、新建视图site/form.php



 'form']); ?>

    field($model, 'username') ?>

    field($model, 'password')->passwordInput() ?>


    field($model, 'date')->widget(DatePicker::class,[
        'language' => 'cn',
        'dateFormat' => 'php:Y/m/d',//按照这个格式传参
    ])?>


    

效果:

image-20201023171409622.png
image-20201023171421911.png

创建小部件

1、创建部件目录components

image-20201023174608014.png

2、在components目录下新建一个小部件类,继承\yii\base\Widget

render('hello',['content'=>$content]);
    }
}

当小部件需要渲染很多内容,虽然你可以在 run() 方法中嵌入内容,但更好的方法是将内容放入一个视图文件, 然后调用 yii\base\Widget::render() 方法渲染该视图文件

小部件的视图一般都存放在components/views

3、直接在site/form.php文件中使用我们的小部件HelloWidget

如下:


    

4、 在SiteController中创建动作

public function actionForm()
{
    $model = new Form();

    if($model->load(Yii::$app->request->post()) && $model->validate())
    {
        //todo...
        echo '
';
        var_dump(Yii::$app->request->post());die;
    }
    //$variable作为参数 将会交给 小部件作为内容输出
    return $this->render('form',['model'=>$model,'variable'=>'这块内容是将要输出的内容 也就是在run方法中$content会取到的内容,如何将$content作为参数传递给小部件视图hello,我们可以将我们视图的布局,数据渲染都交给hello处理']);

}

5、创建小部件视图components/views/hello.php

';

?>



    
    
    
    Document


    

效果:

image-20201023180421777.png

你可能感兴趣的:(Yii2应用结构笔记)