Completed
Push — master ( 8386c2...ffd2be )
by Simonas
61:26
created

Configuration::addManagersSection()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 1
eloc 23
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\FilterManagerBundle\DependencyInjection;
13
14
use ONGR\ElasticsearchDSL\Aggregation\TermsAggregation;
15
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16
use Symfony\Component\Config\Definition\Builder\ParentNodeDefinitionInterface;
17
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
18
use Symfony\Component\Config\Definition\ConfigurationInterface;
19
20
/**
21
 * This is the class that validates and merges configuration from app/config files.
22
 */
23
class Configuration implements ConfigurationInterface
24
{
25
    /**
26
     * {@inheritdoc}
27
     */
28
    public function getConfigTreeBuilder()
29
    {
30
        $treeBuilder = new TreeBuilder();
31
        $rootNode = $treeBuilder->root('ongr_filter_manager');
32
33
        $this->addManagersSection($rootNode);
34
        $this->addFiltersSection($rootNode);
35
        $this->addCacheSection($rootNode);
36
37
        return $treeBuilder;
38
    }
39
40
    /**
41
     * @param ArrayNodeDefinition $rootNode
42
     */
43
    private function addManagersSection(ArrayNodeDefinition $rootNode)
44
    {
45
        $rootNode
46
            ->children()
47
                ->arrayNode('managers')
48
                    ->requiresAtLeastOneElement()
49
                    ->useAttributeAsKey('name')
50
                    ->prototype('array')
51
                        ->children()
52
                            ->scalarNode('name')
53
                                ->info('Filter manager name')
54
                            ->end()
55
                            ->arrayNode('filters')
56
                                ->info('Filter names to include in manager.')
57
                                ->prototype('scalar')->end()
58
                            ->end()
59
                            ->scalarNode('repository')
60
                                ->isRequired()
61
                                ->info('ElasticsearchBundle repository used for fetching data.')
62
                            ->end()
63
                        ->end()
64
                    ->end()
65
                ->end()
66
            ->end();
67
    }
68
69
    /**
70
     * @param ArrayNodeDefinition $rootNode
71
     */
72
    private function addFiltersSection(ArrayNodeDefinition $rootNode)
73
    {
74
        $rootNode
75
            ->children()
76
                ->arrayNode('filters')
77
                    ->validate()
78
                        ->ifTrue(
79
                            function ($v) {
80
                                $v = array_filter($v);
81
82
                                return empty($v);
83
                            }
84
                        )
85
                        ->thenInvalid('At least single filter must be configured.')
86
                    ->end()
87
                    ->children()
88
                        ->append($this->buildFilterTree('choice'))
89
                        ->append($this->buildFilterTree('multi_choice'))
90
                        ->append($this->buildFilterTree('match'))
91
                        ->append($this->buildFilterTree('fuzzy'))
92
                        ->append($this->buildFilterTree('sort'))
93
                        ->append($this->buildFilterTree('pager'))
94
                        ->append($this->buildFilterTree('range'))
95
                        ->append($this->buildFilterTree('date_range'))
96
                        ->append($this->buildFilterTree('field_value'))
97
                        ->append($this->buildFilterTree('document_value'))
98
                    ->end()
99
                ->end()
100
            ->end();
101
    }
102
103
    /**
104
     * @param ArrayNodeDefinition $rootNode
105
     */
106
    private function addCacheSection(ArrayNodeDefinition $rootNode)
107
    {
108
        $rootNode
109
            ->children()
110
                ->arrayNode('cache')
111
                    ->addDefaultsIfNotSet()
112
                    ->children()
113
                        ->scalarNode('engine')
114
                            ->info('Caching engine service name.')
115
                            ->defaultValue('es.cache_engine')
116
                        ->end()
117
                        ->arrayNode('exclude')
118
                            ->info('Array of filter names to exclude from caching.')
119
                            ->prototype('scalar')->end()
120
                        ->end()
121
                        ->integerNode('life_time')
122
                            ->info('Cached search life time.')
123
                            ->defaultValue(10800)
124
                        ->end()
125
                    ->end()
126
                ->end()
127
            ->end();
128
    }
129
    
130
    /**
131
     * Builds filter config tree for given filter name.
132
     *
133
     * @param string $filterName
134
     *
135
     * @return ArrayNodeDefinition
136
     */
137
    private function buildFilterTree($filterName)
138
    {
139
        $filter = new ArrayNodeDefinition($filterName);
140
141
        /** @var ParentNodeDefinitionInterface $node */
142
        $node = $filter
143
            ->requiresAtLeastOneElement()
144
            ->useAttributeAsKey('name')
145
            ->prototype('array')
146
                ->children()
147
                    ->scalarNode('name')->end()
148
                    ->arrayNode('relations')
149
                        ->children()
150
                            ->append($this->buildRelationsTree('search'))
151
                            ->append($this->buildRelationsTree('reset'))
152
                        ->end()
153
                    ->end()
154
                    ->scalarNode('field')
155
                        ->info('Document field name.')
156
                    ->end()
157
                    ->arrayNode('tags')
158
                        ->info('Filter tags that will be passed to view data.')
159
                        ->prototype('scalar')->end()
160
                    ->end()
161
                ->end();
162
163
        if ($filterName != 'field_value') {
164
            $node
165
                ->children()
166
                    ->scalarNode('request_field')
167
                        ->info('URL parameter name.')
168
                        ->isRequired()
169
                    ->end()
170
                ->end();
171
        }
172
173
        switch ($filterName) {
174
            case 'choice':
175
            case 'multi_choice':
176
                $node
177
                    ->children()
178
                        ->integerNode('size')
179
                            ->info('Result size to return.')
180
                        ->end()
181
                        ->arrayNode('sort')
182
                        ->children()
183
                            ->enumNode('type')
184
                                ->values(['_term', '_count'])
185
                                ->defaultValue('_term')
186
                            ->end()
187
                            ->enumNode('order')
188
                                ->values(['asc', 'desc'])
189
                                ->defaultValue('asc')
190
                            ->end()
191
                            ->arrayNode('priorities')->prototype('scalar')->end()
192
                            ->end()
193
                        ->end()
194
                    ->end();
195
                break;
196
            case 'match':
197
                $node
198
                    ->children()
199
                        ->scalarNode('operator')
200
                            ->info('The operator flag.')
201
                        ->end()
202
                        ->scalarNode('fuzziness')
203
                            ->info('The maximum edit distance.')
204
                        ->end()
205
                    ->end();
206
                break;
207
            case 'fuzzy':
208
                $node
209
                    ->children()
210
                        ->scalarNode('fuzziness')
211
                            ->info('The maximum edit distance.')
212
                        ->end()
213
                        ->integerNode('prefix_length')
214
                            ->info(
215
                                'The number of initial characters which will not be “fuzzified”.
216
                                This helps to reduce the number of terms which must be examined.'
217
                            )
218
                        ->end()
219
                        ->integerNode('max_expansions')
220
                            ->info('The maximum number of terms that the fuzzy query will expand to.')
221
                        ->end()
222
                    ->end();
223
                break;
224
            case 'sort':
225
                $node
226
                    ->children()
227
                        ->arrayNode('choices')
228
                            ->prototype('array')
229
                                ->beforeNormalization()
230
                                    ->always(
231
                                        function ($v) {
232
                                            if (empty($v['fields']) && !empty($v['field'])) {
233
                                                $field = ['field' => $v['field']];
234
                                                if (array_key_exists('order', $v)) {
235
                                                    $field['order'] = $v['order'];
236
                                                }
237
                                                if (array_key_exists('mode', $v)) {
238
                                                    $field['mode'] = $v['mode'];
239
                                                }
240
                                                $v['fields'][] = $field;
241
                                            }
242
243
                                            if (empty($v['label'])) {
244
                                                $v['label'] = $v['fields'][0]['field'];
245
                                            }
246
247
                                            return $v;
248
                                        }
249
                                    )
250
                                ->end()
251
                                ->addDefaultsIfNotSet()
252
                                ->children()
253
                                    ->scalarNode('label')->end()
254
                                    ->scalarNode('field')->end()
255
                                    ->scalarNode('order')->defaultValue('asc')->end()
256
                                    ->scalarNode('mode')->defaultNull()->end()
257
                                    ->scalarNode('key')->info('Custom parameter value')->end()
258
                                    ->booleanNode('default')->defaultFalse()->end()
259
                                    ->arrayNode('fields')
260
                                        ->isRequired()
261
                                        ->requiresAtLeastOneElement()
262
                                        ->prototype('array')
263
                                        ->children()
264
                                            ->scalarNode('field')->isRequired()->end()
265
                                            ->scalarNode('order')->defaultValue('asc')->end()
266
                                            ->scalarNode('mode')->defaultNull()->end()
267
                                        ->end()
268
                                    ->end()
269
                                ->end()
270
                            ->end()
271
                        ->end()
272
                    ->end();
273
                break;
274
            case 'pager':
275
                $node
276
                    ->children()
277
                        ->integerNode('count_per_page')
278
                            ->info('Item count per page')
279
                            ->defaultValue(10)
280
                        ->end()
281
                        ->integerNode('max_pages')
282
                            ->info('Max pages displayed in pager at once.')
283
                            ->defaultValue(8)
284
                        ->end()
285
                    ->end();
286
                break;
287
            case 'range':
288
            case 'date_range':
289
                $node
290
                    ->children()
291
                        ->booleanNode('inclusive')
292
                            ->info('Whether filter should match range ends.')
293
                            ->defaultFalse()
294
                        ->end()
295
                    ->end();
296
                break;
297
            case 'field_value':
298
                $node
299
                    ->children()
300
                        ->scalarNode('value')
301
                            ->info('Value which will be used for filtering.')
302
                            ->isRequired()
303
                    ->end();
304
                break;
305
            case 'document_value':
306
                $node
307
                    ->children()
308
                        ->scalarNode('document_field')
309
                            ->info('Field name from document object to pass to the filter.')
310
                            ->isRequired()
311
                    ->end();
312
                break;
313
            default:
314
                // Default config is enough.
315
                break;
316
        }
317
318
        return $filter;
319
    }
320
321
    /**
322
     * Builds relations config tree for given relation name.
323
     *
324
     * @param string $relationType
325
     *
326
     * @return ArrayNodeDefinition
327
     */
328
    private function buildRelationsTree($relationType)
329
    {
330
        $filter = new ArrayNodeDefinition($relationType);
331
332
        $filter
333
            ->validate()
334
                ->ifTrue(
335
                    function ($v) {
336
                        return empty($v['include']) && empty($v['exclude']);
337
                    }
338
                )
339
                ->thenInvalid('Relation must have "include" or "exclude" fields specified.')
340
            ->end()
341
            ->validate()
342
                ->ifTrue(
343
                    function ($v) {
344
                        return !empty($v['include']) && !empty($v['exclude']);
345
                    }
346
                )
347
                ->thenInvalid('Relation must have only "include" or "exclude" fields specified.')
348
            ->end()
349
            ->children()
350
                ->arrayNode('include')
351
                    ->beforeNormalization()
352
                        ->ifString()
353
                        ->then(
354
                            function ($v) {
355
                                return [$v];
356
                            }
357
                        )->end()
358
                    ->prototype('scalar')->end()
359
                ->end()
360
                ->arrayNode('exclude')
361
                    ->beforeNormalization()
362
                        ->ifString()
363
                        ->then(
364
                            function ($v) {
365
                                return [$v];
366
                            }
367
                        )
368
                    ->end()
369
                    ->prototype('scalar')->end()
370
                ->end()
371
            ->end();
372
373
        return $filter;
374
    }
375
}
376