ImageType   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 10
dl 0
loc 140
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A buildForm() 0 17 3
A configureOptions() 0 38 1
A buildView() 0 23 3
B buildDeleteField() 0 42 7
A setAspectRatio() 0 4 1
1
<?php
2
3
namespace Presta\ImageBundle\Form\Type;
4
5
use Presta\ImageBundle\Form\DataTransformer\Base64ToImageTransformer;
6
use Presta\ImageBundle\Model\AspectRatio;
7
use Symfony\Component\Form\AbstractType;
8
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
9
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
10
use Symfony\Component\Form\FormBuilderInterface;
11
use Symfony\Component\Form\FormEvent;
12
use Symfony\Component\Form\FormEvents;
13
use Symfony\Component\Form\FormInterface;
14
use Symfony\Component\Form\FormView;
15
use Symfony\Component\OptionsResolver\Options;
16
use Symfony\Component\OptionsResolver\OptionsResolver;
17
use Vich\UploaderBundle\Handler\UploadHandler;
18
use Vich\UploaderBundle\Storage\StorageInterface;
19
20
class ImageType extends AbstractType
21
{
22
    private $storage;
23
    private $handler;
24
25
    public function __construct(StorageInterface $storage, UploadHandler $handler)
26
    {
27
        $this->storage = $storage;
28
        $this->handler = $handler;
29
    }
30
31
    public function buildForm(FormBuilderInterface $builder, array $options): void
32
    {
33
        $builder
34
            ->add('base64', HiddenType::class, [
35
                'required' => $options['required'],
36
                'attr' => [
37
                    'class' => 'cropper-base64',
38
                ],
39
            ])
40
        ;
41
42
        $builder->addModelTransformer(new Base64ToImageTransformer());
43
44
        if ($options['allow_delete'] && !$options['required']) {
45
            $this->buildDeleteField($builder, $options);
46
        }
47
    }
48
49
    public function configureOptions(OptionsResolver $resolver): void
50
    {
51
        $aspectRatios = [];
52
53
        $this->setAspectRatio($aspectRatios, '16:9', 1.78);
54
        $this->setAspectRatio($aspectRatios, '4:3', 1.33);
55
        $this->setAspectRatio($aspectRatios, '1', 1);
56
        $this->setAspectRatio($aspectRatios, '2:3', 0.66);
57
        $this->setAspectRatio($aspectRatios, 'nan', null, true);
58
59
        $resolver
60
            ->setDefault('allow_delete', true)
61
            ->setDefault('delete_label', 'btn_delete')
62
            ->setDefault('aspect_ratios', $aspectRatios)
63
            ->setDefault('cropper_options', ['autoCropArea' => 1])
64
            ->setDefault('max_width', 320)
65
            ->setDefault('max_height', 180)
66
            ->setDefault('preview_width', function (Options $options) {
67
                return sprintf('%dpx', $options['max_width']);
68
            })
69
            ->setDefault('preview_height', function (Options $options) {
70
                return sprintf('%dpx', $options['max_height']);
71
            })
72
            ->setDefault('upload_button_class', 'btn btn-sm btn-primary')
73
            ->setDefault('upload_button_icon', 'fa fa-upload')
74
            ->setDefault('cancel_button_class', 'btn btn-default')
75
            ->setDefault('save_button_class', 'btn btn-primary')
76
            ->setDefault('download_uri', null)
77
            ->setDefault('download_link', true)
78
            ->setDefault('enable_locale', true)
79
            ->setDefault('enable_remote', true)
80
            ->setDefault('enable_rotation', false)
81
            ->setDefault('translation_domain', 'PrestaImageBundle')
82
            ->setDefault('upload_mimetype', 'image/png')
83
            ->setDefault('upload_quality', 0.92)  // default value: https://developer.mozilla.org/de/docs/Web/API/HTMLCanvasElement/toDataURL
84
            ->setDefault('error_bubbling', false)
85
        ;
86
    }
87
88
    public function buildView(FormView $view, FormInterface $form, array $options): void
89
    {
90
        $view->vars['aspect_ratios'] = $options['aspect_ratios'];
91
        $view->vars['cropper_options'] = json_encode($options['cropper_options']);
92
        $view->vars['max_width'] = $options['max_width'];
93
        $view->vars['max_height'] = $options['max_height'];
94
        $view->vars['preview_width'] = $options['preview_width'];
95
        $view->vars['preview_height'] = $options['preview_height'];
96
        $view->vars['upload_button_class'] = $options['upload_button_class'];
97
        $view->vars['upload_button_icon'] = $options['upload_button_icon'];
98
        $view->vars['cancel_button_class'] = $options['cancel_button_class'];
99
        $view->vars['save_button_class'] = $options['save_button_class'];
100
        $view->vars['enable_locale'] = $options['enable_locale'];
101
        $view->vars['enable_remote'] = $options['enable_remote'];
102
        $view->vars['enable_rotation'] = $options['enable_rotation'];
103
        $view->vars['upload_mimetype'] = $options['upload_mimetype'];
104
        $view->vars['upload_quality'] = $options['upload_quality'];
105
        $view->vars['object'] = $form->getParent()->getData();
106
107
        if ($options['download_link'] && $view->vars['object']) {
108
            $view->vars['download_uri'] = $this->storage->resolveUri($form->getParent()->getData(), $form->getName());
109
        }
110
    }
111
112
    private function buildDeleteField(FormBuilderInterface $builder, array $options): void
113
    {
114
        // add delete only if there is a file
115
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
116
            $form = $event->getForm();
117
            $object = $form->getParent()->getData();
118
119
            // no object or no uploaded file: no delete button
120
            if (null === $object || null === $this->storage->resolvePath($object, $form->getName())) {
121
                return;
122
            }
123
124
            $form->add('delete', CheckboxType::class, [
125
                'label' => $options['delete_label'],
126
                'required' => false,
127
                'mapped' => false,
128
                'translation_domain' => $options['translation_domain']
129
            ]);
130
        });
131
132
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
133
            $data = $event->getData();
134
            $delete = isset($data['delete']) ? $data['delete'] : false;
135
            if ($delete) {
136
                $data['base64'] = null;
137
                $event->setData($data);
138
            }
139
        });
140
141
        // delete file if needed
142
        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($options) {
143
            $form = $event->getForm();
144
            $delete = $form->has('delete') ? $form->get('delete')->getData() : false;
145
            $entity = $form->getParent()->getData();
146
147
            if (!$delete) {
148
                return;
149
            }
150
151
            $this->handler->remove($entity, $form->getName());
152
        });
153
    }
154
155
    private function setAspectRatio(array &$aspectRatios, string $key, ?float $value, bool $checked = false): void
156
    {
157
        $aspectRatios[$key] = new AspectRatio($value, "aspect_ratio.$key", $checked);
158
    }
159
}
160