Skip to content

Commit 708d092

Browse files
rminchella-deezerHendra-Huang
authored andcommitted
Entities mapping per keyspace (#11)
* Manage group of entities per connection for schema creation process (= multi keyspaces) * Remove useless isRequired property for new paramater entity_group_prefix_folder * Fix unit tests for cassandra extension * Fix unit test * Add entity_managers configuration parameter under orm * Add entity_managers orm config parameter to define mapping of entity directories per entityManager which uses a connection (keyspace) * Replace slash with backslash slash for regexp of entity directory mappings * Review regexp directory for schema creation process * Add README section for entityManager * Fix Checkstyles * Review checkstyles * Fix based on reviews * Fix checkstyle
1 parent 4e35a63 commit 708d092

11 files changed

Lines changed: 238 additions & 11 deletions

File tree

Cassandra/ORM/EntityManager.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,31 @@ class EntityManager implements Session, EntityManagerInterface
2323
private $repositoryFactory;
2424
private $schemaManager;
2525

26+
/** @var array */
27+
private $config;
28+
2629
const STATEMENT = 'statement';
2730
const ARGUMENTS = 'arguments';
2831

29-
public function __construct(Connection $connection, ClassMetadataFactoryInterface $metadataFactory, LoggerInterface $logger)
30-
{
32+
/**
33+
* @param Connection $connection
34+
* @param ClassMetadataFactoryInterface $metadataFactory
35+
* @param LoggerInterface $logger
36+
* @param array $config
37+
*/
38+
public function __construct(
39+
Connection $connection,
40+
ClassMetadataFactoryInterface $metadataFactory,
41+
LoggerInterface $logger,
42+
$config = []
43+
) {
3144
$this->connection = $connection;
3245
$this->logger = $logger;
3346
$this->metadataFactory = $metadataFactory;
3447
$this->schemaManager = new SchemaManager($connection);
3548
$this->repositoryFactory = new DefaultRepositoryFactory();
3649
$this->statements = [];
50+
$this->config = $config;
3751
}
3852

3953
public function getConnection()
@@ -56,6 +70,16 @@ public function getLogger()
5670
return $this->logger;
5771
}
5872

73+
public function getTargetedEntityDirectories()
74+
{
75+
$entityDirectories = [];
76+
foreach ($this->config['mappings'] as $type => $mapping) {
77+
$entityDirectories[$type] = isset($mapping['dir']) ? $mapping['dir'] : false;
78+
}
79+
80+
return $entityDirectories;
81+
}
82+
5983
/**
6084
* Gets the metadata factory used to gather the metadata of classes.
6185
*

Cassandra/ORM/EntityManagerInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@
55
interface EntityManagerInterface
66
{
77
public function getConnection();
8+
9+
/**
10+
* @return string
11+
*/
12+
public function getTargetedEntityDirectories();
813
}

Cassandra/ORM/Tools/SchemaCreate.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ public function execute($connection = 'default')
1616
$em = $this->container->get(sprintf('cassandra.%s_entity_manager', $connection));
1717
$schemaManager = $em->getSchemaManager();
1818

19+
$entityDirectoriesRegexp = '/src\/.*Entity\//';
20+
$entityDirectories = $em->getTargetedEntityDirectories();
21+
if (!empty($entityDirectories)) {
22+
$entityDirectories = array_map(function ($entityDirectory) {
23+
return str_replace('/', '\/', $entityDirectory);
24+
}, $entityDirectories);
25+
$entityDirectoriesRegexp = sprintf('/((%s))/', implode(')|(', $entityDirectories));
26+
}
27+
1928
// Get all files in src/*/Entity directories
2029
$path = $this->container->getParameter('kernel.root_dir').'/../src';
2130
$iterator = new \RegexIterator(
@@ -31,7 +40,7 @@ public function execute($connection = 'default')
3140
if (!preg_match('(^phar:)i', $sourceFile)) {
3241
$sourceFile = realpath($sourceFile);
3342
}
34-
if (preg_match('/src\/.*Entity\//', $sourceFile)) {
43+
if (preg_match($entityDirectoriesRegexp, $sourceFile)) {
3544
$className = str_replace('/', '\\', preg_replace('/(.*src\/)(.*).php/', '$2', $sourceFile));
3645
$metadata = $em->getClassMetadata($className);
3746
$tableName = $metadata->table['name'];

DependencyInjection/CassandraExtension.php

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,47 @@ public function load(array $configs, ContainerBuilder $container)
3333
}
3434
$this->metadataFactoryLoad($container, $ormConfig);
3535

36+
$this->validateEntityManagerConfiguration($ormConfig['default_entity_manager'], $ormConfig['entity_managers']);
3637
foreach ($config['connections'] as $connectionId => $connectionConfig) {
38+
$emConfig = $this->getEntityManagerConfiguration(
39+
$connectionId,
40+
$ormConfig['default_entity_manager'],
41+
$ormConfig['entity_managers']
42+
);
3743
$connectionConfig['dispatch_events'] = $config['dispatch_events'];
38-
$this->ormLoad($container, $connectionId, $connectionConfig);
44+
$this->ormLoad($container, $connectionId, $connectionConfig, $emConfig);
3945
}
4046
}
4147

48+
/**
49+
* @param $defaultEmName
50+
* @param $emConfigs
51+
*
52+
* @throws \InvalidArgumentException
53+
*/
54+
private function validateEntityManagerConfiguration($defaultEmName, $emConfigs)
55+
{
56+
if (!isset($emConfigs[$defaultEmName])) {
57+
throw new \InvalidArgumentException('Undefined default entity manager in config "orm.entity_managers"');
58+
}
59+
}
60+
61+
/**
62+
* @param string $connectionId
63+
* @param string $defaultEmName
64+
* @param array $emConfigs
65+
*
66+
* @return array
67+
*/
68+
private function getEntityManagerConfiguration($connectionId, $defaultEmName, $emConfigs)
69+
{
70+
if (isset($emConfigs[$connectionId])) {
71+
return $emConfigs[$connectionId];
72+
}
73+
74+
return $emConfigs[$defaultEmName];
75+
}
76+
4277
protected function metadataFactoryLoad(ContainerBuilder $container, array $config)
4378
{
4479
$classMetadataFactoryDefinition = $container
@@ -53,7 +88,7 @@ protected function metadataFactoryLoad(ContainerBuilder $container, array $confi
5388
}
5489
}
5590

56-
protected function ormLoad(ContainerBuilder $container, $connectionId, array $config)
91+
protected function ormLoad(ContainerBuilder $container, $connectionId, array $config, array $emConfig)
5792
{
5893
$class = 'CassandraBundle\\Cassandra\\Connection';
5994
$definition = new Definition($class);
@@ -72,6 +107,7 @@ protected function ormLoad(ContainerBuilder $container, $connectionId, array $co
72107
->addArgument(new Reference(sprintf('cassandra.connection.%s', $connectionId)))
73108
->addArgument(new Reference('cassandra.factory.metadata'))
74109
->addArgument(new Reference('logger'))
110+
->addArgument($emConfig)
75111
->setPublic(true);
76112
}
77113

DependencyInjection/Configuration.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace CassandraBundle\DependencyInjection;
44

5+
use CassandraBundle\Cassandra\ORM\EntityManager;
56
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
67
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
78
use Symfony\Component\Config\Definition\ConfigurationInterface;
9+
use Symfony\Component\DependencyInjection\Exception\LogicException;
810

911
/**
1012
* This is the class that validates and merges configuration from your app/config files.
@@ -104,6 +106,36 @@ private function addOrmSection(ArrayNodeDefinition $rootNode)
104106
$rootNode
105107
->children()
106108
->arrayNode('orm')
109+
->beforeNormalization()
110+
->ifTrue(static function ($v) {
111+
if (!empty($v) && !class_exists(EntityManager::class)) {
112+
throw new LogicException('The cassandra/orm package is required when the cassandra.orm config is set.');
113+
}
114+
115+
return null === $v || (\is_array($v) && !\array_key_exists('entity_managers', $v) && !\array_key_exists('entity_manager', $v));
116+
})
117+
->then(static function ($v) {
118+
$v = (array) $v;
119+
// Key that should not be rewritten to the connection config
120+
$excludedKeys = [
121+
'default_entity_manager' => true,
122+
'mappings' => true,
123+
'metadata_cache_driver' => true,
124+
];
125+
$entityManager = [];
126+
foreach ($v as $key => $value) {
127+
if (isset($excludedKeys[$key])) {
128+
continue;
129+
}
130+
$entityManager[$key] = $v[$key];
131+
unset($v[$key]);
132+
}
133+
$v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
134+
$v['entity_managers'] = [$v['default_entity_manager'] => $entityManager];
135+
136+
return $v;
137+
})
138+
->end()
107139
->children()
108140
->arrayNode('mappings')
109141
->requiresAtLeastOneElement()
@@ -117,6 +149,39 @@ private function addOrmSection(ArrayNodeDefinition $rootNode)
117149
->end()
118150
->end()
119151
->scalarNode('metadata_cache_driver')->defaultNull()->end()
152+
->scalarNode('default_entity_manager')->end()
153+
->arrayNode('entity_managers')
154+
->requiresAtLeastOneElement()
155+
->useAttributeAsKey('name')
156+
->prototype('array')
157+
->treatNullLike([])
158+
->performNoDeepMerging()
159+
->children()
160+
->scalarNode('connection')->isRequired()->end()
161+
->end()
162+
->fixXmlConfig('mapping')
163+
->children()
164+
->arrayNode('mappings')
165+
->useAttributeAsKey('name')
166+
->prototype('array')
167+
->beforeNormalization()
168+
->ifString()
169+
->then(static function ($v) {
170+
return ['type' => $v];
171+
})
172+
->end()
173+
->treatNullLike([])
174+
->treatFalseLike(['mapping' => false])
175+
->performNoDeepMerging()
176+
->children()
177+
->scalarNode('mapping')->defaultValue(true)->end()
178+
->scalarNode('dir')->end()
179+
->end()
180+
->end()
181+
->end()
182+
->end()
183+
->end()
184+
->end()
120185
->end()
121186
->end()
122187
->end();

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,15 @@ Datacollector is available when the symfony profiler is enabled. The collector a
182182

183183
**NOTE :** The time reported in the data collector may not be the real execution time in case you use the async calls : `executeAsync` and `prepareAsync`
184184

185+
## EntityManager
186+
187+
EntityManager is linked to one connection, so one keyspace in Cassandra.
188+
In the bundle, you can map some Entity folders to an entityManager to then create some tables (via the SchemaManager) a specific keyspace.
189+
There is a configuration parameter under ``orm`` called ``entity_managers`` where you can describe each ``entity_manager``.
190+
The entityManager config contains the linked connection and the entity mapping directories.
191+
192+
If the linked connection can't be found, will fallback to default connection
193+
185194
## Configuration reference
186195

187196
```yaml
@@ -214,6 +223,23 @@ cassandra:
214223

215224
client_name:
216225
...
226+
orm:
227+
default_entity_manager: default
228+
entity_managers:
229+
default:
230+
connection: default
231+
mappings:
232+
User:
233+
dir: "src/UserEntity"
234+
Preference:
235+
dir: "src/PreferenceEntity"
236+
237+
client_name:
238+
connection: client_name
239+
mappings:
240+
EntityGroupOne:
241+
dir: "src/GroupOneEntity"
242+
...
217243
```
218244

219245
## Running the test

Tests/Fixtures/default-config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ cassandra:
99
protocol_version: 3
1010
user: ''
1111
password: ''
12+
orm:
13+
default_entity_manager: client_test
14+
entity_managers:
15+
client_test:
16+
connection: client_test

Tests/Fixtures/em-config.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cassandra:
2+
connections:
3+
client_one:
4+
keyspace: 'test1'
5+
hosts:
6+
- '127.0.0.1'
7+
- '127.0.0.2'
8+
- '127.0.0.3'
9+
protocol_version: 3
10+
user: ''
11+
password: ''
12+
client_two:
13+
keyspace: 'test2'
14+
hosts:
15+
- '127.0.0.1'
16+
- '127.0.0.2'
17+
- '127.0.0.3'
18+
protocol_version: 3
19+
user: ''
20+
password: ''
21+
orm:
22+
default_entity_manager: client_one
23+
entity_managers:
24+
client_one:
25+
connection: client_one
26+
mappings:
27+
TestOne:
28+
dir: 'src/Entity/TestOne'
29+
TestTwo:
30+
dir: 'src/Entity/TestTwo'

Tests/Fixtures/multiclients.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ cassandra:
2020
- 127.0.0.5
2121
user: 'usertest'
2222
password: 'passwdtest'
23+
orm:
24+
default_entity_manager: client_test
25+
entity_managers:
26+
client_test:
27+
connection: client_test

Tests/Fixtures/override-config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ cassandra:
2626
request: 15
2727
retries:
2828
sync_requests: 1
29+
orm:
30+
default_entity_manager: client_test
31+
entity_managers:
32+
client_test:
33+
connection: client_test

0 commit comments

Comments
 (0)