Completed
Pull Request — master (#90)
by Arnaud
02:11
created

FieldFactory::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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