Completed
Push — develop ( 6e693a...0a1380 )
by Nate
01:48
created

Link::getTableAttributeHtml()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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