Change service parameter value dynamically for FOS

2019-07-06 08:41发布

I asked about using multiple entity manager for FOSUserBundle before, and it turns out that FOSUserBundle has (partially) supported that. All I need to do is to specify the connection / manager I want to use in the model_manager_name parameter, as explained here

fos_user:
# ........
    model_manager_name: account1

Sample app/config/config.yml

With this FOSUserBundle will use account1 connection, and use user information in that connection's database.

doctrine:
dbal:
    default_connection:       default
    connections:
    account2:
        dbname:           account2
        user:             account2
        password:         password2
        driver:   pdo_mysql
        host:     localhost
        port:     ~
        charset:  UTF8
    account1:
        dbname:           account1
        user:             account1
        password:         password1
        driver:   pdo_mysql
        host:     localhost
        port:     ~
        charset:  UTF8
    default:
        dbname:           account
        user:             account
        password:         password
        driver:   pdo_mysql
        host:     localhost
        port:     ~
        charset:  UTF8

My app require that when a user goes to (for example) http://myapp.com/a/account1, the app will use account1 connection, and going to http://myapp.com/a/account2 will use account2's connection. For my application's logic, this is easily done from my controllers as I could use something like the following;

$em = $this->get('doctrine')->getManager('account2');
$repository = $this->get('doctrine')->getRepository($class, 'account2')

For the login part though, it's not that easy. FOSUserBundle runs as a service container, and I don't know where/how to dynamically change the model_manager_name's value. I do know though that in FOS\UserBundle\DependencyInjection\FOSUserExtension I can manually change its value the following way;

class FOSUserExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
    $processor = new Processor();
    $configuration = new Configuration();

    $config = $processor->processConfiguration($configuration, $configs);

    $config['model_manager_name'] = 'account2';
    // .................

Any thoughts?

1条回答
闹够了就滚
2楼-- · 2019-07-06 09:20

The configuration is stored inside the container, in the fos_user.model_manager_name to be exact.

You can write a compiler pass. This will be executed just before freezing the container, it is the last place where you can change the container and it is the place to change the container based on other services.

Your compiler pass will look like this:

// src/Acme/DemoBundle/DependencyInjection/Compiler/ChangeModelManagerPass.php
namespace Acme\DemoBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ChangeModelManagerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $request = $container->get('request');
        $uri = $request->getUri();

        // check if the uri matches some pattern which will cause a change in the
        // `model_manager_name` setting
        if (...) {
            // ... do some stuff to get the correct model manager name

            // set the setting
            $container->setParameter('fos_user.model_manager_name', ...);
        }
    }
}

Read more about compiler passes in the docs or in this great blog post by Richard Miller.

查看更多
登录 后发表回答