如何解决API平台和Symfony,多实体管理器和一些api操作上的动态连接问题
以下代码显示了一个几乎一切正常的解决方案。但不是所有... 我正在尝试为客户端使用单独的 MysqL 数据库。
这是数据库结构的样子:
系统实体管理器:
- database_system
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 举报,一经查实,本站将立刻删除。