【Drupal8】別のデータベースに接続してノードを自サイトのDBに複製する

## したいこと
* Drupal8.3でWebアプリを作っている。  
* 複数のDrupalサイトでひとつのシステムを形成する。  
* ひとつひとつのサイトが個々にデータベースを持つ。  
* サイトには役割がある。  
  * 雛形としてのサイト
  * 実際に稼働するサイト
* 実際に稼働するサイトに、雛形サイトの記事（Node）をインポートする機能が欲しい。

## 必要な機能
* 別のデータベースに接続する。
* SQLクエリの実行だけでなく、きちんと「Drupal\node\Entity\Node」オブジェクトが欲しい。
* 別のデータベースから取得したNodeを複製し、自サイトのDBに保存する。

## 概要
* データベース接続設定の追加  
  `\Drupal\Core\Database\Database::addConnectionInfo($key, $target, array $info)`
* データベース接続設定を指定してのデータベースコネクションインスタンスの取得  
  `\Drupal\Core\Database\Database::getConnection($target,  $key)`
* ノード取得のための Drupal\node\NodeStorage をDB接続を指定して取得  

    ```
    $node_storage = new NodeStorage(
        \Drupal::entityManager()->getDefinition('node'),
        データベースコネクションインスタンス,
        \Drupal::entityManager(),
        \Drupal::cache(),
        \Drupal::languageManager()
    );
    $node_storage->resetCache(); //キャッシュが強く働いて接続先を変更しても最初の接続から変更が反映されないのでキャッシュリセットを呼ぶ
    ```
* Nodeの取得
  `$node = $node_storage->->load(ノードID)`
* Nodeの複製
  `$new_node = $node->createDuplicate();`
* Nodeの保存
  `$new_node->save()` だと、そのノードが取得されたのと同じDBに保存されてしまう。
  ノードインスタンスは内部にprotectedのノードストレージを持っていて、本来それを使って保存している。
  Drupal\node\NodeStorage::save(ノードインスタンス) を使うことで、接続先DBを指定して保存できる。
  DB接続先を変更して $another_node_storage を作るとして、
  `$another_node_storage->save($new_node)`

## 見本
PHP7.1 の環境で作成したもの。

```
<?php

namespace Drupal\{モジュール名}\Utility;

use Drupal;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\node\NodeStorage;

/**
 * 複数のデータベースにおけるノードストレージを利用するクラス
 */
class MultiDatabaseNodeStorageManager
{
    // DB接続設定 本来はconfigなんかを使うと良さそう
    const TEMPLATE_DATABASE_INFO_DATABASE = 'ここに接続先DBのデータベース名を代入';
    const TEMPLATE_DATABASE_INFO_USERNAME = 'ここに接続先DBのユーザー名を代入';
    const TEMPLATE_DATABASE_INFO_PASSWORD = 'ここに接続先DBのパスワードを代入';


    const DEFAULT_DATABASE_KEY     = 'default';
    const DEFAULT_DATABASE_TARGET  = 'default';
    const TEMPLATE_DATABASE_KEY    = 'template';
    const TEMPLATE_DATABASE_TARGET = 'default';

    /**
     * @var Drupal\node\NodeStorage
     *      当サイトのノードストレージ
     */
    private $default_node_storage;

    /**
     * @var Drupal\node\NodeStorage
     *      テンプレートサイトのノードストレージ
     */
    private $template_node_storage;

    /**
     * @var Drupal\Core\Database\Driver\mysql\Connection
     *      当サイトのDB接続
     */
    private $default_db_connection;

    /**
     * @var Drupal\Core\Database\Driver\mysql\Connection
     *      テンプレートDBへの接続
     */
    private $template_db_connection;


    public function __construct()
    {
        $default_database_info_array = Database::getConnectionInfo(self::DEFAULT_DATABASE_KEY);
        $template_database_info = $default_database_info_array[self::DEFAULT_DATABASE_TARGET];
        $template_database_info['database'] = self::TEMPLATE_DATABASE_INFO_DATABASE;
        $template_database_info['username'] = self::TEMPLATE_DATABASE_INFO_USERNAME;
        $template_database_info['password'] = self::TEMPLATE_DATABASE_INFO_PASSWORD;
        Database::addConnectionInfo(self::TEMPLATE_DATABASE_KEY, self::TEMPLATE_DATABASE_TARGET, $template_database_info);

        $this->default_db_connection  = Database::getConnection(self::DEFAULT_DATABASE_TARGET,  self::DEFAULT_DATABASE_KEY);
        $this->template_db_connection = Database::getConnection(self::TEMPLATE_DATABASE_TARGET, self::TEMPLATE_DATABASE_KEY);
    }


    /**
     * テンプレートサイトのDBから当サイトのDBへ、ノードをインポートする。
     * ノードは新規追加される。
     *
     * Drupal\node\Entity\Node::save() は Drupal\node\NodeStorage::save(Node) と同じ動作であることを利用している。
     *
     * @param int ノードのID インポート元サイトにおける元記事のID
     */
    public function importNodeFromTemplateDatabase(int $nid)
    {
        $node_base = $this->getTemplateNodeStorage()->load($nid);
        if (empty($node_base)) {
            return;
        }
        $new_node = $node_base->createDuplicate();
        $this->getDefaultNodeStorage()->save($new_node);
    }


    /**
     * 当サイトのノードストレージインスタンスを取得。
     * 一度DB接続が行われた後はその接続インスタンスのキャッシュが強く残ってしまいDBの変更がうまく反映されないため、
     * resetCache()を呼んでいる。
     */
    public function getDefaultNodeStorage(): NodeStorage
    {
        if (empty($this->default_node_storage)) {
            $this->default_node_storage = new NodeStorage(
                Drupal::entityManager()->getDefinition('node'),
                $this->default_db_connection,
                Drupal::entityManager(),
                Drupal::cache(),
                Drupal::languageManager()
            );
        }
        $this->default_node_storage->resetCache();
        return $this->default_node_storage;
    }


    /**
     * テンプレートサイトのノードストレージインスタンスを取得。
     * resetCache()を呼んでいる。
     */
    public function getTemplateNodeStorage(): NodeStorage
    {
        if (empty($this->template_node_storage)) {
            $this->template_node_storage = new NodeStorage(
                Drupal::entityManager()->getDefinition('node'),
                $this->template_db_connection,
                Drupal::entityManager(),
                Drupal::cache(),
                Drupal::languageManager()
            );
        }
        $this->template_node_storage->resetCache();
        return $this->template_node_storage;
    }
}

```