前言:最近在用php寫一個項目的接口,所以需要學習一下Yii的框架,也在這里記錄一下。
整體結構
ssets文件夾:assets的作用是方便模塊化,插件化的,一般來說出於安全原因不允許通過url訪問protected下面的文件 ,但是我們又希望將module單獨出來,所以需要使用發布,即將一個目錄下的文件復制一份到assets下面方便通過url訪問。
commands文件夾:控制台腳本存放的地方,自動運行腳本
config文件夾:配置文件存放的文件夾
controller文件夾:MVC中C文件存放的文件夾
mail文件夾:郵件發送目錄,具體干啥的我還在摸索中哈~
models文件夾:MVC中M文件存放的文件夾
runtime:日志文件
tests:測試腳本文件夾
vendor:第三方組件存放,composer下載的組件存放的文件夾,自動幫你autoload
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。
例如
help
, post/create
, admin/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, ]); } } }
用戶訪問的時候就是直接被指向到某個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