Skip to content

Commit

Permalink
Added mongo database support. Will do it properly with SOLID later on
Browse files Browse the repository at this point in the history
  • Loading branch information
kschroeder committed Dec 27, 2017
1 parent 7f44c8e commit 87bb838
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 16 deletions.
1 change: 1 addition & 0 deletions assets/magium-configuration.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<xs:sequence>
<xs:element type="xs:string" name="driver" />
<xs:element type="xs:string" name="database" />
<xs:element type="xs:string" name="table" minOccurs="0"/>
<xs:element type="xs:string" name="username" minOccurs="0"/>
<xs:element type="xs:string" name="password" minOccurs="0"/>
<xs:element type="xs:string" name="hostname" minOccurs="0"/>
Expand Down
19 changes: 11 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,26 @@
"zendframework/zend-http": "^2",
"zendframework/zend-form": "^2",
"zendframework/zend-i18n": "^2",
"zendframework/zend-json": "^2"
"zendframework/zend-json": "^2",
"mongodb/mongodb": "^1.2",
"zendframework/zend-db": "^2.0"
},
"require": {
"zendframework/zend-cache": "^2.0",
"psr/container": "^1",
"zendframework/zend-db": "^2.0",
"symfony/console": "~2.3|~3.0",
"psr/http-message": "^1.0",
"symfony/yaml": "^2"
},
"suggest": {
"zendframework/zend-psr7bridge": "^0.1",
"zendframework/zend-view": "^2",
"zendframework/zend-http": "^2",
"zendframework/zend-form": "^2",
"zendframework/zend-i18n": "^2",
"zendframework/zend-json": "^2"
"zendframework/zend-psr7bridge": "For use with the optional view layer",
"zendframework/zend-view": "For use with the optional view layer",
"zendframework/zend-http": "For use with the optional view layer",
"zendframework/zend-form": "For use with the optional view layer",
"zendframework/zend-i18n": "For use with the optional view layer",
"zendframework/zend-json": "For use with the optional view layer",
"mongodb/mongodb": "If you intend to use MongoDB for your configuration storage",
"zendframework/zend-db": "If you intend to use a relational DB for your configuration storage"
},
"bin": ["bin/magium-configuration"]
}
51 changes: 47 additions & 4 deletions lib/Config/BuilderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace Magium\Configuration\Config;

use Magium\Configuration\Config\Storage\Mongo;
use Magium\Configuration\Config\Storage\RelationalDatabase;
use Magium\Configuration\File\Configuration\ConfigurationFileRepository;
use Magium\Configuration\File\Context\AbstractContextConfigurationFile;
use Magium\Configuration\InvalidConfigurationException;
use Magium\Configuration\Manager\CacheFactory;
use MongoDB\Client;
use Zend\Db\Adapter\Adapter;

class BuilderFactory implements BuilderFactoryInterface
Expand Down Expand Up @@ -36,19 +38,60 @@ protected function getCache(\SimpleXMLElement $element)
return $cacheFactory->getCache($element);
}

public function getAdapter()
public function getDatabaseConfiguration()
{
$config = json_encode($this->configuration->persistenceConfiguration);
$config = json_decode($config, true);
return $config;
}

public function getRelationalAdapter()
{
if (!$this->adapter instanceof Adapter) {
$config = json_encode($this->configuration->persistenceConfiguration);
$config = json_decode($config, true);
$config = $this->getDatabaseConfiguration();
$this->adapter = new Adapter($config);
}
return $this->adapter;
}

public function getMongoDsnString()
{
$config = $this->getDatabaseConfiguration();
$dsn = 'mongodb://'; //[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]';
$at = false;
if (isset($config['username'])) {
$dsn .= $config['username'];
$at = true;
}
if (isset($config['password'])) {
$dsn .= ':'.$config['password'];
$at = true;
}
if ($at) {
$dsn .= '@';
}
$dsn .= $config['hostname'];
if (isset($config['port'])) {
$dsn .= ':' . $config['port'];
}
return $dsn;
}

public function getMongoAdapter()
{
$config = $this->getDatabaseConfiguration();
$dsn = $this->getMongoDsnString();
$client = new Client($dsn);
$collection = isset($config['table'])?$config['table']:Mongo::TABLE;
return new Mongo($client->selectCollection($config['database'], $collection));
}

public function getPersistence()
{
$persistence = new RelationalDatabase($this->getAdapter(), $this->contextFile);
if (stripos($this->configuration->persistenceConfiguration->driver, 'mongo') === 0) {
return $this->getMongoAdapter();
}
$persistence = new RelationalDatabase($this->getRelationalAdapter(), $this->contextFile);
return $persistence;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Config/BuilderFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function getBuilder();
* @return Adapter
*/

public function getAdapter();
public function getRelationalAdapter();

/**
* @return Storage\StorageInterface
Expand Down
65 changes: 65 additions & 0 deletions lib/Config/Storage/Mongo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Magium\Configuration\Config\Storage;

use Magium\Configuration\Config\Repository\ConfigurationRepository;
use MongoDB\Collection;


class Mongo implements StorageInterface
{
const TABLE = 'magium_configuration_values';

private $mongo;

public function __construct(
Collection $mongo
)
{
$this->mongo = $mongo;
}

public function getCollection()
{
return $this->mongo;
}

public function getValue($path, $context = ConfigurationRepository::CONTEXT_DEFAULT)
{
$document = $this->mongo->findOne([
'context' => $context
]);
$paths = explode('/', $path);
if ($document === null) {
$document = [];
}
if (isset($document[$paths[0]][$paths[1]][$paths[2]])) {
return $document[$paths[0]][$paths[1]][$paths[2]];
}
return null;
}

public function setValue($path, $value, $context = ConfigurationRepository::CONTEXT_DEFAULT)
{
$document = $this->mongo->findOne([
'context' => $context
]);
$paths = explode('/', $path);
if ($document === null) {
$document = [];
}
$document[$paths[0]][$paths[1]][$paths[2]] = $value;
if (isset($document['_id'])) {
$this->mongo->replaceOne(['_id' => $document['_id']], $document);
return;
}
$this->mongo->insertOne($document);
}

public function create()
{
// Not necessary
}


}
8 changes: 5 additions & 3 deletions lib/Config/Storage/RelationalDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ class RelationalDatabase implements StorageInterface
protected $adapter;
protected $configurationFile;
protected $data = [];
protected $table;

public function __construct(
Adapter $adapter,
AbstractContextConfigurationFile $context
AbstractContextConfigurationFile $context,
$table = self::TABLE
)
{
$adapter->getDriver()->getConnection()->connect(); // Force a consistent connect point
$this->adapter = $adapter;
$this->configurationFile = $context;

$this->table = $table;
}

public function getContexts()
Expand Down Expand Up @@ -158,7 +160,7 @@ protected function doUpdate(Sql $sql, $path, $value, $context)

public function create()
{
$table = new CreateTable(self::TABLE);
$table = new CreateTable($this->table);
$table->addColumn(new Varchar('path', 255));
$table->addColumn(new Text('value'));
$table->addColumn(new Varchar('context', 255));
Expand Down
106 changes: 106 additions & 0 deletions tests/Config/BuilderFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Magium\Configuration\Config\BuilderFactory;
use Magium\Configuration\Config\MergedStructure;
use Magium\Configuration\Config\Storage\Mongo;
use Magium\Configuration\File\Context\AbstractContextConfigurationFile;
use Magium\Configuration\InvalidConfigurationException;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -94,6 +95,111 @@ public function testGetOneConfigurationFile()
self::assertContains(realpath(__DIR__ . '/xml/config-merge-1.xml'), $files);
}

public function testMongoDsn()
{
$config = new \SimpleXMLElement(<<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<magiumBase xmlns="http://www.magiumlib.com/BaseConfiguration">
<persistenceConfiguration>
<driver>mongo</driver>
<database>database</database>
<hostname>hostname</hostname>
<table>table</table>
<username>username</username>
<password>password</password>
<port>27017</port>
</persistenceConfiguration>
</magiumBase>
XML
);
$builderFactory = new BuilderFactory(
new \SplFileInfo(__DIR__),
$config,
$this->getMockBuilder(AbstractContextConfigurationFile::class)->disableOriginalConstructor()->getMock()
);
$dsn = $builderFactory->getMongoDsnString();
self::assertEquals('mongodb://username:password@hostname:27017', $dsn);
}

public function testMongoDsnWithRequiredOnly()
{
$config = new \SimpleXMLElement(<<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<magiumBase xmlns="http://www.magiumlib.com/BaseConfiguration">
<persistenceConfiguration>
<driver>mongo</driver>
<database>database</database>
<hostname>hostname</hostname>
</persistenceConfiguration>
</magiumBase>
XML
);
$builderFactory = new BuilderFactory(
new \SplFileInfo(__DIR__),
$config,
$this->getMockBuilder(AbstractContextConfigurationFile::class)->disableOriginalConstructor()->getMock()
);
$dsn = $builderFactory->getMongoDsnString();
self::assertEquals('mongodb://hostname', $dsn);
}

public function testMongoAdapter()
{
if (!extension_loaded('mongodb')) {
$this->markTestSkipped('Mongo extension not installed');
}

$config = new \SimpleXMLElement(<<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<magiumBase xmlns="http://www.magiumlib.com/BaseConfiguration">
<persistenceConfiguration>
<driver>mongo</driver>
<database>database</database>
<hostname>localhost</hostname>
</persistenceConfiguration>
</magiumBase>
XML
);
$builderFactory = new BuilderFactory(
new \SplFileInfo(__DIR__),
$config,
$this->getMockBuilder(AbstractContextConfigurationFile::class)->disableOriginalConstructor()->getMock()
);
$mongo = $builderFactory->getMongoAdapter();
$collection = $mongo->getCollection();
$name = $collection->getCollectionName();
self::assertEquals(Mongo::TABLE, $name);
}

public function testMongoAdapterWithBespokeCollection()
{
if (!extension_loaded('mongodb')) {
$this->markTestSkipped('Mongo extension not installed');
}

$config = new \SimpleXMLElement(<<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<magiumBase xmlns="http://www.magiumlib.com/BaseConfiguration">
<persistenceConfiguration>
<driver>mongo</driver>
<database>database</database>
<table>test_collection</table>
<hostname>localhost</hostname>
</persistenceConfiguration>
</magiumBase>
XML
);
$builderFactory = new BuilderFactory(
new \SplFileInfo(__DIR__),
$config,
$this->getMockBuilder(AbstractContextConfigurationFile::class)->disableOriginalConstructor()->getMock()
);
$mongo = $builderFactory->getMongoAdapter();
$collection = $mongo->getCollection();
$name = $collection->getCollectionName();
self::assertEquals('test_collection', $name);
}

public function testGetMultipleConfigurationFiles()
{
$config = new \SimpleXMLElement(<<<XML
Expand Down
Loading

0 comments on commit 87bb838

Please sign in to comment.