Issues (257)

src/helpers/Field.php (1 issue)

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