Field   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 45
eloc 140
c 2
b 1
f 0
dl 0
loc 380
ccs 0
cts 181
cp 0
rs 8.8

9 Methods

Rating   Name   Duplication   Size   Complexity  
B matrixFieldsOfType() 0 31 8
A fieldsOfTypeFromElement() 0 12 2
A fieldsOfTypeFromGlobals() 0 26 3
A fieldsOfTypeFromAssetVolumes() 0 21 4
A fieldsOfTypeFromUsers() 0 5 1
B neoFieldsOfType() 0 27 7
B superTableFieldsOfType() 0 34 8
B fieldsOfTypeFromLayout() 0 32 8
A fieldsOfTypeFromSource() 0 26 4

How to fix   Complexity   

Complex Class

Complex classes like Field often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Field, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
4
 *
5
 * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful,
6
 * and flexible
7
 *
8
 * @link      https://nystudio107.com
9
 * @copyright Copyright (c) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\helpers;
13
14
use benf\neo\elements\Block as NeoBlock;
15
use benf\neo\Field as NeoField;
16
use besteadfast\preparsefield\fields\PreparseFieldType;
17
use Craft;
18
use craft\base\Element;
19
use craft\base\Field as BaseField;
20
use craft\base\Volume;
21
use craft\ckeditor\Field as CKEditorField;
22
use craft\elements\MatrixBlock;
23
use craft\elements\User;
24
use craft\fields\Assets as AssetsField;
25
use craft\fields\Matrix as MatrixField;
26
use craft\fields\PlainText as PlainTextField;
27
use craft\fields\Tags as TagsField;
28
use craft\models\FieldLayout;
29
use craft\redactor\Field as RedactorField;
30
use nystudio107\seomatic\fields\Seomatic_Meta as Seomatic_MetaField;
31
use nystudio107\seomatic\fields\SeoSettings as SeoSettingsField;
32
use nystudio107\seomatic\Seomatic;
33
use nystudio107\seomatic\services\MetaBundles;
34
use verbb\doxter\fields\Doxter as DoxterField;
35
use verbb\supertable\elements\SuperTableBlockElement as SuperTableBlock;
36
use verbb\supertable\fields\SuperTableField;
37
use verbb\supertable\models\SuperTableBlockTypeModel;
38
use yii\base\InvalidConfigException;
39
40
/**
41
 * @author    nystudio107
42
 * @package   Seomatic
43
 * @since     3.0.0
44
 */
45
class Field
46
{
47
    // Constants
48
    // =========================================================================
49
50
    const TEXT_FIELD_CLASS_KEY = 'text';
51
    const ASSET_FIELD_CLASS_KEY = 'asset';
52
    const BLOCK_FIELD_CLASS_KEY = 'block';
53
    const SEO_SETTINGS_CLASS_KEY = 'seo';
54
    const OLD_SEOMATIC_META_CLASS_KEY = 'Seomatic_Meta';
55
56
    const FIELD_CLASSES = [
57
        self::TEXT_FIELD_CLASS_KEY => [
58
            CKEditorField::class,
59
            PlainTextField::class,
60
            MatrixField::class,
61
            RedactorField::class,
62
            TagsField::class,
63
            NeoField::class,
64
            SuperTableField::class,
65
            PreparseFieldType::class,
66
            DoxterField::class,
67
        ],
68
        self::ASSET_FIELD_CLASS_KEY => [
69
            AssetsField::class,
70
        ],
71
        self::BLOCK_FIELD_CLASS_KEY => [
72
            MatrixField::class,
73
            NeoField::class,
74
            SuperTableField::class,
75
        ],
76
        self::SEO_SETTINGS_CLASS_KEY => [
77
            SeoSettingsField::class,
78
        ],
79
        self::OLD_SEOMATIC_META_CLASS_KEY => [
80
            Seomatic_MetaField::class,
81
        ],
82
    ];
83
84
    // Static Properties
85
    // =========================================================================
86
87
    /**
88
     * @var array Memoization cache
89
     */
90
    public static $fieldsOfTypeFromLayoutCache = [];
91
92
    /**
93
     * @var array Memoization cache
94
     */
95
    public static $matrixFieldsOfTypeCache = [];
96
97
    /**
98
     * @var array Memoization cache
99
     */
100
    public static $neoFieldsOfTypeCache = [];
101
102
    /**
103
     * @var array Memoization cache
104
     */
105
    public static $superTableFieldsOfTypeCache = [];
106
107
    // Static Methods
108
    // =========================================================================
109
110
    /**
111
     * Return all of the fields from the $layout that are of the type
112
     * $fieldClassKey
113
     *
114
     * @param string $fieldClassKey
115
     * @param FieldLayout $layout
116
     * @param bool $keysOnly
117
     *
118
     * @return array
119
     */
120
    public static function fieldsOfTypeFromLayout(
121
        string      $fieldClassKey,
122
        FieldLayout $layout,
123
        bool        $keysOnly = true
124
    ): array {
125
        $foundFields = [];
126
        if (!empty(self::FIELD_CLASSES[$fieldClassKey])) {
127
            // Cache me if you can
128
            $memoKey = $fieldClassKey . $layout->id . ($keysOnly ? 'keys' : 'nokeys');
129
            if (!empty(self::$fieldsOfTypeFromLayoutCache[$memoKey])) {
130
                return self::$fieldsOfTypeFromLayoutCache[$memoKey];
131
            }
132
            $fieldClasses = self::FIELD_CLASSES[$fieldClassKey];
133
            $fields = $layout->getFields();
134
            /** @var BaseField $field */
135
            foreach ($fields as $field) {
136
                /** @var array $fieldClasses */
137
                foreach ($fieldClasses as $fieldClass) {
138
                    if ($field instanceof $fieldClass) {
139
                        $foundFields[$field->handle] = $field->name;
140
                    }
141
                }
142
            }
143
            // Return only the keys if asked
144
            if ($keysOnly) {
145
                $foundFields = array_keys($foundFields);
146
            }
147
            // Cache for future use
148
            self::$fieldsOfTypeFromLayoutCache[$memoKey] = $foundFields;
149
        }
150
151
        return $foundFields;
152
    }
153
154
    /**
155
     * Return all of the fields in the $element of the type $fieldClassKey
156
     *
157
     * @param Element $element
158
     * @param string $fieldClassKey
159
     * @param bool $keysOnly
160
     *
161
     * @return array
162
     */
163
    public static function fieldsOfTypeFromElement(
164
        Element $element,
165
        string  $fieldClassKey,
166
        bool    $keysOnly = true
167
    ): array {
168
        $foundFields = [];
169
        $layout = $element->getFieldLayout();
170
        if ($layout !== null) {
171
            $foundFields = self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
172
        }
173
174
        return $foundFields;
175
    }
176
177
    /**
178
     * Return all of the fields from Users layout of the type $fieldClassKey
179
     *
180
     * @param string $fieldClassKey
181
     * @param bool $keysOnly
182
     *
183
     * @return array
184
     */
185
    public static function fieldsOfTypeFromUsers(string $fieldClassKey, bool $keysOnly = true): array
186
    {
187
        $layout = Craft::$app->getFields()->getLayoutByType(User::class);
188
189
        return self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
190
    }
191
192
    /**
193
     * Return all of the fields from all Asset Volume layouts of the type
194
     * $fieldClassKey
195
     *
196
     * @param string $fieldClassKey
197
     * @param bool $keysOnly
198
     *
199
     * @return array
200
     */
201
    public static function fieldsOfTypeFromAssetVolumes(string $fieldClassKey, bool $keysOnly = true): array
202
    {
203
        $foundFields = [];
204
        $volumes = Craft::$app->getVolumes()->getAllVolumes();
205
        foreach ($volumes as $volume) {
206
            /** @var Volume $volume */
207
            try {
208
                $layout = $volume->getFieldLayout();
209
            } catch (InvalidConfigException $e) {
210
                $layout = null;
211
            }
212
            if ($layout) {
213
                /** @noinspection SlowArrayOperationsInLoopInspection */
214
                $foundFields = array_merge(
215
                    $foundFields,
216
                    self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly)
217
                );
218
            }
219
        }
220
221
        return $foundFields;
222
    }
223
224
    /**
225
     * Return all of the fields from all Global Set layouts of the type
226
     * $fieldClassKey
227
     *
228
     * @param string $fieldClassKey
229
     * @param bool $keysOnly
230
     *
231
     * @return array
232
     */
233
    public static function fieldsOfTypeFromGlobals(string $fieldClassKey, bool $keysOnly = true): array
234
    {
235
        $foundFields = [];
236
        $globals = Craft::$app->getGlobals()->getAllSets();
237
        foreach ($globals as $global) {
238
            $layout = $global->getFieldLayout();
239
            if ($layout) {
240
                $fields = self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
241
                // Prefix the keys with the global set name
242
                $prefix = $global->handle;
243
                $fields = array_combine(
244
                    array_map(function($key) use ($prefix) {
245
                        return $prefix . '.' . $key;
246
                    }, array_keys($fields)),
247
                    $fields
248
                );
249
                // Merge with any fields we've already found
250
                /** @noinspection SlowArrayOperationsInLoopInspection */
251
                $foundFields = array_merge(
252
                    $foundFields,
253
                    $fields
254
                );
255
            }
256
        }
257
258
        return $foundFields;
259
    }
260
261
    /**
262
     * Return all of the fields from the $sourceBundleType in the $sourceHandle
263
     * of the type $fieldClassKey
264
     *
265
     * @param string $sourceBundleType
266
     * @param string $sourceHandle
267
     * @param string $fieldClassKey
268
     * @param bool $keysOnly
269
     * @param int|string|null $typeId
270
     *
271
     * @return array
272
     */
273
    public static function fieldsOfTypeFromSource(
274
        string $sourceBundleType,
275
        string $sourceHandle,
276
        string $fieldClassKey,
277
        bool   $keysOnly = true,
278
               $typeId = null
279
    ): array {
280
        $foundFields = [];
281
        $layouts = [];
282
        // Get the layouts
283
        if ($sourceBundleType !== MetaBundles::GLOBAL_META_BUNDLE) {
284
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
285
            if ($seoElement !== null) {
286
                $layouts = $seoElement::fieldLayouts($sourceHandle, $typeId);
287
            }
288
        }
289
        // Iterate through the layouts looking for the fields of the type $fieldType
290
        foreach ($layouts as $layout) {
291
            /** @noinspection SlowArrayOperationsInLoopInspection */
292
            $foundFields = array_merge(
293
                $foundFields,
294
                self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly)
295
            );
296
        }
297
298
        return $foundFields;
299
    }
300
301
    /**
302
     * Return all of the fields in the $matrixBlock of the type $fieldType class
303
     *
304
     * @param MatrixBlock $matrixBlock
305
     * @param string $fieldType
306
     * @param bool $keysOnly
307
     *
308
     * @return array
309
     */
310
    public static function matrixFieldsOfType(MatrixBlock $matrixBlock, string $fieldType, bool $keysOnly = true): array
311
    {
312
        $foundFields = [];
313
314
        try {
315
            $matrixBlockTypeModel = $matrixBlock->getType();
316
        } catch (InvalidConfigException $e) {
317
            $matrixBlockTypeModel = null;
318
        }
319
        if ($matrixBlockTypeModel) {
320
            // Cache me if you can
321
            $memoKey = $fieldType . $matrixBlock->id . ($keysOnly ? 'keys' : 'nokeys');
322
            if (!empty(self::$matrixFieldsOfTypeCache[$memoKey])) {
323
                return self::$matrixFieldsOfTypeCache[$memoKey];
324
            }
325
            $fields = $matrixBlockTypeModel->getFields();
326
            /** @var BaseField $field */
327
            foreach ($fields as $field) {
328
                if ($field instanceof $fieldType) {
329
                    $foundFields[$field->handle] = $field->name;
330
                }
331
            }
332
            // Return only the keys if asked
333
            if ($keysOnly) {
334
                $foundFields = array_keys($foundFields);
335
            }
336
            // Cache for future use
337
            self::$matrixFieldsOfTypeCache[$memoKey] = $foundFields;
338
        }
339
340
        return $foundFields;
341
    }
342
343
344
    /**
345
     * Return all of the fields in the $neoBlock of the type $fieldType class
346
     *
347
     * @param NeoBlock $neoBlock
348
     * @param string $fieldType
349
     * @param bool $keysOnly
350
     *
351
     * @return array
352
     */
353
    public static function neoFieldsOfType(NeoBlock $neoBlock, string $fieldType, bool $keysOnly = true): array
354
    {
355
        $foundFields = [];
356
357
        $layout = $neoBlock->getFieldLayout();
358
        if ($layout) {
359
            // Cache me if you can
360
            $memoKey = $fieldType . $neoBlock->id . ($keysOnly ? 'keys' : 'nokeys');
361
            if (!empty(self::$neoFieldsOfTypeCache[$memoKey])) {
362
                return self::$neoFieldsOfTypeCache[$memoKey];
363
            }
364
            $fields = $layout->getFields();
365
            /** @var BaseField $field */
366
            foreach ($fields as $field) {
367
                if ($field instanceof $fieldType) {
368
                    $foundFields[$field->handle] = $field->name;
369
                }
370
            }
371
            // Return only the keys if asked
372
            if ($keysOnly) {
373
                $foundFields = array_keys($foundFields);
374
            }
375
            // Cache for future use
376
            self::$neoFieldsOfTypeCache[$memoKey] = $foundFields;
377
        }
378
379
        return $foundFields;
380
    }
381
382
    /**
383
     * Return all of the fields in the $superTableBlock of the type $fieldType class
384
     *
385
     * @param SuperTableBlock $superTableBlock
386
     * @param string $fieldType
387
     * @param bool $keysOnly
388
     *
389
     * @return array
390
     */
391
    public static function superTableFieldsOfType(SuperTableBlock $superTableBlock, string $fieldType, bool $keysOnly = true): array
392
    {
393
        $foundFields = [];
394
395
        try {
396
            /** @var SuperTableBlockTypeModel $superTableBlockTypeModel */
397
            $superTableBlockTypeModel = $superTableBlock->getType();
398
        } catch (InvalidConfigException $e) {
399
            $superTableBlockTypeModel = null;
400
        }
401
        if ($superTableBlockTypeModel) {
402
            // Cache me if you can
403
            $memoKey = $fieldType . $superTableBlock->id . ($keysOnly ? 'keys' : 'nokeys');
404
            if (!empty(self::$superTableFieldsOfTypeCache[$memoKey])) {
405
                return self::$superTableFieldsOfTypeCache[$memoKey];
406
            }
407
            // The SuperTableBlockTypeModel class lacks @mixin FieldLayoutBehavior in its annotations
408
            /** @phpstan-ignore-next-line */
409
            $fields = $superTableBlockTypeModel->getFields();
410
            /** @var BaseField $field */
411
            foreach ($fields as $field) {
412
                if ($field instanceof $fieldType) {
413
                    $foundFields[$field->handle] = $field->name;
414
                }
415
            }
416
            // Return only the keys if asked
417
            if ($keysOnly) {
418
                $foundFields = array_keys($foundFields);
419
            }
420
            // Cache for future use
421
            self::$superTableFieldsOfTypeCache[$memoKey] = $foundFields;
422
        }
423
424
        return $foundFields;
425
    }
426
}
427