Completed
Pull Request — master (#89)
by Maxime
11:43
created

Configuration::getConfigTreeBuilder()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 3
nc 4
nop 0
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
                    ->validate()
53
                        ->ifTrue(static function (array $v): bool {return self::hasNonEnumKeys($v); })
54
                        ->then(static function (array $v) { self::throwsNonEnumKeysException($v); })
55
                    ->end()
56
                    ->useAttributeAsKey('class')
57
                    ->arrayPrototype()
58
                    ->beforeNormalization()
59
                        ->ifString()->then(static function (string $v): array { return ['name' => $v]; })
60
                    ->end()
61
                    ->children()
62
                        ->scalarNode('name')->cannotBeEmpty()->end()
63
                        ->enumNode('type')
64
                            ->values(['enum', 'string', 'int'])
65
                            ->info(<<<TXT
66
Which column definition to use and the way the enumeration values are stored in the database:
67
- string: VARCHAR
68
- enum: ENUM(...values) (Your platform must support it)
69
- int: INT
70
71
Default is either "string" or "enum", controlled by the `elao_enum.doctrine.enum_sql_declaration` option.
72
Default for flagged enums is "int".
73
TXT
74
                            )
75
                            ->cannotBeEmpty()
76
                            ->defaultValue(null)
77
                        ->end()
78
                    ->end()
79
                ->end()
80
            ->end()
81
        ->end();
82
    }
83
84
    private function addTranslationExtractorSection(ArrayNodeDefinition $rootNode)
85
    {
86
        $rootNode->children()
87
            ->arrayNode('translation_extractor')
88
            ->canBeEnabled()
89
            ->fixXmlConfig('path')
90
            ->children()
91
                ->arrayNode('paths')
92
                    ->example(['App\Enum' => '%kernel.project_dir%/src/Enum'])
93
                    ->useAttributeAsKey('namespace')
94
                    ->scalarPrototype()
95
                    ->end()
96
                ->end()
97
                ->scalarNode('domain')
98
                    ->defaultValue('messages')
99
                    ->cannotBeEmpty()
100
                ->end()
101
                ->scalarNode('filename_pattern')
102
                    ->example('*Enum.php')
103
                    ->defaultValue('*.php')
104
                    ->cannotBeEmpty()
105
                ->end()
106
                ->arrayNode('ignore')
107
                    ->example(['%kernel.project_dir%/src/Enum/Other/*'])
108
                    ->scalarPrototype()->cannotBeEmpty()->end()
109
                ->end()
110
            ->end()
111
        ->end();
112
    }
113
114
    private function addJsEnumsSection(ArrayNodeDefinition $rootNode)
115
    {
116
        $rootNode->children()
117
            ->arrayNode('js')
118
                ->addDefaultsIfNotSet()
119
                ->fixXmlConfig('path')
120
                ->children()
121
                    ->scalarNode('base_dir')
122
                        ->info('A prefixed dir used for relative paths supplied for each of the generated enums and library path')
123
                        ->example('%kernel.project_dir%/assets/js/modules')
124
                        ->defaultNull()
125
                    ->end()
126
                    ->scalarNode('lib_path')
127
                        ->info('The path of the file were to place the javascript library sources used by the dumped enums.')
128
                        ->example('%kernel.project_dir%/assets/js/lib/enum.js')
129
                        ->defaultNull()
130
                    ->end()
131
                    ->arrayNode('paths')
132
                        ->defaultValue([])
133
                        ->info('Path where to generate the javascript enums per enum class')
134
                        ->example(['App\Enum\SimpleEnum' => '"common/SimpleEnum.js"'])
135
                        ->useAttributeAsKey('class')
136
                        ->validate()
137
                            ->ifTrue(static function (array $v): bool {return self::hasNonEnumKeys($v); })
138
                            ->then(static function (array $v) { self::throwsNonEnumKeysException($v); })
139
                        ->end()
140
                        ->scalarPrototype()->end()
141
                    ->end()
142
                ->end()
143
            ->end()
144
        ->end();
145
    }
146
147
    private static function hasNonEnumKeys(array $values): bool
148
    {
149
        $classes = array_keys($values);
150
        foreach ($classes as $class) {
151
            if (!is_a($class, EnumInterface::class, true)) {
152
                return true;
153
            }
154
        }
155
156
        return false;
157
    }
158
159
    private static function throwsNonEnumKeysException(array $values)
160
    {
161
        $classes = array_keys($values);
162
        $invalids = [];
163
        foreach ($classes as $class) {
164
            if (!is_a($class, EnumInterface::class, true)) {
165
                $invalids[] = $class;
166
            }
167
        }
168
169
        throw new \InvalidArgumentException(sprintf(
170
            'Invalid classes %s. Expected instances of "%s"',
171
            json_encode($invalids),
172
            EnumInterface::class
173
        ));
174
    }
175
}
176