Test Failed
Push — v5 ( 567dd3...527085 )
by Andrew
46:05 queued 21:29
created

Field::fieldsOfTypeFromLayout()   C

Complexity

Conditions 13
Paths 20

Size

Total Lines 49
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 26
c 4
b 1
f 0
dl 0
loc 49
rs 6.6166
cc 13
nc 20
nop 6

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
 * SEOmatic plugin for Craft CMS
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;
0 ignored issues
show
Bug introduced by
The type besteadfast\preparsefield\fields\PreparseFieldType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Craft;
18
use craft\base\Element;
19
use craft\base\Field as BaseField;
20
use craft\base\FieldInterface;
21
use craft\ckeditor\Field as CKEditorField;
22
use craft\elements\Entry;
0 ignored issues
show
Bug introduced by
The type craft\elements\Entry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use craft\elements\User;
24
use craft\fieldlayoutelements\CustomField;
25
use craft\fields\Assets as AssetsField;
26
use craft\fields\ContentBlock as ContentBlockField;
27
use craft\fields\Matrix as MatrixField;
28
use craft\fields\PlainText as PlainTextField;
29
use craft\fields\Tags as TagsField;
30
use craft\models\FieldLayout;
31
use craft\models\Volume;
32
use craft\redactor\Field as RedactorField;
33
use nystudio107\seomatic\fields\Seomatic_Meta as Seomatic_MetaField;
34
use nystudio107\seomatic\fields\SeoSettings as SeoSettingsField;
35
use nystudio107\seomatic\Seomatic;
36
use nystudio107\seomatic\services\MetaBundles;
37
use verbb\doxter\fields\Doxter as DoxterField;
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
    public const TEXT_FIELD_CLASS_KEY = 'text';
51
    public const ASSET_FIELD_CLASS_KEY = 'asset';
52
    public const BLOCK_FIELD_CLASS_KEY = 'block';
53
    public const NESTED_FIELD_CLASS_KEY = 'nested';
54
    public const SEO_SETTINGS_CLASS_KEY = 'seo';
55
    public const OLD_SEOMATIC_META_CLASS_KEY = 'Seomatic_Meta';
56
57
    public const FIELD_CLASSES = [
58
        self::TEXT_FIELD_CLASS_KEY => [
59
            CKEditorField::class,
60
            PlainTextField::class,
61
            MatrixField::class,
62
            RedactorField::class,
63
            TagsField::class,
64
            NeoField::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
        ],
75
        self::SEO_SETTINGS_CLASS_KEY => [
76
            SeoSettingsField::class,
77
        ],
78
        self::OLD_SEOMATIC_META_CLASS_KEY => [
79
            Seomatic_MetaField::class,
80
        ],
81
    ];
82
83
    // Static Properties
84
    // =========================================================================
85
86
    /**
87
     * @var array Memoization cache
88
     */
89
    public static $fieldsOfTypeFromLayoutCache = [];
90
91
    /**
92
     * @var array Memoization cache
93
     */
94
    public static $matrixFieldsOfTypeCache = [];
95
96
    /**
97
     * @var array Memoization cache
98
     */
99
    public static $neoFieldsOfTypeCache = [];
100
101
    // Static Methods
102
    // =========================================================================
103
104
    /**
105
     * Return all of the fields from the $layout that are of the type
106
     * $fieldClassKey
107
     *
108
     * @param string $fieldClassKey
109
     * @param FieldLayout $layout
110
     * @param bool $keysOnly
111
     * @param FieldInterface|null $parentField
112
     * @param CustomField|null $parentFieldElement
113
     * @return array
114
     */
115
    public static function fieldsOfTypeFromLayout(
116
        string          $fieldClassKey,
117
        FieldLayout     $layout,
118
        bool            $keysOnly = true,
119
        ?FieldInterface $parentField = null,
120
        ?CustomField    $parentFieldElement = null,
121
        bool            $parseContentBlocks = true,
122
    ): array {
123
        $foundFields = [];
124
        $nestedFoundFields = [];
125
        $handlePrefix = '';
126
        $namePrefix = '';
127
        if ($parentField !== null && $parentFieldElement !== null) {
128
            $handlePrefix = $parentField->handle . '.';
129
            $namePrefix = ($parentFieldElement->label() ?? $parentField->name) . ' → ';
130
        }
131
        if (!empty(self::FIELD_CLASSES[$fieldClassKey])) {
132
            // Cache me if you can
133
            $memoKey = $fieldClassKey . $layout->id . ($keysOnly ? 'keys' : 'nokeys') . ($parseContentBlocks ? 'content' : 'nocontent');
134
            if (!empty(self::$fieldsOfTypeFromLayoutCache[$memoKey])) {
135
                return self::$fieldsOfTypeFromLayoutCache[$memoKey];
136
            }
137
            $fieldClasses = self::FIELD_CLASSES[$fieldClassKey];
138
            $fieldElements = $layout->getCustomFieldElements();
139
            foreach ($fieldElements as $fieldElement) {
140
                $field = $fieldElement->getField();
141
                // Handle ContentBlock fields recursively
142
                if ($parseContentBlocks && $field instanceof ContentBlockField) {
143
                    $nestedFoundFields = array_merge($nestedFoundFields,
144
                        self::fieldsOfTypeFromLayout($fieldClassKey, $field->getFieldLayout(), $keysOnly, $field, $fieldElement, $parseContentBlocks));
145
                }
146
                /** @var array $fieldClasses */
147
                foreach ($fieldClasses as $fieldClass) {
148
                    if ($field instanceof $fieldClass) {
149
                        $foundFields[$handlePrefix . $field->handle] = $namePrefix . ($fieldElement->label() ?? $field->name);
150
                    }
151
                }
152
            }
153
154
            // Return only the keys if asked
155
            if ($keysOnly) {
156
                $foundFields = array_keys($foundFields);
157
            }
158
            $foundFields = array_merge($foundFields, $nestedFoundFields);
159
            // Cache for future use
160
            self::$fieldsOfTypeFromLayoutCache[$memoKey] = $foundFields;
161
        }
162
163
        return $foundFields;
164
    }
165
166
    /**
167
     * Return all of the fields in the $element of the type $fieldClassKey
168
     *
169
     * @param Element $element
170
     * @param string $fieldClassKey
171
     * @param bool $keysOnly
172
     *
173
     * @return array
174
     */
175
    public static function fieldsOfTypeFromElement(
176
        Element $element,
177
        string  $fieldClassKey,
178
        bool    $keysOnly = true,
179
    ): array {
180
        $foundFields = [];
181
        $layout = $element->getFieldLayout();
182
        if ($layout !== null) {
183
            $foundFields = self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
184
        }
185
186
        return $foundFields;
187
    }
188
189
    /**
190
     * Return all of the fields from Users layout of the type $fieldClassKey
191
     *
192
     * @param string $fieldClassKey
193
     * @param bool $keysOnly
194
     *
195
     * @return array
196
     */
197
    public static function fieldsOfTypeFromUsers(string $fieldClassKey, bool $keysOnly = true): array
198
    {
199
        $layout = Craft::$app->getFields()->getLayoutByType(User::class);
200
201
        return self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
202
    }
203
204
    /**
205
     * Return all of the fields from all Asset Volume layouts of the type
206
     * $fieldClassKey
207
     *
208
     * @param string $fieldClassKey
209
     * @param bool $keysOnly
210
     *
211
     * @return array
212
     */
213
    public static function fieldsOfTypeFromAssetVolumes(string $fieldClassKey, bool $keysOnly = true): array
214
    {
215
        $foundFields = [];
216
        $volumes = Craft::$app->getVolumes()->getAllVolumes();
217
        foreach ($volumes as $volume) {
218
            /** @var Volume $volume */
219
            try {
220
                $layout = $volume->getFieldLayout();
221
            } catch (InvalidConfigException $e) {
222
                $layout = null;
223
            }
224
            if ($layout) {
225
                /** @noinspection SlowArrayOperationsInLoopInspection */
226
                $foundFields = array_merge(
227
                    $foundFields,
228
                    self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly)
229
                );
230
            }
231
        }
232
233
        return $foundFields;
234
    }
235
236
    /**
237
     * Return all of the fields from all Global Set layouts of the type
238
     * $fieldClassKey
239
     *
240
     * @param string $fieldClassKey
241
     * @param bool $keysOnly
242
     *
243
     * @return array
244
     */
245
    public static function fieldsOfTypeFromGlobals(string $fieldClassKey, bool $keysOnly = true): array
246
    {
247
        $foundFields = [];
248
        $globals = Craft::$app->getGlobals()->getAllSets();
249
        foreach ($globals as $global) {
250
            /** @var FieldLayout|null $layout */
251
            $layout = $global->getFieldLayout();
252
            if ($layout) {
253
                $fields = self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly);
254
                // Prefix the keys with the global set name
255
                $prefix = $global->handle;
256
                $fields = array_combine(
257
                    array_map(function($key) use ($prefix) {
258
                        return $prefix . '.' . $key;
259
                    }, array_keys($fields)),
260
                    $fields
261
                );
262
                // Merge with any fields we've already found
263
                /** @noinspection SlowArrayOperationsInLoopInspection */
264
                $foundFields = array_merge(
265
                    $foundFields,
266
                    $fields
267
                );
268
            }
269
        }
270
271
        return $foundFields;
272
    }
273
274
    /**
275
     * Return all of the fields from the $sourceBundleType in the $sourceHandle
276
     * of the type $fieldClassKey
277
     *
278
     * @param string $sourceBundleType
279
     * @param string $sourceHandle
280
     * @param string $fieldClassKey
281
     * @param bool $keysOnly
282
     * @param int|string|null $typeId
283
     *
284
     * @return array
285
     */
286
    public static function fieldsOfTypeFromSource(
287
        string $sourceBundleType,
288
        string $sourceHandle,
289
        string $fieldClassKey,
290
        bool   $keysOnly = true,
291
               $typeId = null,
292
    ): array {
293
        $foundFields = [];
294
        $layouts = [];
295
        // Get the layouts
296
        if ($sourceBundleType !== MetaBundles::GLOBAL_META_BUNDLE) {
297
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
298
            if ($seoElement !== null) {
299
                $layouts = $seoElement::fieldLayouts($sourceHandle, $typeId);
300
            }
301
        }
302
        // Iterate through the layouts looking for the fields of the type $fieldType
303
        foreach ($layouts as $layout) {
304
            /** @noinspection SlowArrayOperationsInLoopInspection */
305
            $foundFields = array_merge(
306
                $foundFields,
307
                self::fieldsOfTypeFromLayout($fieldClassKey, $layout, $keysOnly)
308
            );
309
        }
310
311
        return $foundFields;
312
    }
313
314
    /**
315
     * Return all of the fields in the $matrixEntry of the type $fieldType class
316
     *
317
     * @param Entry $matrixEntry
318
     * @param string $fieldType
319
     * @param bool $keysOnly
320
     *
321
     * @return array
322
     */
323
    public static function matrixFieldsOfType(Entry $matrixEntry, string $fieldType, bool $keysOnly = true): array
324
    {
325
        $foundFields = [];
326
327
        try {
328
            $matrixEntryTypeModel = $matrixEntry->getType();
329
        } catch (InvalidConfigException $e) {
330
            $matrixEntryTypeModel = null;
331
        }
332
        if ($matrixEntryTypeModel) {
333
            // Cache me if you can
334
            $memoKey = $fieldType . $matrixEntry->id . ($keysOnly ? 'keys' : 'nokeys');
335
            if (!empty(self::$matrixFieldsOfTypeCache[$memoKey])) {
336
                return self::$matrixFieldsOfTypeCache[$memoKey];
337
            }
338
            $fields = $matrixEntryTypeModel->getCustomFields();
339
            /** @var BaseField $field */
340
            foreach ($fields as $field) {
341
                if ($field instanceof $fieldType) {
342
                    $foundFields[$field->handle] = $field->name;
343
                }
344
            }
345
            // Return only the keys if asked
346
            if ($keysOnly) {
347
                $foundFields = array_keys($foundFields);
348
            }
349
            // Cache for future use
350
            self::$matrixFieldsOfTypeCache[$memoKey] = $foundFields;
351
        }
352
353
        return $foundFields;
354
    }
355
356
357
    /**
358
     * Return all of the fields in the $neoBlock of the type $fieldType class
359
     *
360
     * @param NeoBlock $neoBlock
361
     * @param string $fieldType
362
     * @param bool $keysOnly
363
     *
364
     * @return array
365
     */
366
    public static function neoFieldsOfType(NeoBlock $neoBlock, string $fieldType, bool $keysOnly = true): array
367
    {
368
        $foundFields = [];
369
370
        $layout = $neoBlock->getFieldLayout();
371
        if ($layout) {
372
            // Cache me if you can
373
            $memoKey = $fieldType . $neoBlock->id . ($keysOnly ? 'keys' : 'nokeys');
374
            if (!empty(self::$neoFieldsOfTypeCache[$memoKey])) {
375
                return self::$neoFieldsOfTypeCache[$memoKey];
376
            }
377
            $fieldElements = $layout->getCustomFieldElements();
378
            foreach ($fieldElements as $fieldElement) {
379
                $field = $fieldElement->getField();
380
                if ($field instanceof $fieldType) {
381
                    $foundFields[$field->handle] = $field->name;
382
                }
383
            }
384
            // Return only the keys if asked
385
            if ($keysOnly) {
386
                $foundFields = array_keys($foundFields);
387
            }
388
            // Cache for future use
389
            self::$neoFieldsOfTypeCache[$memoKey] = $foundFields;
390
        }
391
392
        return $foundFields;
393
    }
394
}
395