src/Database/Drivers/Mongodb.php
<?php
namespace DrMVC\Database\Drivers;
use MongoDB\BSON\ObjectID;
use MongoDB\Driver\Exception\Exception as MongoException;
use MongoDB\Driver\Exception\InvalidArgumentException;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Exception\BulkWriteException;
use MongoDB\Driver\Manager as MongoManager;
use MongoDB\Driver\BulkWrite as MongoBulk;
use MongoDB\Driver\WriteConcern as MongoWrite;
use MongoDB\Driver\Command as MongoCommand;
use MongoDB\Driver\Query as MongoQuery;
use DrMVC\Database\Exception;
use DrMVC\Database\Drivers\Interfaces\MongodbInterface;
use DrMVC\Database\Drivers\Interfaces\DriverInterface;
/**
* Class for work with modern MongoDB php driver (for PHP >= 7.0 only)
*
* @package DrMVC\Database\Drivers
* @since 3.0
*/
class Mongodb extends NoSQL implements MongodbInterface
{
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = '27017';
/**
* @link http://nl1.php.net/manual/en/mongodb-driver-manager.construct.php
*
* Additional connection string options, which will overwrite any options with
* the same name in the uri parameter.
*/
const AVAILABLE_OPTIONS = [
'appname',
'authMechanism',
'authMechanismProperties',
'authSource',
'canonicalizeHostname',
'compressors',
'connectTimeoutMS',
'gssapiServiceName',
'heartbeatFrequencyMS',
'journal',
'localThresholdMS',
'maxStalenessSeconds',
'password',
'readConcernLevel',
'readPreference',
'readPreferenceTags',
'replicaSet',
'retryWrites',
'safe',
'serverSelectionTimeoutMS',
'serverSelectionTryOnce',
'slaveOk',
'socketCheckIntervalMS',
'socketTimeoutMS',
'ssl',
'username',
'w',
'wTimeoutMS',
'zlibCompressionLevel'
];
const AVAILABLE_DRIVER_OPTIONS = [
'allow_invalid_hostname',
'ca_dir',
'ca_file',
'crl_file',
'pem_file',
'pem_pwd',
'context',
'weak_cert_validation'
];
/**
* Get current connection
*
* @return MongoManager
*/
public function getInstance(): MongoManager
{
return $this->_instance;
}
/**
* Initiate connection with database
*
* @return DriverInterface
*/
public function connect(): DriverInterface
{
// URL options
$options = $this->getConfig()->get();
// TODO: This option should be optional, in addition, it may not be an array
// Driver options
$optionsDriver = $options['driver_options']->get();
try {
$connection = new MongoManager(
$this->getDsn(),
$this->getOptions($options, self::AVAILABLE_OPTIONS),
$this->getOptions($optionsDriver, self::AVAILABLE_DRIVER_OPTIONS)
);
$this->setInstance($connection);
} catch (RuntimeException $e) {
new Exception('Unable to connect');
} catch (InvalidArgumentException $e) {
new Exception('Invalid argument provided');
}
return $this;
}
/**
* Generate DSN by parameters in config
*
* @param array $config
* @return string
*/
public function genDsn($config): string
{
// Get driver of connection
$driver = strtolower($config['driver']);
$url = $config['url'];
return "$driver://$url";
}
/**
* Generate options array
*
* @param array $options
* @param array $allowed
* @return array
*/
private function getOptions(array $options, array $allowed): array
{
$result = [];
foreach ($options as $key => $value) {
if (\in_array($key, $allowed, false)) {
$result[$key] = $value;
}
}
return $result;
}
/**
* @return MongoBulk
*/
private function getBulk(): MongoBulk
{
try {
$bulk = new MongoBulk();
} catch (InvalidArgumentException $e) {
new Exception('Unable to create Bulk object');
}
return $bulk;
}
/**
* @return ObjectID
*/
private function getID(): ObjectID
{
try {
$objectID = new ObjectID();
} catch (InvalidArgumentException $e) {
new Exception('ObjectID could not to be generated');
}
return $objectID;
}
/**
* @return MongoWrite
*/
private function getWrite(): MongoWrite
{
try {
$write = new MongoWrite(MongoWrite::MAJORITY, 1000);
} catch (InvalidArgumentException $e) {
new Exception('WriteConcern could not to be initiated');
}
return $write;
}
/**
* Insert in database and return of inserted element
*
* @param array $data array of columns and values
* @return mixed
*/
public function insert(array $data): string
{
// Set object ID as id of item
$data['_id'] = $this->getID();
// Set statement of bulk object
$bulk = $this->getBulk();
$bulk->insert($data);
try {
$this->getInstance()->executeBulkWrite(
$this->getParam('database') . '.' . $this->getCollection(),
$bulk,
$this->getWrite()
);
} catch (BulkWriteException $e) {
new Exception('Unable to write in database');
}
return (string) $data['_id'];
}
/**
* Create query object from filter and option arrays
*
* @param array $where
* @param array $options
* @return MongoQuery
*/
private function getQuery(array $where, array $options): MongoQuery
{
try {
$query = new MongoQuery($where, $options);
} catch (InvalidArgumentException $e) {
new Exception('WriteConcern could not to be initiated');
}
return $query;
}
/**
* Execute MongoQuery
*
* @param array $filter
* @param array $options
* @return mixed
*/
public function select(array $filter = [], array $options = [])
{
// Create query object from filter and option arrays
$query = $this->getQuery($filter, $options);
try {
$cursor = $this->getInstance()->executeQuery(
$this->getParam('database') . '.' . $this->getCollection(),
$query
);
$response = $cursor->toArray();
} catch (MongoException $e) {
new Exception('Unable to execute query');
}
return $response ?? false;
}
/**
* Update data in database
*
* @param array $data
* @param array $filter
* @param array $updateOptions
* @return mixed
*/
public function update(array $data, array $filter = [], array $updateOptions = [])
{
// Set statement of bulk object
$bulk = $this->getBulk();
$bulk->update($filter, $data, $updateOptions);
try {
$response = $this->getInstance()->executeBulkWrite(
$this->getParam('database') . '.' . $this->getCollection(),
$bulk,
$this->getWrite()
);
} catch (BulkWriteException $e) {
new Exception('Unable to write in database');
}
return $response ?? false;
}
/**
* Delete data from table/collection
*
* @param array $filter
* @param array $deleteOptions
* @return mixed
*/
public function delete(array $filter, array $deleteOptions = [])
{
// Set statement of bulk object
$bulk = $this->getBulk();
$bulk->delete($filter, $deleteOptions);
try {
$response = $this->getInstance()->executeBulkWrite(
$this->getParam('database') . '.' . $this->getCollection(),
$bulk,
$this->getWrite()
);
} catch (BulkWriteException $e) {
new Exception('Unable to write in database');
}
return $response ?? false;
}
/**
* Create command from query
*
* @param array $query
* @return MongoCommand
*/
private function getCommand($query): MongoCommand
{
try {
$command = new MongoCommand($query);
} catch (InvalidArgumentException $e) {
new Exception('WriteConcern could not to be initiated');
}
return $command;
}
/**
* Execute MongoCommand
*
* @param array $query should be like new MongoDB\Driver\Query($filter, $options);
* @return mixed
*/
public function command(array $query)
{
// Create command from query
$command = $this->getCommand($query);
try {
$cursor = $this->getInstance()->executeCommand(
$this->getParam('database'),
$command
);
$response = $cursor->toArray();
} catch (MongoException $e) {
new Exception('Unable to execute command');
}
return $response ?? false;
}
}