CodeIgniter4 服務(wù)

2020-08-13 11:37 更新

引言

在CodeIgniter內(nèi)部的所有類實(shí)際上都是以”服務(wù)”的形式呈現(xiàn)的。這意味著,所有的類都是以定義在一個(gè)簡(jiǎn)單的配置文件里,而非硬編碼所需要加載的類名,來進(jìn)行加載的。 該配置文件實(shí)際上扮演了一種為所需類創(chuàng)建新的實(shí)例的工廠的角色。

一個(gè)簡(jiǎn)單的例子可能會(huì)講得更清楚,比如請(qǐng)?jiān)O(shè)想你需要獲得一個(gè) Timer (計(jì)時(shí)器)類的實(shí)例,最簡(jiǎn)單的方法就是為該類創(chuàng)建一個(gè)新的實(shí)例:

$timer = new \CodeIgniter\Debug\Timer();

這種方式運(yùn)行得相當(dāng)不錯(cuò),直到你決定需要在該位置上使用另一個(gè)計(jì)時(shí)器類時(shí)??赡苓@個(gè)類比起默認(rèn)的計(jì)時(shí)器類提供了更高級(jí)的報(bào)告方法。 為了實(shí)現(xiàn)這一目標(biāo),你可能會(huì)查找應(yīng)用中的所有位置來定位哪些地方使用了定時(shí)器類。由于你可能在很多地方都設(shè)置了該類的實(shí)例,以獲取應(yīng)用日常運(yùn)行的性能日志, 這種查找-替換的工作可能會(huì)變得相當(dāng)?shù)暮臅r(shí)并且錯(cuò)誤頻發(fā)。這就是服務(wù)的用武之地。

取代了手動(dòng)創(chuàng)建實(shí)例的操作,我們保留了一個(gè)中央控制類來為我們新建實(shí)例。該類的結(jié)構(gòu)相當(dāng)簡(jiǎn)單,僅僅包含了一個(gè)方法用于調(diào)度我們需要用作服務(wù)的每個(gè)類。 該方法只是返回了指定類的一個(gè)共享實(shí)例,用于所有依賴該類的地方以服務(wù)的形式來調(diào)用。從而我們可以用以下代碼來取代每次都新建一個(gè)實(shí)例的方式:

$timer = \Config\Services::timer();

當(dāng)你想要更改這一實(shí)現(xiàn)時(shí),只需要更改服務(wù)的配置文件,從而在應(yīng)用中就可以自動(dòng)地進(jìn)行變更替換,不需要任何其他操作。 現(xiàn)在你所需要的只是使用新的替換上來的類的特性,非常地簡(jiǎn)單且不易出錯(cuò)。

注解

我們推薦只在控制器里創(chuàng)建服務(wù)。在其他文件例如模型和庫,應(yīng)當(dāng)依賴于構(gòu)造函數(shù)或者 setter 方法的傳參來實(shí)例化。

便利的方法

有兩個(gè)方法被用于獲取一個(gè)服務(wù),這些方法都非常的方便。

第一個(gè)就是 service() 方法,該方法返回了指定服務(wù)的新的實(shí)例。唯一需要的參數(shù)就是服務(wù)名。 該方法與服務(wù)文件內(nèi)部返回共享實(shí)例的方式是一樣的,因此對(duì)該方法的多次調(diào)用總是會(huì)返回一個(gè)相同的實(shí)例:

$logger = service('logger');

如果創(chuàng)建的方法需要額外的參數(shù),那么這些參數(shù)就應(yīng)該在服務(wù)名后傳遞:

$renderer = service('renderer', APPPATH.'views/');

第二個(gè)方法 single_service() ,和 service() 一樣調(diào)用,不過每次都會(huì)返回一個(gè)指定類的新的實(shí)例:

$logger = single_service('logger');

定義服務(wù)

為了保證服務(wù)的正常運(yùn)行,你需要能夠?qū)γ總€(gè)擁有常量API,或者實(shí)現(xiàn)了 接口 的類建立依賴。 CodeIgniter 的大部分類都提供了一個(gè)它們所應(yīng)當(dāng)提供的接口。當(dāng)你需要擴(kuò)展或者替代核心類時(shí),你只需要確保自己符合這些接口的要求并且確定這些類的功能是完善的。

舉例來說, RouterCollection 類實(shí)現(xiàn)了 RouterCollectionInterface 接口。當(dāng)你想要替代該類,并實(shí)現(xiàn)不同的路由管理方法時(shí),只需要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)了 RouterCollectionInterface 接口的類即可:

class MyRouter implements \CodeIgniter\Router\RouteCollectionInterface
{
        // Implement required methods here.
}

最后,修改 /app/Config/Services.php 以創(chuàng)建 MyRouter 類的實(shí)例,來替代 CodeIgniter\Router\RouterCollection

public static function routes()
{
        return new \App\Router\MyRouter();
}

允許使用參數(shù)

在某些情況下,你可能想要使用某個(gè)選項(xiàng)來為一個(gè)類在實(shí)例化的時(shí)候傳遞配置信息。 由于服務(wù)文件只是簡(jiǎn)單的類文件,如上操作非常方便。

renderer 服務(wù)就是一個(gè)不錯(cuò)的例子。默認(rèn)情況下,我們需要該類能夠找到 APPPATH.views/ 目錄下的視圖文件。我們同時(shí)也想為開發(fā)者提供改變路徑的選項(xiàng)(如果他們需要的話)。 因此該類接受 $viewPath 變量作為構(gòu)造函數(shù)的參數(shù)。該服務(wù)的調(diào)用方法可能如下所示:

public static function renderer($viewPath=APPPATH.'views/')
{
        return new \CodeIgniter\View\View($viewPath);
}

這一過程在構(gòu)造函數(shù)方法里設(shè)置了默認(rèn)路徑,同時(shí)也可以輕松地改變其所使用的路徑:

$renderer = \Config\Services::renderer('/shared/views');

共享類

某些情況下你可能只需要?jiǎng)?chuàng)建一個(gè)類的單實(shí)例。該操作可以通過工廠方法內(nèi)部調(diào)用的 getSharedInstance() 方法來輕松地處理。 該方法檢查了該類是否已創(chuàng)建了存儲(chǔ)于內(nèi)部的單個(gè)實(shí)例,如果沒有的話,就會(huì)創(chuàng)建一個(gè)新的。所有的工廠方法都會(huì)提供一個(gè) $getShared = true 的值作為最后的參數(shù)。你可以像這樣操作該方法:

class Services
{
    public static function routes($getShared = false)
    {
        if (! $getShared)
        {
            return new \CodeIgniter\Router\RouteCollection();
        }


        return static::getSharedInstance('routes');
    }
}

服務(wù)發(fā)現(xiàn)

CodeIgniter可以自動(dòng)發(fā)現(xiàn)所有你在其他命名空間里可能定義的 Config\Services.php 文件。這一功能允許了任何模塊服務(wù)化文件的簡(jiǎn)單使用。 為了這些定制化的服務(wù)文件可以被自動(dòng)發(fā)現(xiàn),他們需要滿足這些要求

  • 它們的命名空間必須在 Config\Autoload.php 中已定義
  • 在命名空間內(nèi)部,該文件必須可以在 Config\Services.php 里被找到
  • 它們必須繼承 CodeIgniter\Config\BaseService

一個(gè)小例子可以幫助我們更好地理解。

假設(shè)你創(chuàng)建了一個(gè)新的目錄,比如在根目錄下的一個(gè)叫做 Blog 的目錄。該目錄中里有一個(gè) 博客模塊 ,并含有控制器,模型等文件。 如果你愿意的話也可以將某些類作為服務(wù)而使用。第一步就是創(chuàng)建一個(gè)新的文件: Blog\Config\Services.php ,該文件結(jié)構(gòu)應(yīng)當(dāng)如下所示:

<?php namespace Blog\Config;


use CodeIgniter\Config\BaseService;


class Services extends BaseService
{
    public static function postManager()
    {
        ...
    }
}

現(xiàn)在你可以使用如上描述的文件。每當(dāng)你想要調(diào)用其他控制器的 posts 服務(wù)時(shí),就可以簡(jiǎn)單地使用該框架的 Config\Services 類來獲取你所需要的服務(wù):

$postManager = Config\Services::postManager();

注解

如果多個(gè)服務(wù)文件擁有相同的方法名,那么第一個(gè)被發(fā)現(xiàn)的服務(wù)實(shí)例就會(huì)作為返回值。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)