Completed
Pull Request — master (#168)
by
unknown
63:02
created

Configuration::getConfigTreeBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 1
eloc 7
nc 1
nop 0
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 'fuzzy':
197
                $node
198
                    ->children()
199
                        ->scalarNode('fuzziness')
200
                            ->info('The maximum edit distance.')
201
                        ->end()
202
                        ->integerNode('prefix_length')
203
                            ->info(
204
                                'The number of initial characters which will not be “fuzzified”.
205
                                This helps to reduce the number of terms which must be examined.'
206
                            )
207
                        ->end()
208
                        ->integerNode('max_expansions')
209
                            ->info('The maximum number of terms that the fuzzy query will expand to.')
210
                        ->end()
211
                    ->end();
212
                break;
213
            case 'sort':
214
                $node
215
                    ->children()
216
                        ->arrayNode('choices')
217
                            ->prototype('array')
218
                                ->beforeNormalization()
219
                                    ->always(
220
                                        function ($v) {
221
                                            if (empty($v['fields']) && !empty($v['field'])) {
222
                                                $field = ['field' => $v['field']];
223
                                                if (array_key_exists('order', $v)) {
224
                                                    $field['order'] = $v['order'];
225
                                                }
226
                                                if (array_key_exists('mode', $v)) {
227
                                                    $field['mode'] = $v['mode'];
228
                                                }
229
                                                $v['fields'][] = $field;
230
                                            }
231
232
                                            if (empty($v['label'])) {
233
                                                $v['label'] = $v['fields'][0]['field'];
234
                                            }
235
236
                                            return $v;
237
                                        }
238
                                    )
239
                                ->end()
240
                                ->addDefaultsIfNotSet()
241
                                ->children()
242
                                    ->scalarNode('label')->end()
243
                                    ->scalarNode('field')->end()
244
                                    ->scalarNode('order')->defaultValue('asc')->end()
245
                                    ->scalarNode('mode')->defaultNull()->end()
246
                                    ->scalarNode('key')->info('Custom parameter value')->end()
247
                                    ->booleanNode('default')->defaultFalse()->end()
248
                                    ->arrayNode('fields')
249
                                        ->isRequired()
250
                                        ->requiresAtLeastOneElement()
251
                                        ->prototype('array')
252
                                        ->children()
253
                                            ->scalarNode('field')->isRequired()->end()
254
                                            ->scalarNode('order')->defaultValue('asc')->end()
255
                                            ->scalarNode('mode')->defaultNull()->end()
256
                                        ->end()
257
                                    ->end()
258
                                ->end()
259
                            ->end()
260
                        ->end()
261
                    ->end();
262
                break;
263
            case 'pager':
264
                $node
265
                    ->children()
266
                        ->integerNode('count_per_page')
267
                            ->info('Item count per page')
268
                            ->defaultValue(10)
269
                        ->end()
270
                        ->integerNode('max_pages')
271
                            ->info('Max pages displayed in pager at once.')
272
                            ->defaultValue(8)
273
                        ->end()
274
                    ->end();
275
                break;
276
            case 'range':
277
            case 'date_range':
278
                $node
279
                    ->children()
280
                        ->booleanNode('inclusive')
281
                            ->info('Whether filter should match range ends.')
282
                            ->defaultFalse()
283
                        ->end()
284
                    ->end();
285
                break;
286
            case 'field_value':
287
                $node
288
                    ->children()
289
                        ->scalarNode('value')
290
                            ->info('Value which will be used for filtering.')
291
                            ->isRequired()
292
                    ->end();
293
                break;
294
            case 'document_value':
295
                $node
296
                    ->children()
297
                        ->scalarNode('document_field')
298
                            ->info('Field name from document object to pass to the filter.')
299
                            ->isRequired()
300
                    ->end();
301
                break;
302
            default:
303
                // Default config is enough.
304
                break;
305
        }
306
307
        return $filter;
308
    }
309
310
    /**
311
     * Builds relations config tree for given relation name.
312
     *
313
     * @param string $relationType
314
     *
315
     * @return ArrayNodeDefinition
316
     */
317
    private function buildRelationsTree($relationType)
318
    {
319
        $filter = new ArrayNodeDefinition($relationType);
320
321
        $filter
322
            ->validate()
323
                ->ifTrue(
324
                    function ($v) {
325
                        return empty($v['include']) && empty($v['exclude']);
326
                    }
327
                )
328
                ->thenInvalid('Relation must have "include" or "exclude" fields specified.')
329
            ->end()
330
            ->validate()
331
                ->ifTrue(
332
                    function ($v) {
333
                        return !empty($v['include']) && !empty($v['exclude']);
334
                    }
335
                )
336
                ->thenInvalid('Relation must have only "include" or "exclude" fields specified.')
337
            ->end()
338
            ->children()
339
                ->arrayNode('include')
340
                    ->beforeNormalization()
341
                        ->ifString()
342
                        ->then(
343
                            function ($v) {
344
                                return [$v];
345
                            }
346
                        )->end()
347
                    ->prototype('scalar')->end()
348
                ->end()
349
                ->arrayNode('exclude')
350
                    ->beforeNormalization()
351
                        ->ifString()
352
                        ->then(
353
                            function ($v) {
354
                                return [$v];
355
                            }
356
                        )
357
                    ->end()
358
                    ->prototype('scalar')->end()
359
                ->end()
360
            ->end();
361
362
        return $filter;
363
    }
364
}
365