サービス

前書き

CodeIgniter のクラスはすべて "サービス" として提供されます。これの意味するところはシンプルです。ロードするのにクラス名をハードコーディングする代わりに、とても単純な設定ファイルに呼び出すクラスを定義します。このファイルは要求された新しいインスタンスを作るファクトリとして振る舞います。

簡単な例のほうがわかりやすいでしょう。イメージとして、Timer クラスのインスタンスを取得したいとします。一番シンプルな方法は、単にクラスのインスタンスを新しく作ることです:

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

そして、これは確かに動作します。異なるタイマークラスに取り替えようと思わない限りは。 デフォルトのタイマーが提供しない優れたレポート機能があるかもしれません。これを実施するため、すでにタイマークラスを使用した箇所を全部置き換える必要があります。 アプリケーションのパフォーマンスログを取得するため内で絶えず処理しているのところをそのまま残したなら、これは手間がかかる上にエラーを引き起こしやすい対応です。ここでサービスが役に立ちます。

自分でインスタンスを作成する代わりに、中央クラスにインスタンスを作成させます。このクラスはとてもシンプルであるよう維持されています。サービスとして使用したいクラスごとにメソッドがあるだけです。このメソッドはたいていの場合クラスの共有インスタンスを返すので、その中にいくらかの依存する情報を渡せます。さて、タイマー作成コードを新しいクラスを呼び出すコードに置き換えます:

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

実装を変える必要があるときには、サービス設定ファイルを編集すれば、自動的にアプリケーション全体で変更されます。他の作業はありません。さあ、新しい機能を利用するだけで準備完了です。とてもシンプルで、エラーに強いです。

注釈

コントローラ内で使用するサービスに対してのみおすすめです。Other files, like models and libraries should have the dependencies either passed into the constructor or through a setter method.

便利関数

サービスを取得するために2つの関数が用意されています。その関数は常に利用可能です。

まずは service()、リクエストしたサービスの新しいインスタンスを返します。必須の引数はサービス名だけです。This is the same as the method name within the Services file always returns a SHARED instance of the class, so calling the function multiple times should always return the same instance:

$logger = service('logger');

生成メソッドがさらなる引数を必要とするなら、サービス名のうしろに渡してください:

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

The second function, single_service() works just like service() but returns a new instance of the class:

$logger = single_service('logger');

サービスの定義

サービスをうまく動かすには、各クラス固有の API、つまり インターフェース に依存した使い方をできないといけません。ほとんどすべての CodeIgniter のクラスには、それらに付随するインターフェースがあります。コアクラスを拡張または置き換えたいときには、インターフェースの要求に合わせてクラスに互換性を持たせていることを確認するだけで良いです。

例えば、RouterCollection クラスは RouterCollectionInterface を実装しています。異なる方法でルートを作成するもので置き換えたい場合は、RouterCollectionInterface を実装した新しいクラスを作成するだけです:

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

Finally, modify /app/Config/Services.php to create a new instance of MyRouter instead of CodeIgniter\Router\RouterCollection:

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

引数の許可

いくらかの場合、クラスをインスタンス化する際に設定としてオプションを渡したいことがあるでしょう。 サービスファイルはとてもシンプルなクラスなので、簡単に実施できます。

renderer サービスが良い例です。ビューに APPPATH.views/ を探しに行かせることをデフォルトとしたいとします。しかし、必要なら、このパスを変更するオプションを持たせたいでしょう。そのため、$viewPath をコンストラクタ引数に渡します。サービスメソッドはこのようになります:

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

これはデフォルトパスをコンストラクタメソッドに渡しつつも、使用するパスを簡単に変更できます:

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

共有クラス

サービスを作成するのにインスタンスをただ1つにしたい場合があります。これはファクトリメソッド内で呼び出されている getSharedInstance() により簡単に実現しています。クラス内にすでに生成されたインスタンスがないかを確認し、なければ新しく生成する操作をします。すべてのファクトリメソッドでは $getShared = true を最後の引数として持っています。あなたのメソッドでもそれを守らなければなりません:

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

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

Service Discovery

CodeIgniter can automatically discover any Config\Services.php files you may have created within any defined namespaces. This allows simple use of any module Services files. In order for custom Services files to be discovered, they must meet these requirements:

  • Its namespace must be defined in Config\Autoload.php
  • Inside the namespace, the file must be found at Config\Services.php
  • It must extend CodeIgniter\Config\BaseService

A small example should clarify this.

Imagine that you've created a new directory, Blog in your root directory. This will hold a blog module with controllers, models, etc, and you'd like to make some of the classes available as a service. The first step is to create a new file: Blog\Config\Services.php. The skeleton of the file should be:

<?php namespace Blog\Config;

use CodeIgniter\Config\BaseService;

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

Now you can use this file as described above. When you want to grab the posts service from any controller, you would simply use the framework's Config\Services class to grab your service:

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

注釈

If multiple Services files have the same method name, the first one found will be the instance returned.