Completed
Push — master ( acf3d3...2faf4f )
by Nate
59s
created

Link::normalizeValue()   B

Complexity

Conditions 10
Paths 21

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 0
cts 30
cp 0
rs 7.6666
c 0
b 0
f 0
cc 10
nc 21
nop 2
crap 110

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/link/license
6
 * @link       https://www.flipboxfactory.com/software/link/
7
 */
8
9
namespace flipbox\craft\link\fields;
10
11
use Craft;
12
use craft\base\ElementInterface;
13
use craft\base\Field;
14
use craft\base\PreviewableFieldInterface;
15
use craft\helpers\ArrayHelper;
16
use craft\helpers\Db;
17
use craft\helpers\Json;
18
use flipbox\craft\link\Link as LinkPlugin;
19
use flipbox\craft\link\types\TypeInterface;
20
use flipbox\craft\link\validators\LinkValidator;
21
use flipbox\craft\link\web\assets\settings\Settings;
22
use yii\db\Schema;
23
24
/**
25
 * @author Flipbox Factory <[email protected]>
26
 * @since 1.0.0
27
 */
28
class Link extends Field implements PreviewableFieldInterface
29
{
30
    /**
31
     * @var string
32
     */
33
    public $selectionLabel = ' -- Select link type --';
34
35
    /**
36
     * Type objects that have been configured via the admin
37
     *
38
     * @var TypeInterface[]
39
     */
40
    protected $types = [];
41
42
    /**
43
     * Raw configurations that have been configured via the admin
44
     *
45
     * @var array
46
     */
47
    protected $typeConfigs = [];
48
49
    /**
50
     * @return TypeInterface[]
51
     * @throws \yii\base\InvalidConfigException
52
     */
53
    public function getTypes()
54
    {
55
        // Make sure all un-resolved configs are resolved
56
        $this->resolveConfigs();
57
58
        return $this->types;
59
    }
60
61
    /**
62
     * @param array $types
63
     * @return $this
64
     */
65
    public function setTypes(array $types)
66
    {
67
        foreach ($types as $identifier => $type) {
68
            $identifier = is_array($type) ? ArrayHelper::getValue($type, 'identifier', $identifier) : $identifier;
69
            $this->typeConfigs[$identifier] = $type;
70
        }
71
        return $this;
72
    }
73
74
    /**
75
     * @param string $identifier
76
     * @return TypeInterface|null
77
     * @throws \yii\base\InvalidConfigException
78
     */
79
    public function getType(string $identifier)
80
    {
81
        // Is it already an object?
82
        if (!array_key_exists($identifier, $this->types)) {
83
            // Can we create it?
84
            if (!$type = $this->createFromConfig($identifier)) {
85
                return null;
86
            }
87
88
            $this->types[$identifier] = $type;
89
        }
90
91
        return clone $this->types[$identifier];
92
    }
93
94
    /**
95
     * @inheritdoc
96
     */
97
    public static function displayName(): string
98
    {
99
        return Craft::t('link', 'Link');
100
    }
101
102
    /**
103
     * @inheritdoc
104
     */
105
    public function getContentColumnType(): string
106
    {
107
        return Schema::TYPE_TEXT;
108
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113
    public function getTableAttributeHtml($value, ElementInterface $element): string
114
    {
115
        if ($value instanceof TypeInterface) {
116
            return (string) $value;
117
        }
118
119
        return '';
120
    }
121
122
    /**
123
     * @inheritdoc
124
     * @throws \Twig_Error_Loader
125
     * @throws \yii\base\Exception
126
     * @throws \yii\base\InvalidConfigException
127
     */
128
    public function getSettingsHtml()
129
    {
130
131
        $view = Craft::$app->getView();
132
133
        $view->registerAssetBundle(Settings::class);
134
135
        return $view->renderTemplate(
136
            'link/_components/fieldtypes/Link/settings',
137
            [
138
                'field' => $this,
139
                'types' => LinkPlugin::getInstance()->findAllTypes(),
140
                'namespace' => $view->getNamespace()
141
            ]
142
        );
143
    }
144
145
    /**
146
     * @inheritdoc
147
     * @throws \Twig_Error_Loader
148
     * @throws \yii\base\Exception
149
     */
150
    public function getInputHtml($value, ElementInterface $element = null): string
151
    {
152
        return Craft::$app->getView()->renderTemplate(
153
            'link/_components/fieldtypes/Link/input',
154
            [
155
                'field' => $this,
156
                'value' => $value,
157
                'element' => $element
158
            ]
159
        );
160
    }
161
162
    /**
163
     * @inheritdoc
164
     */
165
    public function serializeValue($value, ElementInterface $element = null)
166
    {
167
        if ($value === null) {
168
            return $value;
169
        }
170
171
        if ($value instanceof TypeInterface) {
172
            $value = array_merge(
173
                [
174
                    'identifier' => $value->getIdentifier(),
175
                ],
176
                $value->getProperties()
177
            );
178
        }
179
180
        return Db::prepareValueForDb($value);
181
    }
182
183
    /**
184
     * @inheritdoc
185
     * @throws \yii\base\InvalidConfigException
186
     */
187
    public function getSettings(): array
188
    {
189
        $settings = parent::getSettings();
190
191
        // Merge the type settings
192
        foreach ($this->getTypes() as $identifier => $type) {
193
            $settings['types'][$identifier] = array_merge(
194
                ['class' => get_class($type)],
195
                $type->getSettings()
196
            );
197
        }
198
199
        return $settings;
200
    }
201
202
    /**
203
     * @inheritdoc
204
     */
205
    public function getElementValidationRules(): array
206
    {
207
        return [
208
            [
209
                LinkValidator::class
210
            ]
211
        ];
212
    }
213
214
    /**
215
     * @param $value
216
     * @param ElementInterface|null $element
217
     * @return array|TypeInterface|mixed|object|null
218
     * @throws \yii\base\InvalidConfigException
219
     */
220
    public function normalizeValue($value, ElementInterface $element = null)
221
    {
222
        if ($value instanceof TypeInterface) {
223
            return $value;
224
        }
225
226
        if (is_string($value) && !empty($value)) {
227
            $value = Json::decodeIfJson($value);
228
        }
229
230
        if (!is_array($value)) {
231
            return null;
232
        }
233
234
        // Get the type by identifier
235
        if ($identifier = ArrayHelper::remove($value, 'identifier')) {
236
            $type = $this->getType($identifier);
237
        } else {
238
            if ($class = ArrayHelper::remove($value, 'class')) {
239
                $type = new $class;
240
            }
241
        }
242
243
        if (empty($type) || !$type instanceof TypeInterface) {
244
            return null;
245
        }
246
247
        // When saving via the admin, there may be multiple 'types' configured
248
        if ($types = ArrayHelper::remove($value, 'types')) {
249
            $value = array_merge(
250
                ArrayHelper::remove($types, $identifier, []),
251
                $value
252
            );
253
        }
254
255
        // Populate
256
        $type->populate($value);
257
258
        return $type;
259
    }
260
261
    /**
262
     * Create objects from all (remaining) configurations
263
     *
264
     * @throws \yii\base\InvalidConfigException
265
     */
266
    private function resolveConfigs()
267
    {
268
        foreach ($this->typeConfigs as $identifier => $config) {
269
            $this->resolveConfig($identifier, $config);
270
        }
271
        $this->typeConfigs = [];
272
    }
273
274
    /**
275
     * @param string $identifier
276
     * @param array  $config
277
     * @return TypeInterface|null
278
     * @throws \yii\base\InvalidConfigException
279
     */
280
    private function resolveConfig(string $identifier, array $config)
281
    {
282
        // Create new
283
        /**
284
         * @var TypeInterface $type
285
         */
286
        if (!$type = $this->createType($config)) {
287
            return null;
288
        }
289
290
        $type->setIdentifier($identifier);
291
292
        // Cache it
293
        $this->types[$identifier] = $type;
294
295
        return $type;
296
    }
297
298
    /**
299
     * @param $type
300
     * @return array|object|null
301
     * @throws \yii\base\InvalidConfigException
302
     */
303
    private function createType($type)
304
    {
305
        if ($type instanceof TypeInterface) {
306
            return $type;
307
        }
308
309
        if (!is_array($type)) {
310
            $type = ['class' => $type];
311
        }
312
313
        $type = Craft::createObject(
314
            $type
315
        );
316
317
        if (!$type instanceof TypeInterface) {
318
            return null;
319
        }
320
321
        return $type;
322
    }
323
324
    /**
325
     * @param string $identifier
326
     * @return TypeInterface|null
327
     * @throws \yii\base\InvalidConfigException
328
     */
329
    private function createFromConfig(string $identifier)
330
    {
331
        if (!$config = ArrayHelper::remove($this->typeConfigs, $identifier)) {
332
            return null;
333
        }
334
335
        return $this->resolveConfig($identifier, $config);
336
    }
337
}
338