【问题标题】:Autowire specific DBAL connection when using multiple of them使用多个时自动连接特定的 DBAL 连接
【发布时间】:2018-02-24 09:30:21
【问题描述】:

我正在使用 Doctrine 2,其中我有多个 DBAL 连接。我在 ORM 中也有多个 EntityManager。

我需要能够以某种方式将特定的 DBAL 连接自动连接到其他 Symfony 3 服务中。

我可以使用 EntityManagerDecorator 自动连接任何 EntitiyManager,但不知道如何对连接执行相同操作。我能够从 EntityManager 获得连接,但我不认为这是要走的路。

【问题讨论】:

  • 很多人在不理解问题时投反对票。我很想看看你是如何为实体管理器使用装饰器的。但就连接而言,我认为您必须定义服务并指定连接。但至少其他依赖项仍将自动装配。

标签: symfony doctrine dbal


【解决方案1】:

您可以为学说连接指定包装类,不需要代理类:

#config.yml
doctrine:
    dbal:
        connections:
            default:
                wrapper_class: AppBundle\Connections\ConnectionDefault
                ...
            second:
                wrapper_class: AppBundle\Connections\ConnectionSecond
                ...

连接应该扩展Doctrine\DBAL\Connection:

<?php

namespace AppBundle\Connection;

use Doctrine\DBAL\Connection;

class ConnectionDefault extends Connection
{

}

class ConnectionSecond extends Connection
{

}

并创建服务别名:

#services.yml
services:
    ...
    AppBundle\Connections\ConnectionDefault: '@doctrine.dbal.default_connection'
    AppBundle\Connections\ConnectionSecond: '@doctrine.dbal.second_connection'

然后您可以通过键入提示将所需的连接简单地注入服务:

class MyService {
    public function __construct(ConnectionDefault $connection) {...}
}

class MyOtherService {
    public function __construct(ConnectionSecond $connection) {...}
}

【讨论】:

  • 承认这一点要简单得多
  • 这在 Symfony 的当前版本中不起作用。它出现以下错误:Cannot autowire service "App\Connection\ConnectionDefault": argument "$params" of method "Doctrine\DBAL\Connection::__construct()" is type-hinted "array", you should configure its value explicitly.
  • 如果您收到 BrianV 报告的上述错误,您需要确保您的“连接”目录未自动连接
【解决方案2】:

我遇到了同样的问题,并更准确地测试了什么有效,为什么有效。

使用代理处理许多 DBAL 连接

编辑: 我做了一些代理抽象类。现在我的每个连接类都继承自这个代理。它工作正常:)

<?php

#src/AppBundle/Connections/ConnectionProxy.php

namespace AppBundle\Connections;

use Doctrine\DBAL\Connection;

abstract class ConnectionProxy
{
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function __call($method, $arguments)
    {
        if (is_callable(array($this->connection, $method))) {
            return call_user_func_array(array($this->connection, $method), $arguments);
        } else {
            return new \Exception("Call to undefined method '{$method}'");
        }
    }
}

我的连接默认类:

<?php

#src/AppBundle/Connections/ConnectionDefault.php

namespace AppBundle\Connections;

class ConnectionDefault extends ConnectionProxy
{

}

第二次连接:

<?php

namespace AppBundle\Connections;

class ConnectionSecond extends ConnectionProxy
{

}

然后我手动添加到我的服务文件中,用于我的所有连接(2 个连接):

AppBundle\Connections\ConnectionDefault:
    public: true
    class: AppBundle\Connections\ConnectionDefault
    arguments: ['@doctrine.dbal.default_connection']

AppBundle\Connections\ConnectionSecond:
    public: true
    class: AppBundle\Connections\ConnectionSecond
    arguments: ['@doctrine.dbal.second_connection']

然后当我使用它们的类型时,我的连接会自动自动装配

class SaveEvent
{
    /** @var  Connection */
    private $connection;

    public function __construct(ConnectionDefault $connection)
    {
        $this->connection = $connection;
    }
    .....

为每个服务注入连接

最简单的选项 - 但需要单独创建每个服务并注入连接名称 - 这意味着在您的服务文件(app/config/services.yml)中手动连接参数**,例如

AppBundle\Classes\SaveEvent:
    public: true
    arguments:
      - '@doctrine.dbal.[connection_name]_connection'

其中 connection_name 是您的连接名称。 在下面的例子中,我们在 config.yml 中有两个连接,这个值可以是“default”或“database2”:

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
              driver: pdo_sqlite
              charset: UTF8
              path: '%database_path%'
            database2:
              driver: pdo_sqlite
              charset: UTF8
              path: '%database_path%'

只为一个 DBAL 连接工作

当我们只有一个连接时,DBAL 连接的自动装配可以正常工作。如果我只保留默认连接(从 config.yml 中删除连接数据库 2)并在 app/config/services.yml 中添加一些要自动连接的路径:

AppBundle\Classes\:
    resource: '../../src/AppBundle/Classes'
    public: true

如果我在我的类中使用类型类 Doctrine\DBAL\Connection,这意味着我想注入默认连接(@doctrine.dbal.default_connection),因为我只有一个连接。

namespace AppBundle\Classes;

use Doctrine\DBAL\Connection;

class SaveEvent
{
    /** @var  Connection */
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

// …
}

回答

// 已编辑 SimPod,您的问题的答案是如上所述使用代理处理许多 DBAL 连接为每个服务注入连接

我知道您已经解决了这个问题,但我的回答可以帮助其他人。

【讨论】:

  • 我认为你的回答总结得更好。我会将其标记为已回答,因为它也可能对其他人有所帮助。你说得对。当有多个连接时,自动连接目前是不可能的,手动连接它们有点痛苦。
  • 我已经编辑了我的答案,并创建了自动连接的解决方案(您不必每次都注入它们)。
【解决方案3】:

每个 DBAL 连接始终可以通过以下标识符在服务容器中访问:

doctrine.dbal.[name]_connection

[name] 是连接名称

https://github.com/doctrine/DoctrineBundle/blob/master/Resources/doc/configuration.rst#doctrine-dbal-configuration

【讨论】:

    【解决方案4】:

    一个更简单的解决方案可以是:

    #services.yml
    services:
        _defaults:
            bind:
                $dbSecond: '@doctrine.dbal.second_connection'
    

    在控制器或服务中使用:

    public function my(Request $request, Connection $dbSecond)
    

    【讨论】:

    • 即使这可能有效,依赖注入应该基于类型而不是变量名来工作。基于变量名可能会给使用此类代码的所有其他开发人员带来意外和不愉快的惊喜。
    【解决方案5】:

    版本 3.4 中有一个新功能,它使这个过程变得更加容易。 见:Symfony 3.3 - Entity Manager injection into services with multiple database?

    【讨论】:

    • 对不起,我在这里没有看到任何帮助
    • 据我了解,从 symfony 3.4 开始,您可以为所有服务实现默认参数。然后,您可以为所有服务设置默认 EM 吗?但我不得不承认我已经快速阅读了手册..也许我错了..
    • 我认为这不是一个好方法,即使存在这种可能性(虽然我自己没有检查过文档)。我认为您应该在其中明确指定每个服务的依赖项。
    猜你喜欢
    • 2016-02-23
    • 1970-01-01
    • 2020-01-30
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 2023-02-24
    • 2014-05-06
    相关资源
    最近更新 更多