8

I have a custom module that pulls data from several different external databases that have been defined as connections in settings.php. The current setup includes a series of controllers that have a service I have created called DataAccess injected into them. My DataAccess service has several methods that accept a $schema parameter and based on this I use the following each time:

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

I can then query each database as needed with this setup. I thought about injecting Database into my service from the Drupal container, but I would then need a base class like ControllerBase's create method to get that service in the first place. What would be the correct way to do this as it isn't a controller - would I use a different base class here? I am new to Drupal 8 development and trying to ensure I am following the best practices, but at the same time I don't want to overcomplicate things.

Any advice?

3 Answers 3

7

You can't inject Drupal\Core\Database\Database, it's a container of static functions, constants, & variables for core database functions.

If you'd like to inject the Connection object, that is be possible, and is more like something you want (unless you have scenario where setting.php is dynamic an all possible values of $schema can't be known).

You can follow the example Drupal sets with the default @database class in core.services.yml:

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

In your mymodule.services.yml you can define the factory/arguments for each database connection you have, plus your DataAccess service class with the injected connections.

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']

From there you can store the database connections in DataAccess's constructor and reference them as required by DataAccess's methods.

1
  • In general good answer but I fear this won't work in this case, $schema is an array and is passed as an argument to the service.
    – Berdir
    Sep 4, 2016 at 6:53
6

As commented on another answer, the answer here is probably, you can't.

The database API isn't fully converted to use services. I tried to do it for quite some time, in https://www.drupal.org/node/1811730, but didn't succeed and neither did others.

So, for now, you need to use the static methods. The best you can do is wrap it in a method or another service that does just that to simplify mocking it when you want to write unit tests.

2
  • I think that now is possible to inject it, at least it works for me like in my answer. Dec 6, 2017 at 16:32
  • 1
    There is a change record related to the DB function, see drupal.org/node/2993033 You can find examples where and how you can get the database connection.
    – voleger
    Aug 29, 2018 at 22:28
4

At this moment I'm using this code to inject the database service:

*.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;
  }
2
  • 1
    That's the default database service, sure, that works. The question was as far as I understood about connection to a different database. That might also work by defining your own service with the right arguments for that, but it's a bit more complicated than this
    – Berdir
    Dec 6, 2017 at 18:13
  • @Berdir I see, maybe was me that misread the question. Dec 6, 2017 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.