Completed
Pull Request — master (#98)
by Benjamin
01:38
created

Configuration::addTranslationExtractorSection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 9.456
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the "elao/enum" package.
5
 *
6
 * Copyright (C) Elao
7
 *
8
 * @author Elao <[email protected]>
9
 */
10
11
namespace Elao\Enum\Bridge\Symfony\Bundle\DependencyInjection;
12
13
use Elao\Enum\EnumInterface;
14
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
use Symfony\Component\Serializer\SerializerInterface;
18
19
class Configuration implements ConfigurationInterface
20
{
21
    public function getConfigTreeBuilder()
22
    {
23
        $treeBuilder = new TreeBuilder('elao_enum');
24
        $rootNode = method_exists(TreeBuilder::class, 'getRootNode') ? $treeBuilder->getRootNode() : $treeBuilder->root('elao_enum');
0 ignored issues
show
Bug introduced by
The method root() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
25
26
        $rootNode->children()
27
            ->arrayNode('argument_value_resolver')->canBeDisabled()->end()
28
            ->arrayNode('serializer')
29
                ->{interface_exists(SerializerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
30
            ->end()
31
        ->end();
32
33
        $this->addDoctrineSection($rootNode);
34
        $this->addTranslationExtractorSection($rootNode);
35
        $this->addJsEnumsSection($rootNode);
36
37
        return $treeBuilder;
38
    }
39
40
    private function addDoctrineSection(ArrayNodeDefinition $rootNode)
41
    {
42
        $rootNode->children()
43
            ->arrayNode('doctrine')
44
            ->addDefaultsIfNotSet()
45
            ->fixXmlConfig('type')
46
            ->children()
47
                ->booleanNode('enum_sql_declaration')
48
                    ->defaultValue(false)
49
                    ->info('If true, by default for string enumerations, generate DBAL types with an ENUM SQL declaration with enum values instead of a VARCHAR (Your platform must support it)')
50
                ->end()
51
                ->arrayNode('types')
52
                    ->beforeNormalization()
53
                        ->always(function ($values) {
54
                            $legacyFormat = false;
55
                            foreach ($values as $name => $config) {
56
                                if (class_exists($name)) {
57
                                    $legacyFormat = true;
58
                                    // need to flip
59
                                    break;
60
                                }
61
                            }
62
                            $newValues = [];
63
64
                            if ($legacyFormat) {
65
                                foreach ($values as $name => $value) {
66
                                    if (\is_string($value)) {
67
                                        $newValues[$value] = $name;
68
                                    } else {
69
                                        $newValues[$value['name']] = $value + ['class' => $name];
70
                                    }
71
                                }
72
                            } else {
73
                                $newValues = $values;
74
                            }
75
76
                            return $newValues;
77
                        })
78
                    ->end()
79
                    ->useAttributeAsKey('name')
80
                    ->arrayPrototype()
81
                    ->beforeNormalization()
82
                        ->ifString()->then(static function (string $v): array { return ['class' => $v]; })
83
                    ->end()
84
                    ->children()
85
                        ->scalarNode('class')
86
                            ->cannotBeEmpty()
87
                            ->validate()
88
                                ->ifTrue(static function (string $class): bool {return !is_a($class, EnumInterface::class, true); })
89
                                ->thenInvalid(sprintf('Invalid class. Expected instance of "%s"', EnumInterface::class))
90
                            ->end()
91
                        ->end()
92
                        ->enumNode('type')
93
                            ->values(['enum', 'string', 'int', 'collection'])
94
                            ->info(<<<TXT
95
Which column definition to use and the way the enumeration values are stored in the database:
96
- string: VARCHAR
97
- enum: ENUM(...values) (Your platform must support it)
98
- int: INT
99
- collection: JSON
100
101
Default is either "string" or "enum", controlled by the `elao_enum.doctrine.enum_sql_declaration` option.
102
Default for flagged enums is "int".
103
TXT
104
                            )
105
                            ->cannotBeEmpty()
106
                            ->defaultValue(null)
107
                        ->end()
108
                    ->end()
109
                ->end()
110
            ->end()
111
        ->end();
112
    }
113
114
    private function addTranslationExtractorSection(ArrayNodeDefinition $rootNode)
115
    {
116
        $rootNode->children()
117
            ->arrayNode('translation_extractor')
118
            ->canBeEnabled()
119
            ->fixXmlConfig('path')
120
            ->children()
121
                ->arrayNode('paths')
122
                    ->example(['App\Enum' => '%kernel.project_dir%/src/Enum'])
123
                    ->useAttributeAsKey('namespace')
124
                    ->scalarPrototype()
125
                    ->end()
126
                ->end()
127
                ->scalarNode('domain')
128
                    ->defaultValue('messages')
129
                    ->cannotBeEmpty()
130
                ->end()
131
                ->scalarNode('filename_pattern')
132
                    ->example('*Enum.php')
133
                    ->defaultValue('*.php')
134
                    ->cannotBeEmpty()
135
                ->end()
136
                ->arrayNode('ignore')
137
                    ->example(['%kernel.project_dir%/src/Enum/Other/*'])
138
                    ->scalarPrototype()->cannotBeEmpty()->end()
139
                ->end()
140
            ->end()
141
        ->end();
142
    }
143
144
    private function addJsEnumsSection(ArrayNodeDefinition $rootNode)
145
    {
146
        $rootNode->children()
147
            ->arrayNode('js')
148
                ->addDefaultsIfNotSet()
149
                ->fixXmlConfig('path')
150
                ->children()
151
                    ->scalarNode('base_dir')
152
                        ->info('A prefixed dir used for relative paths supplied for each of the generated enums and library path')
153
                        ->example('%kernel.project_dir%/assets/js/modules')
154
                        ->defaultNull()
155
                    ->end()
156
                    ->scalarNode('lib_path')
157
                        ->info('The path of the file were to place the javascript library sources used by the dumped enums.')
158
                        ->example('%kernel.project_dir%/assets/js/lib/enum.js')
159
                        ->defaultNull()
160
                    ->end()
161
                    ->arrayNode('paths')
162
                        ->defaultValue([])
163
                        ->info('Path where to generate the javascript enums per enum class')
164
                        ->example(['App\Enum\SimpleEnum' => '"common/SimpleEnum.js"'])
165
                        ->useAttributeAsKey('class')
166
                        ->validate()
167
                            ->ifTrue(static function (array $v): bool {return self::hasNonEnumKeys($v); })
168
                            ->then(static function (array $v) { self::throwsNonEnumKeysException($v); })
169
                        ->end()
170
                        ->scalarPrototype()->end()
171
                    ->end()
172
                ->end()
173
            ->end()
174
        ->end();
175
    }
176
177
    private static function hasNonEnumKeys(array $values): bool
178
    {
179
        $classes = array_column($values, 'class');
180
        foreach ($classes as $class) {
181
            if (!is_a($class, EnumInterface::class, true)) {
182
                return true;
183
            }
184
        }
185
186
        return false;
187
    }
188
189
    private static function throwsNonEnumKeysException(array $values)
190
    {
191
        $classes = array_column($values, 'class');
192
        $invalids = [];
193
        foreach ($classes as $class) {
194
            if (!is_a($class, EnumInterface::class, true)) {
195
                $invalids[] = $class;
196
            }
197
        }
198
199
        throw new \InvalidArgumentException(sprintf(
200
            'Invalid classes %s. Expected instances of "%s"',
201
            json_encode($invalids),
202
            EnumInterface::class
203
        ));
204
    }
205
}
206