web-dev-qa-db-fra.com

Comment injecter la connexion à la base de données dans un service personnalisé?

J'ai un module personnalisé qui extrait des données de plusieurs bases de données externes différentes qui ont été définies comme connexions dans settings.php. La configuration actuelle comprend une série de contrôleurs qui ont un service que j'ai créé appelé DataAccess injecté en eux. Mon service DataAccess a plusieurs méthodes qui acceptent un $schema paramètre et sur cette base, j'utilise à chaque fois ce qui suit:

$connection = Database::getConnection('default', $schema);

Je peux alors interroger chaque base de données selon les besoins avec cette configuration. J'ai pensé à injecter Database dans mon service à partir du conteneur Drupal, mais j'aurais alors besoin d'une classe de base comme la méthode create de ControllerBase pour obtenir ce service en premier lieu. Quelle serait la bonne façon de faire ceci car ce n'est pas un contrôleur - est-ce que j'utiliserais une classe de base différente ici? Je suis nouveau dans le développement Drupal 8 et j'essaye de m'assurer que je suis les meilleures pratiques, mais en même temps fois je ne veux pas trop compliquer les choses.

Aucun conseil?

6
Ian Carnaghan

Vous ne pouvez pas injecter Drupal\Core\Database\Database , c'est un conteneur de fonctions statiques, constantes et variables pour les fonctions de base de données.

Si vous souhaitez injecter l'objet Connection, cela est possible et ressemble plus à quelque chose que vous voulez (sauf si vous avez un scénario où setting.php est dynamique et toutes les valeurs possibles de $schema ne peut pas être connu).

Vous pouvez suivre l'exemple Drupal définit avec la valeur par défaut @database classe dans core.services.yml :

  database:
    class: Drupal\Core\Database\Connection
    factory: Drupal\Core\Database\Database::getConnection
    arguments: [default]

Dans votre mymodule.services.yml , vous pouvez définir la fabrique/arguments pour chaque connexion à la base de données que vous avez, plus votre classe de service DataAccess avec les connexions injectées.

services:
  mymodule.database_schema:
    class: Drupal\Core\Database\Connection
    factory: Drupal\Core\Database\Database::getConnection
    arguments: [default, schema]
  mymodule.database_schema2:
    class: Drupal\Core\Database\Connection
    factory: Drupal\Core\Database\Database::getConnection
    arguments: [default, schema2]
  mymodule.data_access:
    class: Drupal\mymodule\DataAccess
    arguments: ['@mymodule.database_schema', '@mymodule.database_schema2']

De là, vous pouvez stocker les connexions à la base de données dans le constructeur de DataAccess et les référencer comme requis par les méthodes de DataAccess.

6
Shawn Conn

Comme commenté une autre réponse, la réponse ici est probablement, vous ne pouvez pas.

L'API de base de données n'est pas entièrement convertie pour utiliser les services. J'ai essayé de le faire pendant un certain temps, en https://www.drupal.org/node/18117 , mais je n'ai pas réussi et les autres non plus.

Donc, pour l'instant, vous devez utiliser les méthodes statiques. Le mieux que vous puissiez faire est de l'envelopper dans une méthode ou un autre service qui ne fait que cela pour simplifier le mocking lorsque vous souhaitez écrire des tests unitaires.

5
Berdir

En ce moment, j'utilise ce code pour injecter le service de base de données:

*. services.yml

services:
  onlyone:
    class: Drupal\onlyone\OnlyOne
    arguments: ['@entity_type.manager', '@database', '@language_manager']

Drupal\onlyone\OnlyOne

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Language\LanguageManagerInterface;

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $connection, LanguageManagerInterface $language_manager) {
    $this->entityTypeManager = $entity_type_manager;
    $this->connection = $connection;
    $this->languageManager = $language_manager;
  }
1