1
votes

I have a Symfony application that shares the same codebase for many DBs.

It's quite typical, I have a single database for each "instance" (or "account") of my application, which is a simple medical appointments tool. I need to keep this architecture as is.

However, we are migrating from Symfony 2.8 to Symfony 4. Before Symfony 4, I had to create a "pseudo-prod" Symfony environment for each DB, because Symfony (with Doctrine) is very tied to having a single DB specified by the config, and even using env vars like SYMFONY__DATABASE__NAME, the database connection properties get hardcoded into the cache, so I needed to create a different environment for each app instance, hence creating a cache dir for each account, which is far from ideal.

Will the new env vars features from Symfony 4 make dynamic DB connection more feasible in a "native" manner?

Should I use another method to achieve this? (for example, creating a custom connection factory, etc)

I would like to have a single cache for my prod environment, and let the connection parameters be dynamic.

Any ideas appreciated. Thanks!

Note:

This is NOT a duplicate of: Using Relationships with Multiple Entity Managers

I am already using multiple entity managers, it has nothing to do with that.

1
Thanks, I read that, but I hardly see such a pattern ("database_host2") as "dynamic", I don't have a single "customer", what if I have > 50 databases? Do I have to add an entity manager for each one, database_host3, database_host4? All these values get hardcoded into the cache, which prevents me from having a single cache environment for all DBs. Will the env shorthand introduced in 3.2 solve this ("password: "%env(DB_PASSWORD)%")?Pablo Hessualdo
You would need to add a connection for each database that your application uses, in order for doctrine to connect to it. If you're using separate environments for multiple applications, then you can separate them out that way. Entities cannot define associations across different entity managers. If you need that, there are several alternatives that require some custom setup.Will B.
Yes, I get it, I'm actually using two entity managers, one for my client's data, which depends on its subdomain (say: johndoe.myapp.com), and one for some shared data, so I have two EMs, one for "myapp_shared" DB, and one for a "johndoe_data" DB. The thing is, DB connection data is hardcoded into the cache, which feels like nonsense IMO, because it forces me to have a cache dir per DB.Pablo Hessualdo

1 Answers

2
votes

Setting up your application for different hosts with Symfony 4 should be straight forward. You can just provide the DATABASE_URL as environment variable to each host, e.g. with nginx:

server {
    server_name domain.tld www.domain.tld;
    root /var/www/project/public;
    location / {
        try_files $uri /index.php$is_args$args;
    }
    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php7.1-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";

        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;

        internal;
    }
    location ~ \.php$ {
        return 404;
    }
    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;
}

The import bit is fastcgi_param DATABASE_URL ....

By default all instances will use the same cache dir, which is probably not what you want. Imagine customer A seeing customer B's data, because it's pulled from cache and B accessed it first.

A way around this is to modify src/Kernel.php to factor in some other env variable, the project's base name or some other info identifying each host and append that to the cache base directory defined in getCacheDir. By default it looks like this:

public function getCacheDir(): string
{
    return dirname(__DIR__).'/var/cache/'.$this->environment;
}

You could also use Symfony's built in cache component to specify different app.caches per host instead. This way you can possibly reuse the system cache responsible for caching the container, annotations, validations, etc.

You can find the cache configuration in your config/packages/framework.yml under framework.cache. See: https://speakerdeck.com/dbrumann/caching-in-symfony-an-overview?slide=37