Completed
Push — develop ( 72aae6...d42c3f )
by Freddie
02:15
created

AbstractBuilder::getDefaultOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace FlexPHP\Inputs\Builder;
4
5
use Symfony\Component\Form\Extension\Core\Type\FormType;
6
use Symfony\Component\Form\FormBuilderInterface;
7
use Symfony\Component\Form\Forms;
8
use Symfony\Component\Form\FormInterface;
9
use \InvalidArgumentException;
10
11
abstract class AbstractBuilder implements BuilderInterface
12
{
13
    private $name;
14
    private $options;
15
    protected $type;
16
17
    public function __construct(string $name, array $options)
18
    {
19
        $this->name = $name;
20
        $this->options = $this->parseOptions($this->getDefaultOptions($options));
21
    }
22
23
    abstract protected function getType(): string;
24
25
    public function getName(): string
26
    {
27
        return preg_replace('/(\s)+/', '_', trim($this->name)) ?? $this->name;
28
    }
29
30
    public function getOptions(): array
31
    {
32
        return $this->options;
33
    }
34
35
    public function build(): FormInterface
36
    {
37
        $options = $this->getOptions();
38
        $classType = trim($this->getType());
39
40
        if (\strpos($classType, 'Symfony') === false) {
41
            // Not symfony type
42
            $type = preg_replace('/type$/i', '', $classType) ?? $classType;
43
            $classType = \sprintf('\Symfony\Component\Form\Extension\Core\Type\%1$sType', \ucwords($type));
44
        } elseif (!empty($options['type']) && strpos(\strtolower($classType), $options['type']) === false) {
45
            // Symfony type, but its diff in options type contraint
46
            $type = $options['type'];
47
            $classType = \sprintf('\Symfony\Component\Form\Extension\Core\Type\%1$sType', \ucwords($type));
48
        }
49
50
        unset($options['type']);
51
52
        if (!\class_exists($classType)) {
53
            throw new InvalidArgumentException(\sprintf('Type [%1$s] is not supported', $classType));
54
        }
55
56
        return $this
57
            ->factory()
58
            ->add($this->getName(), $classType, $options)
59
            ->getForm();
60
    }
61
62
    public function render(): string
63
    {
64
        return $this->twig()->createTemplate(\sprintf('{{ form_row(form.%1$s) }}', $this->getName()))->render([
65
            'form' => $this->build()->createView(),
66
        ]);
67
    }
68
69
    protected function factory(): FormBuilderInterface
70
    {
71
        return Forms::createFormFactory()->createBuilder(FormType::class, null);
72
    }
73
74
    protected function getDefaultOptions(array $options = []): array
75
    {
76
        return array_merge([
77
            'mapped' => false,
78
            'required' => false,
79
            'trim' => false,
80
        ], $options);
81
    }
82
83
    private function parseOptions(array $options): array
84
    {
85
        $_options = [];
86
87
        $options = array_change_key_case(array_filter($options, function ($var) {
88
            return !is_null($var);
89
        }));
90
91
        if (!empty($options['attr']['type'])) {
92
            $options['type'] = $options['attr']['type'];
93
            unset($options['attr']['type']);
94
        }
95
96
        if (!empty($options['constraints']['type'])) {
97
            $options['type'] = $options['constraints']['type'];
98
            unset($options['constraints']['type']);
99
        }
100
101
        foreach ($options as $option => $value) {
102
            switch ($option) {
103
                case 'default':
104
                    $_options['data'] = $value;
105
                    break;
106
                case 'constraints':
107
                    $attributes = \json_decode($value, true);
108
109
                    if ((\json_last_error() !== JSON_ERROR_NONE)) {
110
                        $attributes = [$value];
111
                    }
112
113
                    foreach ($attributes as $attribute => $_value) {
114
                        $attribute = \strtolower($attribute);
115
116
                        if ($attribute == 'required' || $_value == 'required') {
117
                            $_options['required'] = true;
118
                        } elseif (in_array($attribute, ['length', 'mincheck', 'maxcheck', 'check', 'equalto'])) {
119
                            $_options['attr']['data-parsley-' . $attribute] = $_value;
120
                        } elseif ($attribute == 'range') {
121
                            $_options['type'] = $attribute;
122
                            list($min, $max) = explode(',', $_value);
123
                            $_options['attr'] = compact('min', 'max');
124
                        } elseif ($attribute == 'type') {
125
                            $_options['type'] = $_value;
126
                        } else {
127
                            $_options['attr'][$attribute] = $_value;
128
                        }
129
                    }
130
                    break;
131
                case 'empty_data':
132
                    $_options[$option] = $value;
133
134
                    if (empty($_options['attr'])) {
135
                        $_options['attr'] = [];
136
                    }
137
138
                    $_options['attr']['placeholder'] = $value;
139
140
                    break;
141
                default:
142
                    if (is_array($value) && !empty($_options[$option])) {
143
                        $_options[$option] = array_merge_recursive($_options[$option], $value);
144
                    } else {
145
                        $_options[$option] = $value;
146
                    }
147
                    break;
148
            }
149
150
            unset($options[$option]);
151
        }
152
153
        if (!empty($_options['type'])) {
154
            $_type = strtolower($_options['type']);
155
156
            switch ($_type) {
157
                case 'digits':
158
                case 'alphanum':
159
                    $_options['attr']['data-parsley-type'] = $_type;
160
                    $_options['type'] = 'text';
161
                    break;
162
            }
163
        }
164
165
        return $_options;
166
    }
167
168
    private function twig()
169
    {
170
        $appVariableReflection = new \ReflectionClass('\Symfony\Bridge\Twig\AppVariable');
171
        $vendorTwigBridgeDirectory = dirname((string)$appVariableReflection->getFileName());
172
173
        $loader = new \Twig\Loader\FilesystemLoader([
174
            $vendorTwigBridgeDirectory . '/Resources/views/Form',
175
        ]);
176
177
        $twig = new \Twig\Environment($loader);
178
        $twig->addExtension(new \Symfony\Bridge\Twig\Extension\FormExtension());
179
        $twig->addExtension(new \Symfony\Bridge\Twig\Extension\TranslationExtension(
180
            new \Symfony\Component\Translation\Translator('en')
181
        ));
182
183
        $formEngine = new \Symfony\Bridge\Twig\Form\TwigRendererEngine(['bootstrap_4_layout.html.twig'], $twig);
184
        $twig->addRuntimeLoader(new \Twig\RuntimeLoader\FactoryRuntimeLoader([
185
            \Symfony\Component\Form\FormRenderer::class => function () use ($formEngine) {
186
                return new \Symfony\Component\Form\FormRenderer($formEngine);
187
            },
188
        ]));
189
190
        return $twig;
191
    }
192
}
193