【Yii系列】Yii2.0基础框架

缘起

由于一个月的短暂停留,我在给朋友搞事情,因此Yii系列的文章耽搁了很长时间,如今又重拾当时的知识,给大伙好好撸下这一系列的博客php

提起Yii,虽然是国外的开发者搞的,可是它的做者是华人,这才是让咱们引觉得豪的,若是之后有机会薛强回国大力发展PHP事业,我确定回去他麾下搞事情,为PHP在国内的发展贡献本身的一份力,虽然如今没有这个能力,这不薛强没回来嘛,回来的话那时的我确定能够的~哈哈哈~前端

领英上强哥的背景以下:java

职务:MicroStrategy 公司担任构架师mysql

地址:美国首都华盛顿 Metro Areaweb

教育:sql

  • 杜克大学·计算机科学·博士
  • 浙江大学·计算机科学·硕士
强哥是土生土长的中国人,在美国有固定全职工做,英文很好,同时中文一点问题都没有!!

好了,话步前言,上节咱们已经将Yii2.0完整的安装到咱们的机器中啦,在浏览器中输入下面的地址,你就能够访问你的服务器应用啦~数据库

http://服务器IP/app/yii/web/index.php

出现下面的命令那就对啦~json

总体结构

assets文件夹:assets的做用是方便模块化,插件化的,通常来讲出于安全缘由不容许经过url访问protected下面的文件 ,可是咱们又但愿将module单独出来,因此须要使用发布,即将一个目录下的文件复制一份到assets下面方便经过url访问。bootstrap

commands文件夹:控制台脚本存放的地方,自动运行脚本swift

config文件夹:配置文件存放的文件夹

controller文件夹:MVC中C文件存放的文件夹

mail文件夹:邮件发送目录,具体干啥的我还在摸索中哈~

models文件夹:MVC中M文件存放的文件夹

runtime:日志文件

tests:测试脚本文件夹

vendor:第三方组件存放,composer下载的组件存放的文件夹,自动帮你autoload

views:MVC中V存放的文件夹

web:web主应用入口脚本存放的位置

以上是整个文件夹的布局,里面的各个文件有啥用,我会在后续的【应用Yii2.0搭建后台应用框架】中详细介绍

我这一系列的文章均来自于Yii中文网中对Yii2.0的权威指南,感谢国内开发者对社区的贡献,感谢翻译的人,让咱们免去了百度翻译之苦。

若是你们不太喜欢个人描述,能够去看一下官方对Yii2.0的解释,很详细,但很官方,不会调戏你^_^

官方文档【中文版】:http://www.yiichina.com/doc/guide/2.0

Yii 应用参照模型-视图-控制器 (MVC)设计模式来组织。不懂MVC?这么说吧,无论是前端应用仍是后端应用,首当其冲的设计模式就是MVC。因此了解它至关有必要!

M模型表明数据、业务逻辑和规则;V视图展现模型的输出;C控制器接受出入并将其转换为模型和视图命令。

这就是Yii的整个框架结构设计,咱们的MVC就是其中的控制器,视图和模型,他们的各自做用上面也讲了下,通常的后端应用,M表示从数据库、第三方连接、本地文件中获取的数据进行处理,整理,在交给到V端,V端的做用通常是在页面中反馈给用户的页面,若是是以数据的形式返回给用户,那这个V层就不用作过多的渲染。C层的话主要是链接二者的做用,C层获取到用户的请求,传给M层,M层处理好数据,反馈给C层,C层再将数据给到V层,V层展现给用户。MVC模型的便捷之处就是逻辑清晰,每一个模块负责本身的事,有条有理,很是便于初学者理解,是一个入门的模型。

除此以外,Yii还包含其余逻辑处理块,比方说上面图中的入口脚本【调用应用一开始必被调用的脚本文件】,应用主体【Yii::$app全局可访问对象】,应用组件【全局通用的一些工具集】,模块【业务逻辑单元,每一个业务逻辑一个模块,会让代码很清晰】,过滤器【规范行为的对象,在控制器执行以前或以后调用,定义一类特殊的行为】,前端资源和小部件咱们先不讲,由于是涉及到前端的一些组件内容,后面我会单独开辟一个系列来说前端知识,我出这一系列的目的主要是针对后台应用~

入口脚本

心细的朋友可能早就发现了,为啥咱们在上面的访问连接中后面有个index.php,对,就是它,它就是入口脚本,每次web请求都必须通过它!

http://172.16.122.58/app/yii/web/index.php

通常他都是在web这个目录下面的,这个是web应用的入口脚本。

还有个入口脚本是啥呢,控制台脚本,下面的那个叫yii的php脚本,啥做用呢,大家想一想啊,电商后台中,若是有不少人要调整库存,是否是调整一次就给改一次呀,确定不会呀,库存操做若是调用数据库太频繁了,数据库确定扛不住的,咱们的作法就是先放到相似于Redis的缓存中,等到必定量的时候,或者有个1秒钟的时候咱们给同步一次数据库,同步的方式就是调用控制台脚本啦,配合Linux的crontab,完美解决数据库调用过于频繁的问题。控制台脚本后面咱们会介绍,通常业务线中用的还挺多的。

入口脚本主要完成如下工做:

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

// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

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

(new yii\web\Application($config))->run();

入口脚本是定义全局常量的最好地方,话虽如此,不建议在这里定义啥全局变量!Yii 支持如下三个常量:

YII_DEBUG:标识应用是否运行在调试模式。当在调试模式下,应用会保留更多日志信息,若是抛出异常,会显示详细的错误调用堆栈。所以,调试模式主要适合在开发阶段使用,YII_DEBUG 默认值为 false。

YII_ENV:标识应用运行的环境。YII_ENV 默认值为 'prod',表示应用运行在线上产品环境。

YII_ENABLE_ERROR_HANDLER:标识是否启用 Yii 提供的错误处理,默认为 true。

autoload.php,读过我以前文章的朋友确定对他有印象,PSR-4,自动加载器哈,这个是注册composer自动加载器的。

Yii.php,包含Yii类的文件路径。

倒数第二行是加载应用配置。最后一行是运行一个应用,这里面的$config【web.php】这个文件会在后面详细解答。

应用主体配置

应用主体在入口脚本中建立并能经过表达式 \Yii::$app 全局范围内访问。访问的变量定义在哪儿呢,因为应用主体配置比较复杂,就是刚刚提到的$config,config文件夹中的web.php文件。后面比较复杂的配置均可以放到单个文件中,这是个技巧,即减小了配置文件的代码行数,也将整个框架清晰不少。

这个里面定义了不少属性,咱们来分别看下吧。

<?php

$params = require(__DIR__ . '/params.php');

$config = [

params这个参数里的全部变量就被定义在params.php这个文件里面。

下面是个人项目中配置的一些文件在上方定义

<?php

$params = require(__DIR__ . '/params.php');
$rules = require(__DIR__ . '/rules.php');
$aliases = require(__DIR__ . '/aliases.php');
$cacheConfig = require(__DIR__ . '/cache.php');

这里面主要是params参数,rules路由规则,aliases别名规则,cacheConfig缓存配置,你可能会怀疑,为什么db这么关键的没有配置上来,db是区分环境的,在index.php中会区分stable环境、pro环境仍是测试环境作区分。

咱们仍是回到咱们的Yii源码,配置文件上方配置了params所在的文件,

$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' => 'A9BMCrvbxuCEnE39rVpOUECgcBJTnzUH',
        ],
        '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'),
        /*
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],
        */
    ],
    'params' => $params,
];

这边有几个属性,id、basePath、bootstrap和components。其余还有几个比较重要的属性:aliases、language、modules。

id

yii\base\Application::id 属性用来区分其余应用的惟一标识ID。通常配置为程序名称。必要属性之一。

basePath

yii\base\Application::basePath 指定该应用的根目录。系统预约义 @app 表明这个路径。 你若是须要require目录内的文件,可使用这个方式找到对应文件。另一个必要属性,这个是必须得配置的。

bootstrap

这个属性很实用,它容许你用数组指定启动阶段yii\base\Application::bootstrap()须要运行的组件。通常后端应用中配置个

    'bootstrap' => ['log'],

便可。

components

这是最重要的属性,它容许你注册多个在其余地方使用的应用组件。比方说session、log、db和cache啊,都在这里面配置的,具体的下一节应用组件中会讲到。

aliases

该属性容许你用一个数组定义多个别名。数组的key为别名名称,值为对应的路径。

[
    'aliases' => [
        '@name1' => 'path/to/path1',
        '@name2' => 'path/to/path2',
    ],
]

个人配置里面都是设置的extension扩展类的别名,extension里面放置一些基础调用类,比方说CURL、微信支付等等的。

[
    '@ext' => dirname(__DIR__) . '/extensions',
]

language

该属性指定应用展现给终端用户的语言,默认为 en 标识英文。若是须要以前其余语言能够配置该属性。

    'language' => 'zh-CN',

modules

该属性指定应用所包含的模块。还记得上面说的业务的分割吗,是的,就是在modules里面进行划分的,对应的目录就是根目录下的modules目录,也是须要配置哒~

[
    'modules' => [
        // "booking" 模块以及对应的类
        'booking' => 'app\modules\booking\BookingModule',

        // "comment" 模块以及对应的配置数组
        'comment' => [
            'class' => 'app\modules\comment\CommentModule',
            'db' => 'db',
        ],
    ],
]

个人模块的划分

'modules' => [
        'admin' => [
            'class' => 'app\modules\admin\Admin',
        ],
        'api' => [
            'class' => 'app\modules\api\Api',
        ],
        'user' => [
            'class' => 'app\modules\user\User',
        ],
        'bus' => [
            'class' => 'app\modules\bus\Bus',
        ],
        'goods' => [
            'class' => 'app\modules\goods\Goods',
        ],...
]

这个配置对你代码的结构清晰性有很大的提高,后端应用必配属性。

defaultRoute

该属性指定未配置的请求的响应 路由规则,路由规则可能包含模块ID,控制器ID,动做ID。 例如helppost/createadmin/post/create,若是动做ID没有指定,会使用yii\base\Controller::defaultAction中指定的默认值。

对于 yii\web\Application 网页应用,默认值为 'site' 对应 SiteController 控制器,并使用默认的动做。

对于 yii\console\Application 控制台应用, 默认值为 'help' 对应 yii\console\controllers\HelpController::actionIndex()。

说完了基础的主体配置,其余的主体配置能够去官方文档中查看哟,那里面很详细,不过我仍是针对我使用过的和大伙聊聊。

咱们来看下components组件配置有哪些关键的配置。

应用组件

应用主体配置完毕,咱们来看看其中components有哪些关键配置吧

在同一个应用中,每一个应用组件都有一个独一无二的 ID 用来区分其余应用组件,你能够经过以下表达式访问应用组件

\Yii::$app->componentID

官网给出的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;
        },
    ],
]

这里定义了三个配置属性:cache、db、search,除此以外,比较重要的components属性还有以下几个:urlManager、user、session、errorHandler和log。

cache

表明表明各类缓存存储器,例如内存,文件,数据库。具体会在【Yii系列】操做缓存一章中讲解

db

表明一个能够执行数据库操做的数据库链接。具体会在【Yii系列】链接数据库一章中讲解

search

搜索组件入口配置,之后有机会和你们唠唠solr相关,切割关键字,切割,切割~

urlManager

支持URL地址解析和建立。具体会在【Yii系列】请求处理一章中讲解

user

表明认证登陆用户信息,仅在yii\web\Application 网页应用中可用。若是有后台用户权限认证管理,可去官网了解明细。

session

表明会话信息,仅在yii\web\Application 网页应用中可用,咱们通常都是经过这个来控制用户属性的,具体会在【Yii系列】请求处理一章详细讲解

errorHandler

处理 PHP 错误和异常

log

全局日志配置入口,具体会在【Yii系列】请求处理一章讲解

请谨慎注册太多应用组件,应用组件就像全局变量,使用太多可能加大测试和维护的难度。 通常状况下能够在须要时再建立本地组件。

你们能够着重关注几个配置:db、urlManager、log和cache,这些都是在平常工做中用的比较多的,必配的配置。

控制器

讲完了上面的配置文件,咱们再来看看组成Yii最最关键的部分,也是你们工做过程当中打交道最多的部分啦,请打起大家十二分的精神,看好每个字符哟~

首先咱们来看下MVC中的C,下面咱们就成他为Controller,由于他是入口,就和入口脚本同样,用户发来请求,首先到的就是控制器(只是在后台应用中先到,若是是包含前端资源,用户首先接触到的确定是前端的代码哈)。

Controller的主要责任是接受用户请求,分发处理用户请求,拿到结果数据,递交给V层展现给用户。

继承yii\base\Controller类的对象

Controller由操做【action】组成,它是执行终端用户请求的最基础的单元,一个控制器可有一个或多个操做。

以下示例显示包含两个操做view 和 create 的控制器post

namespace app\controllers;

use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

class PostController extends Controller
{
    public function actionView($id)
    {
        $model = Post::findOne($id);
        if ($model === null) {
            throw new NotFoundHttpException;
        }

        return $this->render('view', [
            'model' => $model,
        ]);
    }

    public function actionCreate()
    {
        $model = new Post;

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }
}

用户访问的时候就是直接被只想到某个action里面的,有这个action方法了,那么咱们如何访问它呢,这边就涉及到一个被称为路由的东西。

终端用户经过所谓的【路由】寻找到操做

路由使用以下格式:

ControllerID/ActionID

模块下的控制器,使用以下格式:

ModuleID/ControllerID/ActionID

若是用户的请求地址为 http://hostname/index.php?r=site/index, 会执行site 控制器的index 操做。是的,有过web基础的朋友可能对这个比较熟悉,?后面的是参数,而这个r参数就表明了访问路径。

那么如何去建立一个控制器呢?

在yii\web\Application网页应用中,控制器应继承yii\web\Controller 或它的子类。

在yii\console\Application控制台应用中,控制器继承yii\console\Controller 或它的子类。

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
}

site就为这个Controller的控制器id。控制器ID一般是和资源有关的名词。

这里面有个事项须要注意下,Yii框架下都是经过驼峰命名法去查找对应的ID的,比方说这边的SiteController,在URL调用中只须要保留site,后面的Controller不须要放到url里面,控制器类必须能被自动加载。

控制器Id可包含子目录前缀,例如 admin/article 表明 yii\base\Application::controllerNamespace控制器命名空间下 admin子目录中 article 控制器。

下面为一些示例,假设yii\base\Application::controllerNamespace控制器命名空间为 app\controllers:

  • article 对应 app\controllers\ArticleController;
  • post-comment 对应 app\controllers\PostCommentController;
  • admin/post-comment 对应 app\controllers\admin\PostCommentController;
  • adminPanels/post-comment 对应 app\controllers\adminPanels\PostCommentController.

每一个应用有一个由yii\base\Application::defaultRoute属性指定的默认控制器; 就是咱们上面讲的那个

[
    'defaultRoute' => 'main'
]

当请求没有指定路由,该属性值做为路由使用。

对于yii\web\Application网页应用,它的值为 'site';

对于 yii\console\Application控制台应用,它的值为 help。

因此URL为 http://hostname/index.php 表示由 site 控制器来处理。

建立action

建立action可简单地在控制器类中定义所谓的操做方法来完成,操做方法必须是以action开头的公有方法。和controller,命名的时候是使用驼峰命名法,访问的时候全小写,若是中间有多个大写字母,用'-'间隔便可。

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public function actionIndex()
    {
        return $this->render('index');
    }

    public function actionHelloWorld()
    {
        return 'Hello World';
    }
}

独立操做咱们不作深究,感兴趣的朋友能够去研究下yii自带的验证码模块源码。

action的参数值从请求中获取,

对于yii\web\Application网页应用, 每一个操做参数的值从$_GET中得到,参数名做为键;通常不会使用$_GET直接去参数,这样不安全。通常使用app的Request去取出用户请求的body和head。

$request = Yii::$app->request;

对于yii\console\Application控制台应用, 操做参数对应命令行参数。

以下例,操做view (内联操做) 申明了两个参数 $id 和 $version

namespace app\controllers;

use yii\web\Controller;

class PostController extends Controller
{
    public function actionView($id, $version = null)
    {
        // ...
    }
}

操做参数会被不一样的参数填入,以下所示:

  • http://hostname/index.php?r=post/view&id=123$id 会填入'123'$version 仍为 null 空由于没有version请求参数;
  • http://hostname/index.php?r=post/view&id=123&version=2: $id 和 $version 分别填入 '123' 和 '2'`;
  • http://hostname/index.php?r=post/view: 会抛出yii\web\BadRequestHttpException 异常 由于请求没有提供参数给必须赋值参数$id
  • http://hostname/index.php?r=post/view&id[]=123: 会抛出yii\web\BadRequestHttpException 异常 由于$id 参数收到数字值 ['123']而不是字符串.

关于Controller的生命周期及其余这边没有提到的,能够去官网看详细的解释。

模型

讲完了入口的Controller,如今咱们来讲说MVC中最最最最重要的数据模型层,这一层负责数据的整合处理,包括业务逻辑控制,从数据库拿出数据,打包数据,缓存操做,返回给Controller等一系列操做,可谓是咱们平常业务逻辑工做中天天大部分时间都在撸的代码了。

模型可经过继承 yii\base\Model 或它的子类定义,基类yii\base\Model支持许多实用的特性:

Model 类也是更多高级模型如Active Record 活动记录的基类,不得不说,对于数据库操做不熟练的朋友能够尝试一下Active Record,Yii的这个特性让你操做数据库像操做对象同样简单。

定义属性,说白了也就是定义model里面的变量。

namespace app\models;

use yii\base\Model;

class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
}

以前作过java,eclipse自动生成get,set方法,感受好方便,这边,通常状况下不须要生成get,set方法,除非你的数据库操做是经过Active Record进行的,你才须要去覆盖魔术方法如__get()__set()使属性像普通对象属性被访问。

对于model里面属性的访问,这要感谢yii\base\Model支持 ArrayAccess 数组访问 和 ArrayIterator 数组迭代器。可使用以下两种方式对属性进行访问,是public属性哟。

$model = new \app\models\ContactForm;

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

// 像访问数组单元项同样访问属性
$model['name'] = 'example';
echo $model['name'];

// 迭代器遍历模型
foreach ($model as $name => $value) {
    echo "$name: $value\n";
}

属性标签,通常应用在输入错误的提示处,属性标签是视图一部分,可是在模型中申明标签一般很是方便,并可行程很是简洁可重用代码。咱们这边不作深究,通常在后台应用中不多涉及。感兴趣的朋友能够去官方文当详查。

属性的规范很重要,常规的操做是不容许两个model之间互相调用各自的属性的,如非必要,通常model中比较常规的属性放到全局变量中去控制,后台应用中,可供给model操做的数据均是来源于用户的,咱们须要作的更多的是对用户输入的控制。你可能在平常的工做中对这些用户输入的控制直接就使用了,枫爷在这边给你们一个小小的警醒,千万不能这么干,有几步仍是须要作的,为了安全性。其一,xss攻击的防范;其二,用户输入的验证。

xss的攻击,Yii提供了相应的方式应对,若是这个参数不对数据库进行操做,通常状况下无需验证,若是须要对数据库进行验证,须要进行xss验证,具体的实例代码百度一下,一大堆,这里就不作讲解啦。

对于用户输入的验证,最好的作法是在Controller里面对用户的行为进行控制,也就是重写behaviors这个方法,而后对每一个action进行验证。

'validation' => [
                'class' => 'ext\controller\behavior\Validation',
                'verifyPath' => 'app\modules\api\modules\v1\models\validation\\',
                'indexName' => 'body',
                'enabled' => true
],

其实,是否须要登陆验证,是否须要缓存均可以在behaviors里面进行配置。

这样区分开来不但可以避免在model里面写过多校验代码,也可使你的model层更加清晰简洁。

其实这个应该是在Controller那边说的,既然这边提到数据校验,就一笔带过啦~具体的如何操做,咱们会在下面一节【过滤器】中详解。

对于数据的获取,数据库仍是缓存,咱们在接下来的章节中继续讲解,这边就不细究了,进入下一个环节,View。

对于Model,也就这么多,其余的和写通常的function同样,写业务逻辑就能够啦,须要注意的都说了哈~之后有补充在评论栏。

视图

后端接口的应用中,这个模块不多被使用到,若是你们对这个模块感兴趣,能够去官网详查,我这边的方法很简单暴力,直接return给用户数据便可。

不要忘了,前端须要什么样的数据格式,咱们这边须要在最后return的时候encode一下哈。最经常使用的莫过于json格式啦。

public function formatJson($data = [])
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
        return $data;
    }

模块

你可能没有在Yii2.0的源代码中找到关于模块的代码,那是由于Yii最大的优点是开发后台系统,并无想过须要把业务逻辑切割的那么细,咱们在平常开发后台接口的时候,会有不少独立的系统,比方说用户模块,订单模块,支付模块等等等等。那模块的优点就显示出来了,每一个系统一个模块,清清楚楚。

如何建立模块呢?下面来一步步的搞清楚。

模块是独立的软件单元,由模型, 视图, 控制器和其余支持组件组成,是否是感受就是个小型的MVC模型,没错,每一个模块都是一个独立的单元。

使用模块必须在应用主体中配置它,终端用户能够访问在应用主体中已安装的模块的控制器。

以下例子显示一个模型的目录结构:

app/
    Module.php                   模块类文件
    controllers/                 包含控制器类文件
        DefaultController.php    default 控制器类文件
    models/                      包含模型类文件
    views/                       包含控制器视图文件和布局文件

Module.php是基础模块类,继承yii\base\Module的模块类,该类文件直接放在模块的yii\base\Module::basePath【模块对应的主目录】下。必须能被自动加载。

在应用主体中配置模块,以下示例

$config = [
    'id' => 'app.name',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'language' => 'zh-CN',
    'components' => [
       ...
    ],
    'params' => $params,
    'modules' => [
        'user' => [
            'class' => 'app\modules\user\User',
        ],
        'goods' => [
            'class' => 'app\modules\goods\Goods',
        ],
        'store' => [
            'class' => 'app\modules\store\Store',
        ],
        'supply' => [
            'class' => 'app\modules\supply\Supply',
        ],
        'order' => [
            'class' => 'app\modules\order\Order',
        ],
        'stock' => [
            'class' => 'app\modules\stock\Stock',
        ],
        'pay' => [
            'class' => 'app\modules\pay\Pay',
        ],
        'message' => [
            'class' => 'app\modules\message\Message',
        ],
    ],
    'aliases' => $aliases,
    'defaultRoute' => 'admin'
];

主体应用中配置的模块就指向刚刚创建的模块基础类。

每一个模块能够有本身独立的配置文件,将模块中须要用到的共同的参数放到这个配置文件中,并在模块基础类中添加进去。

下面就是个人用户模块的目录和代码

<?php
namespace app\modules\goods;

use Yii;

class Goods extends \yii\base\Module
{
    public $controllerNamespace = 'app\modules\goods\controllers';

    public function init()
    {
        parent::init();

        Yii::configure($this, require(__DIR__ . '/config.php'));
    }
}

访问路由

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

通常访问模块的方式以下:

// 获取ID为 "forum" 的模块
$module = \Yii::$app->getModule('forum');

// 获取处理当前请求控制器所属的模块
$module = \Yii::$app->controller->module;

模块在大型项目中常备使用,这些项目的特性可分组,每一个组包含一些强相关的特性, 每一个特性组能够作成一个模块由特定的开发人员和开发组来开发和维护。

在特性组上,使用模块也是重用代码的好方式,一些经常使用特性,如用户管理,评论管理,能够开发成模块, 这样在相关项目中很是容易被重用。无耻的抄袭了官网文档的最佳实践,其实我的感受官网文档的最佳时间是最给力的内容,其余内容都太晦涩了^_^

过滤器

在模块那一节,咱们提到一个校验的神器,behavior,过滤器是Controller动做执行以前或以后执行的对象,因此我说应该放在Controller一节去讲,可是在model层数据处理中提到了,就在那边一笔代了下,如今来详细的说说这个吧。

过滤器可包含 预过滤(过滤逻辑在动做以前) 或 后过滤(过滤逻辑在动做以后),也可同时包含二者。

咱们上面仅是说起了过滤器的一个属性,validation,这也是我自定义的一个属性,用于校验用户输入的正确性。

完整的官网对behaviors的定义以下:

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\HttpCache',
            'only' => ['index', 'view'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('user')->max('updated_at');
            },
        ],
    ];
}

控制器类的过滤器默认应用到该类的全部action,你能够在only参数中指定应用到哪几个action。也能够配置yii\base\ActionFilter::except属性使一些动做不执行过滤器。

通常状况下,咱们使用预过滤,不多会被用到后过滤,他们有啥区别呢。

预过滤

  • 按顺序执行应用主体中behaviors()列出的过滤器。
  • 按顺序执行模块中behaviors()列出的过滤器。
  • 按顺序执行控制器中behaviors()列出的过滤器。
  • 若是任意过滤器终止动做执行,后面的过滤器(包括预过滤和后过滤)再也不执行。
  • 成功经过预过滤后执行动做。

后过滤

  • 倒序执行控制器中behaviors()列出的过滤器。
  • 倒序执行模块中behaviors()列出的过滤器。
  • 倒序执行应用主体中behaviors()列出的过滤器。

建立过滤器,继承 yii\base\ActionFilter 类并覆盖 yii\base\ActionFilter::beforeAction() 和/或 yii\base\ActionFilter::afterAction() 方法来建立动做的过滤器,前者在动做执行以前执行,后者在动做执行以后执行。

下面是我在项目中使用的公共验证器。

<?php
namespace ext\controller\behavior;

use Yii;
use yii\base\ActionFilter;
use yii\base\Exception;

class Validation extends ActionFilter
{
    public $enabled = true; //默认打开验证

    public $verifyPath = null;//验证目录,必填项.命名空间格式填写

    public $whiteParams = null; //过滤以后的参数,供控制器使用

    public $indexName = '';

    public function beforeAction($action)
    {
        if (!$this->enabled) {
            return true;
        }

        return $this->_check($action);
    }

    protected function _check($action)
    {
        if (empty($this->verifyPath)) {
            throw new Exception('验证目录不存在!');
        }

        //目前只支持两级
        $groups = explode('/', $this->owner->id);
        if (is_array($groups) && count($groups) >= 2) {
            $fileName = ucfirst($groups[0]).ucfirst($groups[1]);
        } else {
            $fileName = ucfirst($this->owner->id);
        }
        unset($groups);
        $className = $this->verifyPath . $fileName;

        if (!class_exists($className)) {
            return true;
        }

        $actionId = $action->id;
        
        $v = new $className($this->owner->getParam($this->indexName) ?: []);

        if (!method_exists($v, $actionId)) {
            return true;
        }

        $v->{$actionId}();
        if (!$v->validate(null, false)) {
            $errorList = [];
            $errors = $v->getErrors();

            foreach ($errors as $field => $error) {
                $errorList[] = implode(' ', $error);
            }

            Yii::$app->getResponse()->data = $this->owner->getBehavior('format')->afterAction($action, $this->owner->sendError(implode(' & ', $errorList), 400));
            return false;
        }
        $this->whiteParams = $v->getAttributes();
        return true;
    }
}

在Controller中的behaviors中添加一条validation便可对某些action进行验证啦。

'validation' => [
                'class' => 'ext\controller\behavior\Validation',
                'verifyPath' => 'app\modules\goods\controller\validation\\',
                'indexName' => 'body',
                'enabled' => true
            ],

verifyPath便是被验证的规则路径。若是是验证参数,就对Controller下的全体action进行验证咯。

再来看下是如何编写这些个规则的。建立对应modules的validation。

<?php
namespace app\modules\goods\controllers\validation;

use app\modules\api\models\base\BaseValidation;

class SupplyGoods extends BaseValidation
{

    public function detail()
    {
        $this->defineAttributes('goodsId');
        $this->addRule('goodsId', 'number')->addRule('goodsId', 'required');
    }

    public function add()
    {
        $this->defineAttributes('goodsTitle,goodsPrice,minNum,catId,childCatId,showImages,detailImages,goodsAttr');
        $this->addRule('goodsTitle', 'trim')->addRule('goodsTitle', 'required');

        $this->addRule('goodsPrice', 'required');

        $this->addRule('catId', 'number')->addRule('catId', 'required')
            ->addRule('childCatId', 'number')->addRule('childCatId', 'required');
        

        if (!is_array($this->showImages) || count($this->showImages) == 0) {
            $this->addError('showImages', '商品展现图片必传!');
        }

        if (!is_array($this->detailImages) || count($this->detailImages) == 0) {
            $this->addError('detailImages', '图文详情必传!');
        }

        if (empty($this->goodsAttr['color'])
            || empty($this->goodsAttr['style_id'])
            || empty($this->goodsAttr['size_ids'])
        ) {
            $this->addError('goodsAttr', '请设置库存');
        }
    }

    public function update()
    {
        $this->defineAttributes('goodsId,goodsTitle,goodsPrice,minNum,catId,childCatId,showImages,detailImages,goodsAttr');
        $this->addRule('goodsTitle', 'trim')->addRule('goodsTitle', 'required');

        $this->addRule('goodsPrice', 'required');

        $this->addRule('catId', 'number')->addRule('catId', 'required')
            ->addRule('childCatId', 'number')->addRule('childCatId', 'required')
            ->addRule('goodsId','number')->addRule('goodsId','required');


        if (!is_array($this->showImages) || count($this->showImages) == 0) {
            $this->addError('showImages', '商品展现图片必传!');
        }

        if (!is_array($this->detailImages) || count($this->detailImages) == 0) {
            $this->addError('detailImages', '图文详情必传!');
        }

        if (empty($this->goodsAttr['color'])
            || empty($this->goodsAttr['style_id'])
            || empty($this->goodsAttr['size_ids'])
        ) {
            $this->addError('goodsAttr', '图片属性必传!');
        }
    }

    public function edit()
    {
        $this->defineAttributes('goodsId');
        $this->addRule('goodsId', 'number')->addRule('goodsId', 'required');
    }

    public function lists()
    {

    }

    public function delete()
    {
        $this->defineAttributes('goodsId');
        $this->addRule('goodsId', 'number')->addRule('goodsId', 'required');
    }

    public function category()
    {
        $this->defineAttributes('childCatId');
        $this->addRule('childCatId', 'number')->addRule('childCatId', 'required');
    }

}

BaseValidation是我写的一个基类,对某些全局须要验证的属性进行验证,继承DynamicModel。

验证类的名称SupplyGoods是根据路径来的,个人GoodsController在controllers里面的supply目录下,因此命名成SupplyGoods。

addRule以前必需要定义attributes

$this->defineAttributes('childCatId');
$this->addRule('childCatId', 'number')->addRule('childCatId', 'required');

每一个function的名称对应action后面的操做ID。

这是我本身定义的一个过滤器,其实,Yii提供了一组经常使用过滤器,在yii\filters命名空间下,有些仍是很不错的,你们感兴趣的话能够去官网上瞧瞧。

至此,这一章基础框架部分就所有给大伙说完了,累死我了,撸博客不易啊,且行且珍惜,不过个人Yii系列除了这一章之外,还将会给你们带来另外的几章,你们持续关注,有啥问题在评论区提出来,我会在第一时间给大伙解答哈O(∩_∩)O

相关文章
相关标签/搜索