Completed
Pull Request — master (#116)
by Arnaud
05:17
created

FieldFactory   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 198
rs 10
c 0
b 0
f 0
wmc 17

6 Methods

Rating   Name   Duplication   Size   Complexity  
A instanciateField() 0 17 4
A create() 0 19 2
A getFieldClass() 0 7 2
A __construct() 0 13 1
A createFields() 0 9 2
A resolveConfiguration() 0 38 6
1
<?php
2
3
namespace LAG\AdminBundle\Factory;
4
5
use Exception;
6
use LAG\AdminBundle\Configuration\ActionConfiguration;
7
use LAG\AdminBundle\Configuration\ApplicationConfiguration;
8
use LAG\AdminBundle\Configuration\ApplicationConfigurationStorage;
9
use LAG\AdminBundle\Configuration\FieldConfiguration;
10
use LAG\AdminBundle\Field\FieldInterface;
11
use LAG\AdminBundle\Field\TwigAwareFieldInterface;
12
use LAG\AdminBundle\Field\TranslatorAwareFieldInterface;
13
use Symfony\Component\OptionsResolver\OptionsResolver;
14
use Symfony\Contracts\Translation\TranslatorInterface;
15
use Twig\Environment;
16
17
/**
18
 * Field factory. Instances fields.
19
 */
20
class FieldFactory
21
{
22
    /**
23
     * Application configuration.
24
     *
25
     * @var ApplicationConfiguration
26
     */
27
    protected $configuration;
28
29
    /**
30
     * Field class mapping array, indexed by field type.
31
     *
32
     * @var array
33
     */
34
    protected $fieldsMapping = [];
35
36
    /**
37
     * Translator for field values.
38
     *
39
     * @var TranslatorInterface
40
     */
41
    protected $translator;
42
43
    /**
44
     * Twig engine.
45
     *
46
     * @var Environment
47
     */
48
    protected $twig;
49
50
    /**
51
     * @var ConfigurationFactory
52
     */
53
    protected $configurationFactory;
54
55
    /**
56
     * FieldFactory constructor.
57
     *
58
     * @param ApplicationConfigurationStorage $applicationConfigurationStorage
59
     * @param ConfigurationFactory            $configurationFactory
60
     * @param TranslatorInterface             $translator
61
     * @param Environment                $twig
62
     */
63
    public function __construct(
64
        ApplicationConfigurationStorage $applicationConfigurationStorage,
65
        ConfigurationFactory $configurationFactory,
66
        TranslatorInterface $translator,
67
        Environment $twig
68
    ) {
69
        $this->configuration = $applicationConfigurationStorage->getConfiguration();
70
        $this->fieldsMapping = $this
71
            ->configuration
72
            ->getParameter('fields_mapping'); // shortcut to the fields mapping array
73
        $this->translator = $translator;
74
        $this->twig = $twig;
75
        $this->configurationFactory = $configurationFactory;
76
    }
77
78
    /**
79
     * @param ActionConfiguration $configuration
80
     *
81
     * @return array
82
     */
83
    public function createFields(ActionConfiguration $configuration): array
84
    {
85
        $fields = [];
86
87
        foreach ($configuration->getParameter('fields') as $field => $fieldConfiguration) {
88
            $fields[] = $this->create($field, $fieldConfiguration, $configuration);
89
        }
90
91
        return $fields;
92
    }
93
94
    /**
95
     * Create a new field instance according to the given configuration.
96
     *
97
     * @param string              $name
98
     * @param array               $configuration
99
     * @param ActionConfiguration $actionConfiguration
100
     *
101
     * @return FieldInterface
102
     *
103
     * @throws Exception
104
     */
105
    public function create(string $name, array $configuration, ActionConfiguration $actionConfiguration): FieldInterface
106
    {
107
        $resolver = new OptionsResolver();
108
        $configuration = $this->resolveConfiguration($configuration, $actionConfiguration);
109
110
        $field = $this->instanciateField($name, $configuration['type']);
111
        $field->configureOptions($resolver, $actionConfiguration);
112
113
        try {
114
            $field->setOptions($resolver->resolve($configuration['options']));
115
        } catch (Exception $exception) {
116
            throw new \LAG\AdminBundle\Exception\Exception(
117
                'An error has occurred when resolving the options for the field "'.$name.'": '.$exception->getMessage(),
118
                $exception->getCode(),
119
                $exception
120
            );
121
        }
122
123
        return $field;
124
    }
125
126
    /**
127
     * Return field class according to the field type. If the type is not present in the field mapping array, an
128
     * exception will be thrown.
129
     *
130
     * @param string $type
131
     *
132
     * @return string
133
     *
134
     * @throws Exception
135
     */
136
    private function getFieldClass(string $type): string
137
    {
138
        if (!array_key_exists($type, $this->fieldsMapping)) {
139
            throw new Exception("Field type {$type} not found in field mapping. Check your configuration");
140
        }
141
142
        return $this->fieldsMapping[$type];
143
    }
144
145
    /**
146
     * @param string $name
147
     * @param string $type
148
     *
149
     * @return FieldInterface
150
     *
151
     * @throws Exception
152
     */
153
    private function instanciateField(string $name, string $type): FieldInterface
154
    {
155
        $fieldClass = $this->getFieldClass($type);
156
        $field = new $fieldClass($name);
157
158
        if (!$field instanceof FieldInterface) {
159
            throw new Exception("Field class {$fieldClass} must implements ".FieldInterface::class);
160
        }
161
162
        if ($field instanceof TranslatorAwareFieldInterface) {
163
            $field->setTranslator($this->translator);
164
        }
165
        if ($field instanceof TwigAwareFieldInterface) {
166
            $field->setTwig($this->twig);
167
        }
168
169
        return $field;
170
    }
171
172
    /**
173
     * @param array               $configuration
174
     * @param ActionConfiguration $actionConfiguration
175
     *
176
     * @return array
177
     *
178
     * @throws Exception
179
     */
180
    private function resolveConfiguration(array $configuration, ActionConfiguration $actionConfiguration)
181
    {
182
        $resolver = new OptionsResolver();
183
        $fieldConfiguration = new FieldConfiguration(array_keys($this->fieldsMapping));
184
        $fieldConfiguration->configureOptions($resolver);
185
        $fieldConfiguration->setParameters($resolver->resolve($configuration));
186
        $configuration = $fieldConfiguration->all();
187
188
        // For collection of fields, we resolve the configuration of each item
189
        if ('collection' == $fieldConfiguration->getType()) {
190
            $items = [];
191
192
            foreach ($fieldConfiguration->getOptions() as $itemFieldName => $itemFieldConfiguration) {
193
                // The configuration should be an array
194
                if (!$itemFieldConfiguration) {
195
                    $itemFieldConfiguration = [];
196
                }
197
198
                // The type should be defined
199
                if (!array_key_exists('type', $itemFieldConfiguration)) {
200
                    throw new Exception("Missing type configuration for field {$itemFieldName}");
201
                }
202
203
                // The field options are optional
204
                if (!array_key_exists('options', $itemFieldConfiguration)) {
205
                    $itemFieldConfiguration['options'] = [];
206
                }
207
208
                // create collection item
209
                $items[] = $this->create($itemFieldName, $itemFieldConfiguration, $actionConfiguration);
210
            }
211
            // add created item to the field options
212
            $configuration['options'] = [
213
                'fields' => $items,
214
            ];
215
        }
216
217
        return $configuration;
218
    }
219
}
220