1

I am setting up a multi tenant Symfony 4 application where each tenant has it's own database.

I've set up two database connections in the doctrine.yaml config. One of the connections is static based on an env variable. The other one should have a dynamic URL based on a credential provider service.

doctrine:
    dbal:
        connections:
            default:
                url: "@=service('provider.db.credentials').getUrl()"

The above expression "@=service('provider.db.credentials').getUrl()" is not being parsed though.

When injecting "@=service('provider.db.credentials').getUrl()" as argument into another service the result of getUrl() on the provider.db.credentials service is injected. But when using it in the connection configuration for doctrine the expression is not being parsed.

Does anyone have an idea how to solve this?

Anthon
  • 69,918
  • 32
  • 186
  • 246

1 Answers1

4

You're trying to rely on ability of Symfony services definition to use expressions for defining certain aspects of services. However you need to remember that this functionality is part of Dependency Injection component which is able (but not limited to) to use configuration files for services. To be more precise - this functionality is provided by configuration loaders, you can take a look here for example of how it is handled by Yaml configuration loader.

On the other hand configuration for Doctrine bundle, you're trying to use is provided by Config component. A fact that Dependency Injection component uses same file formats as Config component do may cause an impression that these cases are handled in the same way, but actually they're completely different.

To sum it up: expression inside Doctrine configuration does not work as you expecting because Doctrine bundle configuration processor doesn't expect to get an Expression Language expression and doesn't have support for handling them.

While explanations given above are, hopefully, answers your question - you're probably expecting to get some information about how to actually solve your problem.

There is at least 2 possible ways to do it, but choosing correct way may require some additional information which is out of scope of this question.

  1. In a case if you know which connection to choose at a time of container building (your code assumes that it is a case, but you may not be aware about it) - then you should use compiler pass mechanism yo update Doctrine DBAL services definitions (which may be quite tricky). Reason for this non-trivial process is that configurations are loaded at the early stages of container building process and provides no extension points. You can take a look into sources if necessary. Anyway, while possible, I would not recommend you to go in this way and most likely you will not need it because (I suppose) you need to select connection in runtime rather then in container building time.

  2. Probably more correct approach is to create own wrapper of DBAL Connection class that will maintain list of actual connections and will provide required connection depending on your application's logic. You can refer to implementation details of DBAL sharding feature as example. Wrapper class can be defined directly through Doctrine bundle configuration by using wrapper_class key for dbal configuration

Flying
  • 4,422
  • 2
  • 17
  • 25
  • It seems sharding is the proper way to go in this scenario. Specifically I have a catalog db connection. Which is used by the database credential provider for getting the database credentials for the given tenant (the name of which is passed as a header to my API). The big challenge here would be how to integrate the doctrine sharding mechanism into Symfony. – T.A.C. Commandeur Mar 19 '19 at 15:20
  • Sharding configuration is directly available as part of DBAL configuration into Doctrine bundle, take a look at `shards` configuration [section](https://symfony.com/doc/current/bundles/DoctrineBundle/configuration.html#doctrine-dbal-configuration). – Flying Mar 19 '19 at 15:39
  • I think I understand the sharding system now. Though it seems shards have to be defined in YAML. But in my case there is a catalog database which contains the database credentials per tenant/shard. – T.A.C. Commandeur Mar 19 '19 at 15:48
  • Sharding is not a proper way in your case, I've mentioned it because it have quite similar code to what you will need to write by yourself. You can refer to it as example, but it will not be usable for you directly – Flying Mar 19 '19 at 16:46