亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

directory search
閱讀前篇 簡介 Yii 是什么 從 Yii 1.1 升級 入門 安裝 Yii 運行應(yīng)用 第一次問候 使用Forms 數(shù)據(jù)庫應(yīng)用 使用 Gii 生成代碼 進階 應(yīng)用結(jié)構(gòu) 概述 入口腳本 應(yīng)用(Applications) 應(yīng)用組件(Application Components) 控制器(Controllers) 模型(Models) 視圖(views) 模塊(Modules) 過濾器(Filters) 小部件(Widgets) 前端資源(Assets) 擴展(Extensions) 請求處理 運行概述 啟動引導(dǎo)(Bootstrapping) 路由和創(chuàng)建URL 請求(Requests) 響應(yīng)(Responses) Sessions 和 Cookies 錯誤處理(Handling Errors) 日志(Logging) 關(guān)鍵概念 組件(Component) 屬性(Property) 事件(Events) 行為(Behaviors) 配置(Configurations) 別名(Aliases) 類自動加載(Autoloading) 服務(wù)定位器(Service Locator) 依賴注入容器(Dependency Injection Container) 配合數(shù)據(jù)庫工作 數(shù)據(jù)庫訪問 (Data Access Objects) 查詢生成器(Query Builder) 活動記錄(Active Record) 數(shù)據(jù)庫遷移(Migrations) Sphinx Redis MongoDB Elasticsearch 接收用戶數(shù)據(jù) 創(chuàng)建表單(Creating Forms) 輸入驗證(Validating Input) 文件上傳(Uploading Files) 收集列表輸入(Collecting Tabular Input) 多模型的復(fù)合表單(Getting Data for Multiple Models) 顯示數(shù)據(jù) 格式化輸出數(shù)據(jù)(Data Formatting) 分頁(Pagination) 排序(Sorting) 數(shù)據(jù)提供器(Data Providers) 數(shù)據(jù)小部件(Data Widgets) 客戶端腳本使用(Working with Client Scripts) 主題(Theming) 安全 認證(Authentication) 授權(quán)(Authorization) 處理密碼(Working with Passwords) 客戶端認證(Auth Clients) 最佳安全實踐(Best Practices) 緩存 概述 數(shù)據(jù)緩存 片段緩存 頁面緩存 HTTP 緩存 RESTfull Web服務(wù) 快速入門(Quick Start) 資源(Resources) 控制器(Controllers) 路由(Routing) 格式化響應(yīng)(Response Formatting) 授權(quán)認證(Authentication) 速率限制(Rate Limiting) 版本(Versioning) 錯誤處理(Error Handling) 開發(fā)工具 調(diào)試工具欄和調(diào)試器 使用Gii生成代碼 生成API文檔 測試 概述(Overview) 配置測試環(huán)境(Testing environment setup) 單元測試(Unit Tests) 功能測試(Function Tests) 驗收測試(Acceptance Tests) 測試夾具(Fixtures) 高級專題 高級應(yīng)用模板 創(chuàng)建自定義應(yīng)用程序結(jié)構(gòu) 控制臺命令 核心驗證器(Core Validators) 國際化 收發(fā)郵件 性能優(yōu)化 共享主機環(huán)境 模板引擎 集成第三方代碼 小部件 Bootstrap 小部件 Jquery UI 助手類 概述 Array 助手(ArrayHelper) Html 助手(Html) Url 助手(Url)
characters

依賴注入容器

依賴注入容器

依賴注入(Dependency Injection,DI)容器就是一個對象,它知道怎樣初始化并配置對象及其依賴的所有對象。Martin 的文章?已經(jīng)解釋了 DI 容器為什么很有用。這里我們主要講解 Yii 提供的 DI 容器的使用方法。

依賴注入

Yii 通過 yii\di\Container 類提供 DI 容器特性。它支持如下幾種類型的依賴注入:

  • 構(gòu)造方法注入;
  • Setter 和屬性注入;
  • PHP 回調(diào)注入.

構(gòu)造方法注入

在參數(shù)類型提示的幫助下,DI 容器實現(xiàn)了構(gòu)造方法注入。當容器被用于創(chuàng)建一個新對象時,類型提示會告訴它要依賴什么類或接口。容器會嘗試獲取它所依賴的類或接口的實例,然后通過構(gòu)造器將其注入新的對象。例如:

class Foo{
    public function __construct(Bar $bar)
    {
    }
}

$foo = $container->get('Foo');
// 上面的代碼等價于:$bar = new Bar;
$foo = new Foo($bar);

Setter 和屬性注入

Setter 和屬性注入是通過配置提供支持的。當注冊一個依賴或創(chuàng)建一個新對象時,你可以提供一個配置,該配置會提供給容器用于通過相應(yīng)的 Setter 或?qū)傩宰⑷胍蕾嚒@纾?/p>

use yii\base\Object;

class Foo extends Object{
    public $bar;

    private $_qux;

    public function getQux()
    {
        return $this->_qux;
    }

    public function setQux(Qux $qux)
    {
        $this->_qux = $qux;
    }
}

$container->get('Foo', [], [
    'bar' => $container->get('Bar'),
    'qux' => $container->get('Qux'),
]);

PHP 回調(diào)注入

這種情況下,容器將使用一個注冊過的 PHP 回調(diào)創(chuàng)建一個類的新實例?;卣{(diào)負責解決依賴并將其恰當?shù)刈⑷胄聞?chuàng)建的對象。例如:

$container->set('Foo', function () {
    return new Foo(new Bar);
});

$foo = $container->get('Foo');

注冊依賴關(guān)系

可以用 yii\di\Container::set() 注冊依賴關(guān)系。注冊會用到一個依賴關(guān)系名稱和一個依賴關(guān)系的定義。依賴關(guān)系名稱可以是一個類名,一個接口名或一個別名。依賴關(guān)系的定義可以是一個類名,一個配置數(shù)組,或者一個 PHP 回調(diào)。

$container = new \yii\di\Container;

// 注冊一個同類名一樣的依賴關(guān)系,這個可以省略。$container->set('yii\db\Connection');

// 注冊一個接口// 當一個類依賴這個接口時,相應(yīng)的類會被初始化作為依賴對象。$container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');

// 注冊一個別名。// 你可以使用 $container->get('foo') 創(chuàng)建一個 Connection 實例$container->set('foo', 'yii\db\Connection');

// 通過配置注冊一個類// 通過 get() 初始化時,配置將會被使用。$container->set('yii\db\Connection', [
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

// 通過類的配置注冊一個別名// 這種情況下,需要通過一個 “class” 元素指定這個類$container->set('db', [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

// 注冊一個 PHP 回調(diào)// 每次調(diào)用 $container->get('db') 時,回調(diào)函數(shù)都會被執(zhí)行。$container->set('db', function ($container, $params, $config) {
    return new \yii\db\Connection($config);
});

// 注冊一個組件實例// $container->get('pageCache') 每次被調(diào)用時都會返回同一個實例。$container->set('pageCache', new FileCache);

Tip: 如果依賴關(guān)系名稱和依賴關(guān)系的定義相同,則不需要通過 DI 容器注冊該依賴關(guān)系。

通過?set()?注冊的依賴關(guān)系,在每次使用時都會產(chǎn)生一個新實例??梢允褂?yii\di\Container::setSingleton() 注冊一個單例的依賴關(guān)系:

$container->setSingleton('yii\db\Connection', [
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

解決依賴關(guān)系

注冊依賴關(guān)系后,就可以使用 DI 容器創(chuàng)建新對象了。容器會自動解決依賴關(guān)系,將依賴實例化并注入新創(chuàng)建的對象。依賴關(guān)系的解決是遞歸的,如果一個依賴關(guān)系中還有其他依賴關(guān)系,則這些依賴關(guān)系都會被自動解決。

可以使用 yii\di\Container::get() 創(chuàng)建新的對象。該方法接收一個依賴關(guān)系名稱,它可以是一個類名,一個接口名或一個別名。依賴關(guān)系名或許是通過?set()?或?setSingleton()?注冊的。你可以隨意地提供一個類的構(gòu)造器參數(shù)列表和一個configuration?用于配置新創(chuàng)建的對象。例如:

// "db" 是前面定義過的一個別名$db = $container->get('db');

// 等價于: $engine = new \app\components\SearchEngine($apiKey, ['type' => 1]);$engine = $container->get('app\components\SearchEngine', [$apiKey], ['type' => 1]);

代碼背后,DI 容器做了比創(chuàng)建對象多的多的工作。容器首先將檢查類的構(gòu)造方法,找出依賴的類或接口名,然后自動遞歸解決這些依賴關(guān)系。

如下代碼展示了一個更復(fù)雜的示例。UserLister?類依賴一個實現(xiàn)了?UserFinderInterface?接口的對象;UserFinder?類實現(xiàn)了這個接口,并依賴于一個?Connection?對象。所有這些依賴關(guān)系都是通過類構(gòu)造器參數(shù)的類型提示定義的。通過屬性依賴關(guān)系的注冊,DI 容器可以自動解決這些依賴關(guān)系并能通過一個簡單的?get('userLister')?調(diào)用創(chuàng)建一個新的?UserLister?實例。

namespace app\models;

use yii\base\Object;
use yii\db\Connection;
use yii\di\Container;

interface UserFinderInterface{
    function findUser();
}

class UserFinder extends Object implements UserFinderInterface{
    public $db;

    public function __construct(Connection $db, $config = [])
    {
        $this->db = $db;
        parent::__construct($config);
    }

    public function findUser()
    {
    }
}

class UserLister extends Object{
    public $finder;

    public function __construct(UserFinderInterface $finder, $config = [])
    {
        $this->finder = $finder;
        parent::__construct($config);
    }
}

$container = new Container;
$container->set('yii\db\Connection', [
    'dsn' => '...',
]);
$container->set('app\models\UserFinderInterface', [
    'class' => 'app\models\UserFinder',
]);
$container->set('userLister', 'app\models\UserLister');

$lister = $container->get('userLister');

// 等價于:

$db = new \yii\db\Connection(['dsn' => '...']);
$finder = new UserFinder($db);
$lister = new UserLister($finder);

實踐中的運用

當在應(yīng)用程序的入口腳本中引入?Yii.php?文件時,Yii 就創(chuàng)建了一個 DI 容器。這個 DI 容器可以通過 Yii::$container 訪問。當調(diào)用 Yii::createObject() 時,此方法實際上會調(diào)用這個容器的 yii\di\Container::get() 方法創(chuàng)建新對象。如上所述,DI 容器會自動解決依賴關(guān)系(如果有)并將其注入新創(chuàng)建的對象中。因為 Yii 在其多數(shù)核心代碼中都使用了 Yii::createObject() 創(chuàng)建新對象,所以你可以通過 Yii::$container 全局性地自定義這些對象。

例如,你可以全局性自定義 yii\widgets\LinkPager 中分頁按鈕的默認數(shù)量:

\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);

這樣如果你通過如下代碼在一個視圖里使用這個掛件,它的?maxButtonCount?屬性就會被初始化為 5 而不是類中定義的默認值 10。

echo \yii\widgets\LinkPager::widget();

然而你依然可以覆蓋通過 DI 容器設(shè)置的值:

echo \yii\widgets\LinkPager::widget(['maxButtonCount' => 20]);

另一個例子是借用 DI 容器中自動構(gòu)造方法注入帶來的好處。假設(shè)你的控制器類依賴一些其他對象,例如一個旅館預(yù)訂服務(wù)。你可以通過一個構(gòu)造器參數(shù)聲明依賴關(guān)系,然后讓 DI 容器幫你自動解決這個依賴關(guān)系。

namespace app\controllers;

use yii\web\Controller;
use app\components\BookingInterface;

class HotelController extends Controller{
    protected $bookingService;

    public function __construct($id, $module, BookingInterface $bookingService, $config = [])
    {
        $this->bookingService = $bookingService;
        parent::__construct($id, $module, $config);
    }
}

如果你從瀏覽器中訪問這個控制器,你將看到一個報錯信息,提醒你?BookingInterface?無法被實例化。這是因為你需要告訴 DI 容器怎樣處理這個依賴關(guān)系。

\Yii::$container->set('app\components\BookingInterface', 'app\components\BookingService');

現(xiàn)在如果你再次訪問這個控制器,一個?app\components\BookingService?的實例就會被創(chuàng)建并被作為第三個參數(shù)注入到控制器的構(gòu)造器中。

什么時候注冊依賴關(guān)系

由于依賴關(guān)系在創(chuàng)建新對象時需要解決,因此它們的注冊應(yīng)該盡早完成。如下是推薦的實踐:

  • 如果你是一個應(yīng)用程序的開發(fā)者,你可以在應(yīng)用程序的入口腳本或者被入口腳本引入的腳本中注冊依賴關(guān)系。
  • 如果你是一個可再分發(fā)擴展的開發(fā)者,你可以將依賴關(guān)系注冊到擴展的引導(dǎo)類中。

總結(jié)

依賴注入和服務(wù)定位器都是流行的設(shè)計模式,它們使你可以用充分解耦且更利于測試的風格構(gòu)建軟件。強烈推薦你閱讀?Martin 的文章?,對依賴注入和服務(wù)定位器有個更深入的理解。

Yii 在依賴住入(DI)容器之上實現(xiàn)了它的服務(wù)定位器。當一個服務(wù)定位器嘗試創(chuàng)建一個新的對象實例時,它會把調(diào)用轉(zhuǎn)發(fā)到 DI 容器。后者將會像前文所述那樣自動解決依賴關(guān)系。
Previous article: Next article: