【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

相關文章
相關標籤/搜索