web-dev-qa-db-ja.com

PHP:同じデータベース接続を複数のオブジェクトに注入する

非常に異なる機能のオブジェクトを定義する2つのクラスがあり、データストアでそれらが必要とする情報が2つの別個のデータベースに分割されると仮定します。

たとえば、クラスEmployeeSteelExtruderは、それぞれデータベース`human_resources``capital`に配置できます。それぞれのデータベースへの接続をハードコードするのではなく、

class Employee {
    protected $dbc;
    public function __construct() {
        $this->dbc = new mysqli('localhost', 'user', 'pass', 'human_resources');
    }
}

データベース接続をコンストラクターに注入することによる柔軟性の利点を確認できます。

class Employee {
    protected $dbc;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
}

しかし、それを適切に実装する方法がわかりません。シングルトンとデータベースクラスライブラリは、実際には単なるグローバル変数であるという事実を覆い隠すだけなので、シングルトンとデータベースクラスライブラリは不適切です。一方、代替案も素晴らしいとは思われません。

A)必要な接続をローダーファイルでグローバルに宣言します。機能的にはデータベースライブラリクラスと同じですが、においが悪くなります。

loader.php:
$dbc_human_resources = new mysqli('localhost', 'user', 'pass', 'human_resources');
$dbc_capital = new mysqli('localhost', 'user', 'pass', 'capital');

index.php:
$employees = array(
    new Employee($dbc_human_resources),
    new Employee($dbc_human_resources),
    ...
    );

B)新しいオブジェクトが作成されるたびに新しい接続を作成します。私が認識していない何らかの最適化がない限り、これは不注意に非効率的に見えます。

$employees = array(
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    new Employee(new mysqli('localhost', 'user', 'pass', 'human_resources')),
    ...
);

C)すべてのオブジェクトを、単一の接続をとるライブラリオブジェクトに格納します。

Employee.php:
class Employee {
    //non-database properties
    public function __construct($args = array()) {
        //non-database initialization
    }
}

EmployeeLibrary.php:
class EmployeeLibrary {
    protected $dbc, $employees;
    public function __construct(mysqli $dbc) {
        $this->dbc = $dbc;
    }
    public function add_new_employee($args) {
        array_Push($this->employees, new Employee($args));
    }
    //do things with database connection
}

loader.php:
$employee_library = new EmployeeLibrary(new mysqli('localhost', 'user', 'pass', 'human_resources');

index.php:
$employee_library->add_new_employee(...);
$employee_library->add_new_employee(...);

これはまれな問題ではないと確信しています。これを行うための認められた方法はありますか?

6
concat

ソースコードに接続文字列を格納しているため、3つすべてが間違っています。 ソースコードは構成に適した場所ではありません。データベースが移動するたびにコードを変更する必要がないため(つまり、すべての回帰テストを行う必要がないため)、または毎回)開発データベースからステージング、そして本番データベースに移動します

代わりに:

  • 接続文字列を構成ファイルに保存します。

  • 構成ファイルのオプションにアクセスするクラスを作成します。

    クラスは、呼び出し元にこれらのオプションへのシンプルなインターフェースを提供する必要があります。呼び出し元は、構成ファイルがどこにあるか、またはオプションが内部にどのように格納されるかを気にする必要はありません。

    この抽象化により、たとえば、JSONファイルに格納されている構成からXMLファイルに格納されている構成に切り替えたり、構成をRedisなどのデータベースに移動したりすることができます。

  • 構成オプションにアクセスする必要があるたびに、このクラスのインスタンスを作成します。

    ここではシングルトンは必要ありません。ある日、パフォーマンスが問題になる(つまり、構成ファイルを開いて解析するのに時間がかかる)場合は、キャッシュを使用します。

    すぐに(たとえば、JSONファイルからRedisに)別のストレージメカニズムに移動する必要があると確信している場合(YAGNIに注意)、スタックの一番上に構成オブジェクトを作成することにより、依存性注入を使用できます。そしてそれを発信者に渡します。これにより、別のメカニズムに移行するときに、コードベースの1行のみを変更し、次のものを置き換えます。

    $configuration = new JsonFileAppConfiguration();
    

    沿って:

    $configuration = new RedisAppConfiguration();
    

    しかし、他のすべては、両方のクラスが実装するインターフェイスIAppConfigurationのみに依存します。

5