1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace As3\Bundle\ModlrBundle\DependencyInjection; |
4
|
|
|
|
5
|
|
|
use Symfony\Component\Config\Definition\Builder\TreeBuilder; |
6
|
|
|
use Symfony\Component\Config\Definition\ConfigurationInterface; |
7
|
|
|
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Validates and merges configuration for Modlr. |
11
|
|
|
* |
12
|
|
|
* @author Jacob Bare <[email protected]> |
13
|
|
|
*/ |
14
|
|
|
class Configuration implements ConfigurationInterface |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* {@inheritDoc} |
18
|
|
|
*/ |
19
|
|
|
public function getConfigTreeBuilder() |
20
|
|
|
{ |
21
|
|
|
$treeBuilder = new TreeBuilder(); |
22
|
|
|
$treeBuilder->root('as3_modlr') |
23
|
|
|
->children() |
24
|
|
|
->append($this->getAdapterNode()) |
25
|
|
|
->append($this->getMetadataNode()) |
26
|
|
|
->append($this->getPersistersNode()) |
27
|
|
|
->append($this->getRestNode()) |
28
|
|
|
->append($this->getSearchClientsNode()) |
29
|
|
|
->end() |
30
|
|
|
; |
31
|
|
|
return $treeBuilder; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Formats the root REST endpoint. |
36
|
|
|
* |
37
|
|
|
* @param string $endpoint |
38
|
|
|
* @return string |
39
|
|
|
*/ |
40
|
|
|
private function formatRestEndpoint($endpoint) |
41
|
|
|
{ |
42
|
|
|
return sprintf('%s', trim($endpoint, '/')); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Gets the api adapter configuration node. |
47
|
|
|
* |
48
|
|
|
* @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition |
49
|
|
|
*/ |
50
|
|
|
private function getAdapterNode() |
51
|
|
|
{ |
52
|
|
|
$treeBuilder = new TreeBuilder(); |
53
|
|
|
$node = $treeBuilder->root('adapter'); |
54
|
|
|
return $node |
55
|
|
|
->isRequired() |
56
|
|
|
->addDefaultsIfNotSet() |
57
|
|
|
->children() |
58
|
|
|
->scalarNode('type')->end() |
59
|
|
|
->scalarNode('service')->end() |
60
|
|
|
->end() |
61
|
|
|
->validate() |
62
|
|
|
->always(function($v) { |
63
|
|
|
$this->validateAdapter($v); |
64
|
|
|
return $v; |
65
|
|
|
}) |
66
|
|
|
->end() |
67
|
|
|
; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Gets the metadata configuration node. |
72
|
|
|
* |
73
|
|
|
* @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition |
74
|
|
|
*/ |
75
|
|
|
private function getMetadataNode() |
76
|
|
|
{ |
77
|
|
|
$treeBuilder = new TreeBuilder(); |
78
|
|
|
$node = $treeBuilder->root('metadata'); |
79
|
|
|
return $node |
80
|
|
|
->addDefaultsIfNotSet() |
81
|
|
|
->children() |
82
|
|
|
->arrayNode('drivers') |
83
|
|
|
->defaultValue(['default' => ['type' => 'yml']]) |
84
|
|
|
->useAttributeAsKey('name') |
85
|
|
|
->prototype('array') |
86
|
|
|
->performNoDeepMerging() |
87
|
|
|
->children() |
88
|
|
|
|
89
|
|
|
->enumNode('type')->defaultValue('file') |
90
|
|
|
->values(['yml', null]) |
91
|
|
|
->end() |
92
|
|
|
->scalarNode('service')->cannotBeEmpty()->end() |
93
|
|
|
|
94
|
|
|
->arrayNode('parameters') |
95
|
|
|
->prototype('variable')->end() |
96
|
|
|
->end() |
97
|
|
|
|
98
|
|
|
->end() |
99
|
|
|
->end() |
100
|
|
|
->validate() |
101
|
|
|
->always(function($v) { |
102
|
|
|
$this->validateMetadataDrivers($v); |
103
|
|
|
return $v; |
104
|
|
|
}) |
105
|
|
|
->end() |
106
|
|
|
->end() |
107
|
|
|
|
108
|
|
|
->arrayNode('cache') |
109
|
|
|
->canBeDisabled() |
110
|
|
|
->children() |
111
|
|
|
|
112
|
|
|
->enumNode('type')->defaultValue('file') |
113
|
|
|
->values(['file', 'binary_file', 'redis', null]) |
114
|
|
|
->end() |
115
|
|
|
->scalarNode('service')->cannotBeEmpty()->end() |
116
|
|
|
|
117
|
|
|
->arrayNode('parameters') |
118
|
|
|
->prototype('variable')->end() |
119
|
|
|
->end() |
120
|
|
|
|
121
|
|
|
->end() |
122
|
|
|
|
123
|
|
|
->validate() |
124
|
|
|
->always(function($v) { |
125
|
|
|
$this->validateMetadataCache($v); |
126
|
|
|
return $v; |
127
|
|
|
}) |
128
|
|
|
->end() |
129
|
|
|
->end() |
130
|
|
|
->end() |
131
|
|
|
; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Gets the persisters configuration node. |
136
|
|
|
* |
137
|
|
|
* @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition |
138
|
|
|
*/ |
139
|
|
View Code Duplication |
private function getPersistersNode() |
|
|
|
|
140
|
|
|
{ |
141
|
|
|
$treeBuilder = new TreeBuilder(); |
142
|
|
|
$node = $treeBuilder->root('persisters'); |
143
|
|
|
return $node |
144
|
|
|
->isRequired() |
145
|
|
|
->useAttributeAsKey('name') |
146
|
|
|
->prototype('array') |
147
|
|
|
->children() |
148
|
|
|
|
149
|
|
|
->scalarNode('type')->cannotBeEmpty()->end() |
150
|
|
|
->scalarNode('service')->cannotBeEmpty()->end() |
151
|
|
|
|
152
|
|
|
->arrayNode('parameters') |
153
|
|
|
->prototype('variable')->end() |
154
|
|
|
->end() |
155
|
|
|
->end() |
156
|
|
|
->end() |
157
|
|
|
->validate() |
158
|
|
|
->always(function($v) { |
159
|
|
|
$this->validatePersisters($v); |
160
|
|
|
return $v; |
161
|
|
|
}) |
162
|
|
|
->end() |
163
|
|
|
; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Gets the rest configuration node. |
168
|
|
|
* |
169
|
|
|
* @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition |
170
|
|
|
*/ |
171
|
|
|
private function getRestNode() |
172
|
|
|
{ |
173
|
|
|
$treeBuilder = new TreeBuilder(); |
174
|
|
|
$node = $treeBuilder->root('rest'); |
175
|
|
|
return $node |
176
|
|
|
->addDefaultsIfNotSet() |
177
|
|
|
->children() |
178
|
|
|
->scalarNode('root_endpoint')->isRequired()->cannotBeEmpty()->defaultValue('modlr/api') |
179
|
|
|
->validate() |
180
|
|
|
->always(function($v) { |
181
|
|
|
$v = $this->formatRestEndpoint($v); |
182
|
|
|
return $v; |
183
|
|
|
}) |
184
|
|
|
->end() |
185
|
|
|
->end() |
186
|
|
|
->end() |
187
|
|
|
|
188
|
|
|
; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Gets the search clients configuration node. |
193
|
|
|
* |
194
|
|
|
* @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition |
195
|
|
|
*/ |
196
|
|
View Code Duplication |
private function getSearchClientsNode() |
|
|
|
|
197
|
|
|
{ |
198
|
|
|
$treeBuilder = new TreeBuilder(); |
199
|
|
|
$node = $treeBuilder->root('search_clients'); |
200
|
|
|
return $node |
201
|
|
|
->isRequired() |
202
|
|
|
->useAttributeAsKey('name') |
203
|
|
|
->prototype('array') |
204
|
|
|
->children() |
205
|
|
|
|
206
|
|
|
->scalarNode('type')->cannotBeEmpty()->end() |
207
|
|
|
->scalarNode('service')->cannotBeEmpty()->end() |
208
|
|
|
|
209
|
|
|
->arrayNode('parameters') |
210
|
|
|
->prototype('variable')->end() |
211
|
|
|
->end() |
212
|
|
|
->end() |
213
|
|
|
->end() |
214
|
|
|
->validate() |
215
|
|
|
->always(function($v) { |
216
|
|
|
$this->validateSearchClients($v); |
217
|
|
|
return $v; |
218
|
|
|
}) |
219
|
|
|
->end() |
220
|
|
|
; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Validates the api adapter config. |
225
|
|
|
* |
226
|
|
|
* @param array $adapter |
227
|
|
|
* @return self |
228
|
|
|
* @throws InvalidConfigurationException |
229
|
|
|
*/ |
230
|
|
|
private function validateAdapter(array $adapter) |
231
|
|
|
{ |
232
|
|
|
$this->validateTypeAndService($adapter, 'as3_modlr.adapter'); |
233
|
|
|
|
234
|
|
|
if (isset($adapter['type'])) { |
235
|
|
|
if ('jsonapiorg' === $adapter['type']) { |
236
|
|
|
if (false === class_exists('As3\Modlr\Api\JsonApiOrg\Adapter')) { |
237
|
|
|
throw new InvalidConfigurationException('The jsonapi.org adapter class was not found for "as3_modlr.adapter.type" - was the library installed?'); |
238
|
|
|
} |
239
|
|
|
} else { |
240
|
|
|
throw new InvalidConfigurationException('An unrecognized adapter type was set for "as3_modlr.adapter.type"'); |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
return $this; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Validates the metadata cache config. |
248
|
|
|
* |
249
|
|
|
* @param array $config |
250
|
|
|
* @return self |
251
|
|
|
* @throws InvalidConfigurationException |
252
|
|
|
*/ |
253
|
|
|
private function validateMetadataCache(array $config) |
254
|
|
|
{ |
255
|
|
|
if (false === $config['enabled']) { |
256
|
|
|
return $this; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$this->validateTypeAndService($config, 'as3_modlr.metadata.cache'); |
260
|
|
|
if (isset($config['type']) && 'redis' === $config['type'] && !isset($config['parameters']['handler'])) { |
261
|
|
|
throw new InvalidConfigurationException('A Redis handler service name must be defined for "as3_modlr.metadata.cache.parameters.handler"'); |
262
|
|
|
} |
263
|
|
|
return $this; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Validates the metadata drivers config. |
268
|
|
|
* |
269
|
|
|
* @param array $drivers |
270
|
|
|
* @return self |
271
|
|
|
* @throws InvalidConfigurationException |
272
|
|
|
*/ |
273
|
|
|
private function validateMetadataDrivers(array $drivers) |
274
|
|
|
{ |
275
|
|
|
foreach ($drivers as $name => $config) { |
276
|
|
|
$this->validateTypeAndService($config, sprintf('as3_modlr.metadata.drivers.%s', $name)); |
277
|
|
|
} |
278
|
|
|
return $this; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Validates the persisters config. |
283
|
|
|
* |
284
|
|
|
* @param array $persisters |
285
|
|
|
* @return self |
286
|
|
|
* @throws InvalidConfigurationException |
287
|
|
|
*/ |
288
|
|
|
private function validatePersisters(array $persisters) |
289
|
|
|
{ |
290
|
|
|
foreach ($persisters as $name => $config) { |
291
|
|
|
$this->validateTypeAndService($config, sprintf('as3_modlr.persisters.%s', $name)); |
292
|
|
|
if (isset($config['type'])) { |
293
|
|
|
if ('mongodb' === $config['type']) { |
294
|
|
View Code Duplication |
if (false === class_exists(Utility::getLibraryClass('Persister\MongoDb\Persister'))) { |
|
|
|
|
295
|
|
|
throw new InvalidConfigurationException(sprintf('The MongoDB persister library class was not found for "as3_modlr.persisters.%s.type" - was the library installed?', $name)); |
296
|
|
|
} |
297
|
|
|
if (!isset($config['parameters']['host'])) { |
298
|
|
|
throw new InvalidConfigurationException(sprintf('The MongoDB persister requires a value for "as3_modlr.persisters.%s.parameters.host"', $name)); |
299
|
|
|
} |
300
|
|
|
} else { |
301
|
|
|
throw new InvalidConfigurationException(sprintf('An unrecognized persister type was set for "as3_modlr.persisters.%s.type"', $name)); |
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
} |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Validates the search clients config. |
309
|
|
|
* |
310
|
|
|
* @param array $clients |
311
|
|
|
* @return self |
312
|
|
|
* @throws InvalidConfigurationException |
313
|
|
|
*/ |
314
|
|
|
private function validateSearchClients(array $clients) |
315
|
|
|
{ |
316
|
|
|
foreach ($clients as $name => $config) { |
317
|
|
|
$this->validateTypeAndService($config, sprintf('as3_modlr.search_clients.%s', $name)); |
318
|
|
|
if (isset($config['type'])) { |
319
|
|
|
if ('elastic' === $config['type']) { |
320
|
|
View Code Duplication |
if (false === class_exists(Utility::getLibraryClass('Search\Elastic\Client'))) { |
|
|
|
|
321
|
|
|
throw new InvalidConfigurationException(sprintf('The Elastic persister library class was not found for "as3_modlr.search_clients.%s.type" - was the library installed?', $name)); |
322
|
|
|
} |
323
|
|
|
} else { |
324
|
|
|
throw new InvalidConfigurationException(sprintf('An unrecognized search type was set for "as3_modlr.search_clients.%s.type"', $name)); |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
private function validateTypeAndService(array $config, $path) |
331
|
|
|
{ |
332
|
|
View Code Duplication |
if (!isset($config['type']) && !isset($config['service'])) { |
|
|
|
|
333
|
|
|
throw new InvalidConfigurationException(sprintf('You must set one of "type" or "service" for "%s"', $path)); |
334
|
|
|
} |
335
|
|
View Code Duplication |
if (isset($config['type']) && isset($config['service'])) { |
|
|
|
|
336
|
|
|
throw new InvalidConfigurationException(sprintf('You cannot set both "type" and "service" for "%s" - please choose one.', $path)); |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.