Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FRAM-153] Add MongoConnectionFactory #6

Open
wants to merge 2 commits into
base: 2.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
222 changes: 183 additions & 39 deletions src/MongoDB/Driver/MongoConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Bdf\Prime\Connection\ConnectionInterface;
use Bdf\Prime\Connection\Result\ResultSetInterface;
use Bdf\Prime\Exception\DBALException;
use Bdf\Prime\MongoDB\Driver\Exception\MongoCommandException;
use Bdf\Prime\MongoDB\Driver\Exception\MongoDBALException;
use Bdf\Prime\MongoDB\Driver\ResultSet\CursorResultSet;
Expand All @@ -30,13 +29,18 @@
use Bdf\Prime\Query\Factory\DefaultQueryFactory;
use Bdf\Prime\Query\Factory\QueryFactoryInterface;
use Bdf\Prime\Query\ReadCommandInterface;
use Closure;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ConnectionException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Statement;
use InvalidArgumentException;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
Expand All @@ -46,51 +50,61 @@
use MongoDB\Driver\Query;
use MongoDB\Driver\WriteResult;

use function array_filter;
use function implode;

/**
* Connection for mongoDb
*
* @property Manager $_conn
* @method \Bdf\Prime\Configuration getConfiguration()
* @final
*/
class MongoConnection extends Connection implements ConnectionInterface
{
/**
* @var string
*/
protected $name;

/**
* @var PrimeSchemaManager
*/
protected $schema;
protected string $name = '';
protected ?PrimeSchemaManager $schema = null;

/**
* Field for emulate transaction into MongoDB (will be added on
* Field for emulate transaction into MongoDB (will be added on each document)
*
* @var string
*/
protected $transactionEmulationStateField = '__MONGO_CONNECTION_TRANSACTION__';
protected string $transactionEmulationStateField = '__MONGO_CONNECTION_TRANSACTION__';

/**
* Level for transaction emulation
*
* @var int
*/
protected $transationLevel = 0;
protected int $transationLevel = 0;

protected ?PrimePlatform $platform = null;
protected QueryFactoryInterface $factory;

/**
* @var PrimePlatform
* The mongoDB connection
* Do not use directly, use $this->connection() instead
*/
protected $platform;
private ?Manager $connection = null;
private array $parameters;

/**
* @var QueryFactoryInterface
* @param $params
* @param Driver|Configuration|null $driver
* @param Configuration|null $config
* @param EventManager|null $eventManager
* @throws \Doctrine\DBAL\Exception
*/
private $factory;

public function __construct($params, Driver $driver, Configuration $config = null, EventManager $eventManager = null)
public function __construct($params, $driver = null, ?Configuration $config = null, EventManager $eventManager = null)
{
parent::__construct($params, $driver, $config, $eventManager);
if ($config === null && $driver instanceof Configuration) {
$config = $driver;
$driver = null;
}

parent::__construct($params, $driver ?? new MongoDriver(), $config, $eventManager);

$this->parameters = $params;

/** @psalm-suppress InvalidArgument */
$this->factory = new DefaultQueryFactory(
Expand Down Expand Up @@ -132,7 +146,7 @@
*/
public function getDatabase(): string
{
return $this->getParams()['dbname'];
return $this->parameters['dbname'];
}

/**
Expand All @@ -154,7 +168,7 @@
{
if ($this->platform === null) {
$this->platform = new PrimePlatform(
$this->getDatabasePlatform(),
new MongoPlatform(),
$this->getConfiguration()->getTypes()
);
}
Expand Down Expand Up @@ -228,9 +242,7 @@
public function executeSelect($collection, Query $query): Cursor
{
try {
$this->connect();

$cursor = $this->_conn->executeQuery($this->getNamespace($collection), $query);
$cursor = $this->connection()->executeQuery($this->getNamespace($collection), $query);
$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);

return $cursor;
Expand All @@ -249,9 +261,7 @@
public function executeWrite($collection, BulkWrite $query): WriteResult
{
try {
$this->connect();

return $this->_conn->executeBulkWrite($this->getNamespace($collection), $query);
return $this->connection()->executeBulkWrite($this->getNamespace($collection), $query);
} catch (Exception $e) {
throw new MongoDBALException('MongoDB : ' . $e->getMessage(), 0, $e);
}
Expand All @@ -273,6 +283,22 @@
throw new \BadMethodCallException('Method ' . __METHOD__ . ' cannot be called on mongoDB connection');
}

/**
* {@inheritdoc}
*/
public function prepare(string $sql): Statement
{
throw new \BadMethodCallException('Method ' . __METHOD__ . ' cannot be called on mongoDB connection');
}

/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
throw new \BadMethodCallException('Method ' . __METHOD__ . ' cannot be called on mongoDB connection');
}

/**
* Run a command
*
Expand All @@ -285,9 +311,7 @@
public function runCommand($command, $arguments = 1): Cursor
{
try {
$this->connect();

return $this->_conn->executeCommand(
return $this->connection()->executeCommand(
$this->getDatabase(),
Commands::create($command, $arguments)->get()
);
Expand All @@ -309,9 +333,7 @@
public function runAdminCommand($command, $arguments = 1)
{
try {
$this->connect();

return $this->_conn->executeCommand(
return $this->connection()->executeCommand(
'admin',
Commands::create($command, $arguments)->get()
);
Expand All @@ -337,7 +359,7 @@
{
$this->connect();

foreach ($this->getSchemaManager()->listTableNames() as $collection) {
foreach ($this->schema()->getCollections() as $collection) {
$bulk = new BulkWrite();
$bulk->update([], [
'$inc' => [
Expand Down Expand Up @@ -378,7 +400,7 @@
throw ConnectionException::noActiveTransaction();
}

foreach ($this->getSchemaManager()->listTableNames() as $collection) {
foreach ($this->schema()->getCollections() as $collection) {
$bulk = new BulkWrite();
$bulk->update([], [
'$inc' => [
Expand All @@ -403,7 +425,7 @@
throw ConnectionException::noActiveTransaction();
}

foreach ($this->getSchemaManager()->listTableNames() as $collection) {
foreach ($this->schema()->getCollections() as $collection) {
$bulk = new BulkWrite();

$bulk->delete([
Expand Down Expand Up @@ -450,7 +472,7 @@
return new CursorResultSet($this->runCommand($compiled));
}

throw new \InvalidArgumentException('Unsupported compiled query type ' . get_class($compiled));
throw new InvalidArgumentException('Unsupported compiled query type ' . get_class($compiled));

Check warning on line 475 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L475

Added line #L475 was not covered by tests
}

/**
Expand All @@ -469,5 +491,127 @@
public function close(): void
{
parent::close();

if ($this->connection !== null) {
$this->connection = null;

Check warning on line 496 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L495-L496

Added lines #L495 - L496 were not covered by tests
}
}

private function connection(): Manager
{
if ($this->connection !== null) {
return $this->connection;
}

$params = $this->parameters;
$dsn = $this->buildDsn($params);

return $this->connection = new Manager($dsn, array_filter($params));
}

private function buildDsn(array $params): string
{
$uri = 'mongodb://';

if (!empty($params['host'])) {
$uri .= $params['host'];

if (!empty($params['port'])) {
$uri .= ':' . $params['port'];

Check warning on line 520 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L520

Added line #L520 was not covered by tests
}

return $uri;
}

if (!empty($params['hosts'])) {
$uri .= implode(',', $params['hosts']);

Check warning on line 527 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L526-L527

Added lines #L526 - L527 were not covered by tests

return $uri;

Check warning on line 529 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L529

Added line #L529 was not covered by tests
}

throw new InvalidArgumentException('Cannot build mongodb DSN');

Check warning on line 532 in src/MongoDB/Driver/MongoConnection.php

View check run for this annotation

Codecov / codecov/patch

src/MongoDB/Driver/MongoConnection.php#L532

Added line #L532 was not covered by tests
}

// Mark all methods of doctrine's connection as deprecated

/**
* @deprecated
*/
public function getDriver()
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::getDriver();
}

/**
* @deprecated
*/
public function getDatabasePlatform()
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::getDatabasePlatform();
}

/**
* @deprecated
*/
public function createExpressionBuilder(): ExpressionBuilder
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::createExpressionBuilder();
}

/**
* @deprecated
*/
public function isConnected()
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::isConnected();
}

/**
* @deprecated
*/
public function transactional(Closure $func)
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::transactional($func);
}

/**
* @deprecated
*/
public function getNativeConnection()
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::getNativeConnection();
}

/**
* @deprecated
*/
public function createSchemaManager(): AbstractSchemaManager
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::createSchemaManager();
}

/**
* @deprecated
*/
public function convertToDatabaseValue($value, $type)
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::convertToDatabaseValue($value, $type);
}

/**
* @deprecated
*/
public function convertToPHPValue($value, $type)
{
@trigger_error('Method ' . __METHOD__ . ' is deprecated without replacement.', E_USER_DEPRECATED);
return parent::convertToPHPValue($value, $type);
}
}
38 changes: 38 additions & 0 deletions src/MongoDB/Driver/MongoConnectionFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Bdf\Prime\MongoDB\Driver;

use Bdf\Prime\Configuration;
use Bdf\Prime\Connection\ConnectionInterface;
use Bdf\Prime\Connection\Factory\ConnectionFactoryInterface;
use Bdf\Prime\Exception\DBALException;

/**
* Factory for create a MongoConnection
*
* This factory skip the Doctrine DBAL connection creation using {@see ConnectionFactory::registerDriverMap()},
* so it should be registered before.
*
* This factory will handle DSN with protocol "mongodb" and "mongo"
*/
final class MongoConnectionFactory implements ConnectionFactoryInterface
{
/**
* {@inheritdoc}
*/
public function create(string $connectionName, array $parameters, ?Configuration $config = null): ConnectionInterface
{
$connection = new MongoConnection($parameters, $config ?? new Configuration());
$connection->setName($connectionName);

return $connection;
}

/**
* {@inheritdoc}
*/
public function support(string $connectionName, array $parameters): bool
{
return $parameters['driver'] === 'mongodb' || $parameters['driver'] === 'mongo';
}
}