微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

API平台和Symfony,多实体管理器和一些api操作上的动态连接问题

如何解决API平台和Symfony,多实体管理器和一些api操作上的动态连接问题

以下代码显示一个几乎一切正常的解决方案。但不是所有... 我正在尝试为客户端使用单独的 MysqL 数据库

这是数据库结构的样子:

系统实体管理器:

AccountEntityManager:

  • database_account_1
  • database_account_2
  • database_account_3
  • database_account_4 ...越来越多...

登录用户(使用适当的 JWT 令牌)请求 API 平台时,我使用一种选择适当数据库的机制。 它在创建、删除获取集合操作期间起作用。

但是...这在获取项目或更新操作期间不起作用 此问题的原因是数据库选择错误在这些操作期间,会尝试从 database_system 而不是 database_account_1 等下载数据

当我从 swagger 文档和功能测试中请求时出现问题。

这是我的doctrine.yaml,其中包含两个实体管理器的配置:

  • 系统
  • 帐户(这是与多个客户端数据库的动态连接)
doctrine:
    dbal:
        default_connection: system
        connections:
            system:
                url: '%env(resolve:DATABASE_URL)%'
            account:
                url: '%env(resolve:DATABASE_URL)%'
                wrapper_class: App\Doctrine\DynamicConnection
    orm:
        default_entity_manager: system
        entity_managers:
            system:
                connection: system
                naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
                mappings:
                    System:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/System'
                        prefix: 'App\Entity\System'
                        alias: System
            account:
                connection: account
                naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
                mappings:
                    Account:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/Account'
                        prefix: 'App\Entity\Account'
                        alias: Account`

切换连接的类:

declare(strict_types=1);

namespace App\Doctrine;

use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;

final class DynamicConnection extends Connection
{
    public function __construct(
        array $params,Driver $driver,?Configuration $config = null,?EventManager $eventManager = null
    ) {
        parent::__construct($params,$driver,$config,$eventManager);
    }

    public function switchConnection(int $account): void
    {
        if ($this->isConnected()) {
            $this->close();
        }

        $params = $this->getParams();
        $params['dbname'] = AccountDatabaseHelper::getAccountDatabaseName($account); // return account_1 or account_2
        parent::__construct($params,$this->_driver,$this->_config,$this->_eventManager);
    }
}

当请求来自 API 并且用户被记录时,我切换数据库(我使用 user->id 作为数据库的前缀)

namespace App\Doctrine;


use Symfony\Component\Eventdispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class AccountContextControllerEventSubscriber implements EventSubscriberInterface
{
    private AccountContextService $accountContextService;
    private TokenStorageInterface $tokenStorage;

    public function __construct(AccountContextService $accountContextService,TokenStorageInterface $tokenStorage)
    {
        $this->accountContextService = $accountContextService;
        $this->tokenStorage = $tokenStorage;
    }

    public function onKernelController(ControllerEvent $event)
    {
        if (!$event->isMainRequest()) {
            return;
        }

        if ($this->tokenStorage->getToken()) {
            $this->accountContextService->switchAccount($this->tokenStorage->getToken()->getUser()->getId());
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::CONTROLLER => "onKernelController"
        ];
    }
}

这是我使用 EventdispatcherInterface 的服务(上面代码使用了这个服务)

namespace App\Doctrine;

use Psr\Eventdispatcher\EventdispatcherInterface;

class AccountContextService
{
    private EventdispatcherInterface $dispatcher;

    public function __construct(EventdispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function switchAccount(int $account)
    {
        $this->dispatcher->dispatch(new AccountContextChangeEvent($account));
    }
}

和事件订阅者:

namespace App\Doctrine;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Eventdispatcher\EventSubscriberInterface;

class DoctrineAccountContextService implements EventSubscriberInterface
{
    private EntityManagerInterface $entityManager;

    public function __construct(EntityManagerInterface $accountEntityManager)
    {
        $this->entityManager = $accountEntityManager;
    }

    public function onAccountContextChange(AccountContextChangeEvent $event): void
    {
        $this->switchAccount($event->getAccount());
    }

    public function switchAccount(int $account): void
    {
        $connection = $this->entityManager->getConnection();

        if ($connection instanceof DynamicConnection) {
            $connection->switchConnection($account);
        } else {
            throw new \LogicException("To switch connection to selected account,connection object must be instance of " . DynamicConnection::class);
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            AccountContextChangeEvent::class => "onAccountContextChange"
        ];
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。