Yii2.0基礎框架

前言:最近在用php寫一個項目的接口,因此須要學習一下Yii的框架,也在這裏記錄一下。php

總體結構

ssets文件夾:assets的做用是方便模塊化,插件化的,通常來講出於安全緣由不容許經過url訪問protected下面的文件 ,可是咱們又但願將module單獨出來,因此須要使用發佈,即將一個目錄下的文件複製一份到assets下面方便經過url訪問。css

commands文件夾:控制檯腳本存放的地方,自動運行腳本html

config文件夾:配置文件存放的文件夾前端

controller文件夾:MVC中C文件存放的文件夾java

mail文件夾:郵件發送目錄,具體幹啥的我還在摸索中哈~mysql

models文件夾:MVC中M文件存放的文件夾web

runtime:日誌文件sql

tests:測試腳本文件夾數據庫

vendor:第三方組件存放,composer下載的組件存放的文件夾,自動幫你autoloadbootstrap

views:MVC中V存放的文件夾

web:web主應用入口腳本存放的位置

以上是整個文件夾的佈局,能夠根據本身的項目靈活變化,咱們公司的項目中就弱化了MVC裏面的V,把V放在了前端。

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

Yii 應用參照模型-視圖-控制器 (MVC)設計模式來組織。當時聽到MVC也是一臉懵逼,組長給我幾個網址就讓我本身去了解了。

M模型表明數據、業務邏輯和規則;V視圖展現模型的輸出;C控制器接受出入並將其轉換爲模型和視圖命令。

 

這是官網上面的框架結構設計,MVC就是其中的控制器,視圖和模型,他們的各自做用上面也講了下,通常的後端應用,M表示從數據庫、第三方連接、本地文件中獲取的數據進行處理,整理,在交給到V端,V端的做用通常是在頁面中反饋給用戶的頁面,若是是以數據的形式返回給用戶,那這個V層就不用作過多的渲染。C層的話主要是鏈接二者的做用,C層獲取到用戶的請求,傳給M層,M層處理好數據,反饋給C層,C層再將數據給到V層,V層展現給用戶。MVC模型的便捷之處就是邏輯清晰,每一個模塊負責本身的事,有條有理,很是便於初學者理解,是一個入門的模型。

除此以外,Yii還包含其餘邏輯處理塊,比方說上面圖中的入口腳本【調用應用一開始必被調用的腳本文件】,應用主體【Yii::$app全局可訪問對象】,應用組件【全局通用的一些工具集】,模塊【業務邏輯單元,每一個業務邏輯一個模塊,會讓代碼很清晰】,過濾器【規範行爲的對象,在控制器執行以前或以後調用,定義一類特殊的行爲】,前端資源和小部件咱們先不講,由於是涉及到前端的一些組件內容,後面我會單獨開闢一個系列來說前端知識,我出這一系列的目的主要是針對後臺應用~

入口腳本

web文件夾下面的index.php就是入口腳本,每次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:自動加載器,這個是註冊composer自動加載器的。

Yii.php,包含Yii類的文件路徑。

應用主體配置

應用主體在入口腳本中建立並能經過表達式 \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、moudules

id:

yii\base\Application::id 屬性用來區分其餘應用的惟一標識ID。通常配置爲程序名稱。必要屬性之一

basePath:

yii\base\Application::basePath 指定該應用的根目錄,系統預約義@app表明這個路徑。若是須要require目錄被的文件,可使用這個方式找到對應文件。另一個必要屬性,這個是必須得配置的。

bootstrap:

這個屬性很實用,他容許你用數組啓動階段yii\base\Application::bootstrap()須要運行的組件,通常後端應用中配置這個'bootstrap' => ['log']便可

component:

這是最重要的屬性,他容許你註冊多個在其餘地方使用的應用組件,比方說session、log、db和cache,都在這裏配置的

aliases

該屬性容許你用一個數組定義多個別名。數組的key爲別名名稱,值爲對應的路徑。

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

language

該屬性指定應用展現給終端用戶的語言,默認爲 en 標識英文。若是須要以前其餘語言能夠配置該屬性

  'language' => 'zh-CN',

modules

該屬性指定應用所包含的模塊。還記得上面說的業務的分割嗎,是的,就是在modules裏面進行劃分的,對應的目錄就是根目錄下的modules目錄,也須要配置

[
    'modules' => [
        // "booking" 模塊以及對應的類
        'booking' => 'app\modules\booking\BookingModule',

        // "comment" 模塊以及對應的配置數組
        'comment' => [
            'class' => 'app\modules\comment\CommentModule',
            'db' => 'db',
        ],
    ],
]

這個配置對你代碼的結構清晰性有很大的提高,後端應用必配屬性。

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()。

應用組件

 在同一個應用中,每一個應用組件都有一個獨一無二的 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

表明表明各類緩存存儲器,例如內存,文件,數據庫。

db

表明一個能夠執行數據庫操做的數據庫鏈接。

search

搜索組件入口配置,之後有機會和你們嘮嘮solr相關,切割關鍵字,切割,切割~

urlManager

支持URL地址解析和建立。

user

表明認證登陸用戶信息,僅在yii\web\Application 網頁應用中可用。若是有後臺用戶權限認證管理,可去官網瞭解明細。

session

表明會話信息,僅在yii\web\Application 網頁應用中可用,咱們通常都是經過這個來控制用戶屬性的,

errorHandler

處理 PHP 錯誤和異常

log

全局日誌配置入口

請謹慎註冊太多應用組件,應用組件就像全局變量,使用太多可能加大測試和維護的難度。 通常狀況下能夠在須要時再建立本地組件。

你們能夠着重關注幾個配置:db、urlManager、log和cache,這些都是在平常工做中用的比較多的,必配的配置。

控制器

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,
            ]);
        }
    }
}
View Code

用戶訪問的時候就是直接被指向到某個action裏面的

有這個action方法了,那咱們如何訪問它呢?這邊就涉及到一個叫路由的東西

終端用戶經過所謂的【路由】尋找到操做

路由使用以下格式

ControllerID/ActionID

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

ModuleID/ControllerID/ActionID

若是用戶的請求地址爲 http://hostname/index.php?r=site/index, 會執行site控制器的index操做。?後面的是參數,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裏面,控制器類必須能被自動加載

下面爲一些實例,假設yii\base\Application::controllerNamespace控制器命名空間爲app\controller:

 

  • 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';
    }
}

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']而不是字符串

 

模型

學習完了MVC的C,如今來學習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操做的數據均是來源於用戶的,咱們須要作的更多的是對用戶輸入的控制。你可能在生活中對這些用戶輸入的控制直接就使用了,千萬不能這麼幹,有幾部仍是須要作的,爲了安全性。其一css攻擊的防範;其二用戶輸入的驗證。

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一下哈。最經常使用的就是josn格式啦。

 

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命名空間下,有些仍是很不錯的,你們感興趣的話能夠去官網上瞧瞧。

原文連接:https://www.cnblogs.com/riverdubu/p/6607373.html

相關文章
相關標籤/搜索