1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @copyright Copyright (c) Flipbox Digital Limited |
5
|
|
|
* @license https://flipboxfactory.com/software/meta/license |
6
|
|
|
* @link https://www.flipboxfactory.com/software/meta/ |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace flipbox\meta\services; |
10
|
|
|
|
11
|
|
|
use Craft; |
12
|
|
|
use craft\base\ElementInterface; |
13
|
|
|
use craft\base\Field; |
14
|
|
|
use craft\base\FieldInterface; |
15
|
|
|
use craft\fields\Matrix; |
16
|
|
|
use craft\helpers\ArrayHelper; |
17
|
|
|
use craft\helpers\Json; |
18
|
|
|
use craft\helpers\StringHelper; |
19
|
|
|
use craft\models\FieldLayout; |
20
|
|
|
use craft\models\FieldLayoutTab; |
21
|
|
|
use craft\records\Field as FieldRecord; |
22
|
|
|
use flipbox\meta\db\MetaQuery; |
23
|
|
|
use flipbox\meta\elements\Meta; |
24
|
|
|
use flipbox\meta\fields\Meta as MetaField; |
25
|
|
|
use flipbox\meta\helpers\Field as FieldHelper; |
26
|
|
|
use flipbox\meta\migrations\ContentTable; |
27
|
|
|
use flipbox\meta\records\Meta as MetaRecord; |
28
|
|
|
use flipbox\meta\web\assets\input\Input; |
29
|
|
|
use flipbox\meta\web\assets\settings\Settings as MetaSettingsAsset; |
30
|
|
|
use yii\base\Component; |
31
|
|
|
use yii\base\Exception; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @author Flipbox Factory <[email protected]> |
35
|
|
|
* @since 1.0.0 |
36
|
|
|
*/ |
37
|
|
|
class Configuration extends Component |
38
|
|
|
{ |
39
|
|
|
/** |
40
|
|
|
* @param MetaField $metaField |
41
|
|
|
* @return bool |
42
|
|
|
* @throws \Exception |
43
|
|
|
* @throws \Throwable |
44
|
|
|
* @throws \yii\db\Exception |
45
|
|
|
*/ |
46
|
|
|
public function save(MetaField $metaField) |
47
|
|
|
{ |
48
|
|
|
$transaction = Craft::$app->getDb()->beginTransaction(); |
49
|
|
|
try { |
50
|
|
|
$contentService = Craft::$app->getContent(); |
51
|
|
|
|
52
|
|
|
// Create/Rename table |
53
|
|
|
$this->ensureTable($metaField); |
54
|
|
|
|
55
|
|
|
// Get the originals |
56
|
|
|
$originalContentTable = $contentService->contentTable; |
57
|
|
|
$originalFieldContext = $contentService->fieldContext; |
58
|
|
|
$originalFieldColumnPrefix = $contentService->fieldColumnPrefix; |
59
|
|
|
|
60
|
|
|
// Set our content table |
61
|
|
|
$contentService->contentTable = FieldHelper::getContentTableName($metaField->id); |
62
|
|
|
$contentService->fieldContext = FieldHelper::getContextById($metaField->id); |
63
|
|
|
$contentService->fieldColumnPrefix = 'field_'; |
64
|
|
|
|
65
|
|
|
// Delete old fields |
66
|
|
|
$this->deleteOldFields($metaField); |
67
|
|
|
|
68
|
|
|
// Save fields |
69
|
|
|
$this->saveNewFields($metaField); |
70
|
|
|
|
71
|
|
|
// Revert to originals |
72
|
|
|
$contentService->contentTable = $originalContentTable; |
73
|
|
|
$contentService->fieldContext = $originalFieldContext; |
74
|
|
|
$contentService->fieldColumnPrefix = $originalFieldColumnPrefix; |
75
|
|
|
|
76
|
|
|
// Save the fieldLayoutId via settings |
77
|
|
|
/** @var FieldRecord $fieldRecord */ |
78
|
|
|
$fieldRecord = FieldRecord::findOne($metaField->id); |
79
|
|
|
$fieldRecord->settings = $metaField->getSettings(); |
80
|
|
|
|
81
|
|
|
if (!$fieldRecord->save(true, ['settings'])) { |
82
|
|
|
$metaField->addError( |
83
|
|
|
'settings', |
84
|
|
|
Craft::t('meta', 'Unable to save settings.') |
85
|
|
|
); |
86
|
|
|
$transaction->rollback(); |
87
|
|
|
return false; |
88
|
|
|
} |
89
|
|
|
} catch (\Exception $e) { |
90
|
|
|
$transaction->rollback(); |
91
|
|
|
throw $e; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
$transaction->commit(); |
95
|
|
|
return true; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param MetaField $field |
100
|
|
|
* @return bool |
101
|
|
|
* @throws \Exception |
102
|
|
|
* @throws \yii\db\Exception |
103
|
|
|
*/ |
104
|
|
|
public function beforeDelete(MetaField $field) |
105
|
|
|
{ |
106
|
|
|
$transaction = Craft::$app->getDb()->beginTransaction(); |
107
|
|
|
try { |
108
|
|
|
|
109
|
|
|
// First delete the elements |
110
|
|
|
$elements = Meta::find() |
111
|
|
|
->fieldId($field->id) |
112
|
|
|
->all(); |
113
|
|
|
|
114
|
|
|
foreach ($elements as $element) { |
115
|
|
|
Craft::$app->getElements()->deleteElement($element); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** @var FieldLayout $fieldLayout */ |
119
|
|
|
$fieldLayout = $field->getFieldLayout(); |
120
|
|
|
|
121
|
|
|
// Get content table name |
122
|
|
|
$contentTableName = FieldHelper::getContentTableName($field->id); |
123
|
|
|
|
124
|
|
|
$contentService = Craft::$app->getContent(); |
125
|
|
|
|
126
|
|
|
// Get the originals |
127
|
|
|
$originalContentTable = $contentService->contentTable; |
128
|
|
|
$originalFieldContext = $contentService->fieldContext; |
129
|
|
|
$originalFieldColumnPrefix = $contentService->fieldColumnPrefix; |
130
|
|
|
|
131
|
|
|
// Set our content table |
132
|
|
|
$contentService->contentTable = $contentTableName; |
133
|
|
|
$contentService->fieldContext = FieldHelper::getContextById($field->id); |
134
|
|
|
$contentService->fieldColumnPrefix = 'field_'; |
135
|
|
|
|
136
|
|
|
// Delete fields |
137
|
|
|
foreach ($fieldLayout->getFields() as $field) { |
138
|
|
|
Craft::$app->getFields()->deleteField($field); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
// Revert to originals |
142
|
|
|
$contentService->contentTable = $originalContentTable; |
143
|
|
|
$contentService->fieldContext = $originalFieldContext; |
144
|
|
|
$contentService->fieldColumnPrefix = $originalFieldColumnPrefix; |
145
|
|
|
|
146
|
|
|
// Drop the content table |
147
|
|
|
Craft::$app->getDb()->createCommand() |
148
|
|
|
->dropTableIfExists($contentTableName) |
149
|
|
|
->execute(); |
150
|
|
|
|
151
|
|
|
// Delete field layout |
152
|
|
|
Craft::$app->getFields()->deleteLayout($fieldLayout); |
153
|
|
|
|
154
|
|
|
$transaction->commit(); |
155
|
|
|
|
156
|
|
|
return true; |
157
|
|
|
} catch (\Exception $e) { |
158
|
|
|
// Revert |
159
|
|
|
$transaction->rollback(); |
160
|
|
|
|
161
|
|
|
throw $e; |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @param MetaField $metaField |
168
|
|
|
* @return bool |
169
|
|
|
*/ |
170
|
|
|
public function validate(MetaField $metaField): bool |
171
|
|
|
{ |
172
|
|
|
$validates = true; |
173
|
|
|
|
174
|
|
|
// Can't validate multiple new rows at once so we'll need to give these temporary context to avoid false unique |
175
|
|
|
// handle validation errors, and just validate those manually. Also apply the future fieldColumnPrefix so that |
176
|
|
|
// field handle validation takes its length into account. |
177
|
|
|
$contentService = Craft::$app->getContent(); |
178
|
|
|
$originalFieldContext = $contentService->fieldContext; |
179
|
|
|
|
180
|
|
|
$contentService->fieldContext = StringHelper::randomString(10); |
181
|
|
|
|
182
|
|
|
/** @var FieldRecord $field */ |
183
|
|
|
foreach ($metaField->getFieldLayout()->getFields() as $field) { |
184
|
|
|
// Hack to allow blank field names |
185
|
|
|
if (!$field->name) { |
186
|
|
|
$field->name = '__blank__'; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if (!$field->validate()) { |
190
|
|
|
$metaField->hasFieldErrors = true; |
191
|
|
|
$validates = false; |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$contentService->fieldContext = $originalFieldContext; |
196
|
|
|
|
197
|
|
|
return $validates; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @inheritdoc |
202
|
|
|
*/ |
203
|
|
|
private function createContentTable($tableName) |
204
|
|
|
{ |
205
|
|
|
$migration = new ContentTable([ |
206
|
|
|
'tableName' => $tableName |
207
|
|
|
]); |
208
|
|
|
|
209
|
|
|
ob_start(); |
210
|
|
|
$migration->up(); |
211
|
|
|
ob_end_clean(); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
|
215
|
|
|
/******************************************* |
216
|
|
|
* HTML |
217
|
|
|
*******************************************/ |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param MetaField $field |
221
|
|
|
* @param $value |
222
|
|
|
* @param ElementInterface|null $element |
223
|
|
|
* @return string |
224
|
|
|
* @throws Exception |
225
|
|
|
* @throws \Twig_Error_Loader |
226
|
|
|
* @throws \yii\base\InvalidConfigException |
227
|
|
|
*/ |
228
|
|
|
public function getInputHtml(MetaField $field, $value, ElementInterface $element = null): string |
229
|
|
|
{ |
230
|
|
|
$id = Craft::$app->getView()->formatInputId($field->handle); |
231
|
|
|
|
232
|
|
|
// Get the field data |
233
|
|
|
$fieldInfo = $this->getFieldInfoForInput($field, $element); |
234
|
|
|
|
235
|
|
|
Craft::$app->getView()->registerAssetBundle(Input::class); |
236
|
|
|
|
237
|
|
|
Craft::$app->getView()->registerJs( |
238
|
|
|
'new Craft.MetaInput(' . |
239
|
|
|
'"' . Craft::$app->getView()->namespaceInputId($id) . '", ' . |
240
|
|
|
Json::encode($fieldInfo, JSON_UNESCAPED_UNICODE) . ', ' . |
241
|
|
|
'"' . Craft::$app->getView()->namespaceInputName($field->handle) . '", ' . |
242
|
|
|
($field->min ?: 'null') . ', ' . |
243
|
|
|
($field->max ?: 'null') . |
244
|
|
|
');' |
245
|
|
|
); |
246
|
|
|
|
247
|
|
|
Craft::$app->getView()->registerTranslations('meta', [ |
248
|
|
|
'Add new', |
249
|
|
|
'Add new above' |
250
|
|
|
]); |
251
|
|
|
|
252
|
|
|
if ($value instanceof MetaQuery) { |
253
|
|
|
$value |
254
|
|
|
->limit(null) |
255
|
|
|
->status(null) |
256
|
|
|
->enabledForSite(false); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return Craft::$app->getView()->renderTemplate( |
260
|
|
|
FieldHelper::TEMPLATE_PATH . '/input', |
261
|
|
|
[ |
262
|
|
|
'id' => $id, |
263
|
|
|
'name' => $field->handle, |
264
|
|
|
'field' => $field, |
265
|
|
|
'elements' => $value->all(), |
266
|
|
|
'static' => false, |
267
|
|
|
'template' => $field::DEFAULT_TEMPLATE |
268
|
|
|
] |
269
|
|
|
); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @inheritdoc |
274
|
|
|
*/ |
275
|
|
|
public function getSettingsHtml(MetaField $field) |
276
|
|
|
{ |
277
|
|
|
// Get the available field types data |
278
|
|
|
$fieldTypeInfo = $this->getFieldOptionsForConfiguration(); |
279
|
|
|
|
280
|
|
|
$view = Craft::$app->getView(); |
281
|
|
|
|
282
|
|
|
$view->registerAssetBundle(MetaSettingsAsset::class); |
283
|
|
|
$view->registerJs( |
284
|
|
|
'new Craft.MetaConfiguration(' . |
285
|
|
|
Json::encode($fieldTypeInfo, JSON_UNESCAPED_UNICODE) . ', ' . |
286
|
|
|
Json::encode(Craft::$app->getView()->getNamespace(), JSON_UNESCAPED_UNICODE) . |
287
|
|
|
');' |
288
|
|
|
); |
289
|
|
|
|
290
|
|
|
$view->registerTranslations('meta', [ |
291
|
|
|
'New field' |
292
|
|
|
]); |
293
|
|
|
|
294
|
|
|
$fieldTypeOptions = []; |
295
|
|
|
|
296
|
|
|
/** @var Field|string $class */ |
297
|
|
|
foreach (Craft::$app->getFields()->getAllFieldTypes() as $class) { |
298
|
|
|
$fieldTypeOptions[] = [ |
299
|
|
|
'value' => $class, |
300
|
|
|
'label' => $class::displayName() |
301
|
|
|
]; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// // Handle missing fields |
305
|
|
|
// $fields = $field->getFields(); |
306
|
|
|
// foreach ($fields as $i => $field) { |
307
|
|
|
// if ($field instanceof MissingField) { |
308
|
|
|
// $fields[$i] = $field->createFallback(PlainText::class); |
309
|
|
|
// $fields[$i]->addError('type', Craft::t('app', 'The field type “{type}” could not be found.', [ |
310
|
|
|
// 'type' => $field->expectedType |
311
|
|
|
// ])); |
312
|
|
|
// $field->hasFieldErrors = true; |
313
|
|
|
// } |
314
|
|
|
// } |
315
|
|
|
// $field->setFields($fields); |
316
|
|
|
|
317
|
|
|
return Craft::$app->getView()->renderTemplate( |
318
|
|
|
FieldHelper::TEMPLATE_PATH . '/settings', |
319
|
|
|
[ |
320
|
|
|
'field' => $field, |
321
|
|
|
'fieldTypes' => $fieldTypeOptions, |
322
|
|
|
'defaultTemplate' => $field::DEFAULT_TEMPLATE |
323
|
|
|
] |
324
|
|
|
); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* TODO - eliminate this and render configuration via ajax call |
329
|
|
|
* |
330
|
|
|
* Returns html for all associated field types for the Meta field input. |
331
|
|
|
* |
332
|
|
|
* @return array |
333
|
|
|
*/ |
334
|
|
|
private function getFieldInfoForInput(MetaField $field, ElementInterface $element = null): array |
335
|
|
|
{ |
336
|
|
|
// Set a temporary namespace for these |
337
|
|
|
$originalNamespace = Craft::$app->getView()->getNamespace(); |
338
|
|
|
$namespace = Craft::$app->getView()->namespaceInputName( |
339
|
|
|
$field->handle . '[__META__][fields]', |
340
|
|
|
$originalNamespace |
341
|
|
|
); |
342
|
|
|
Craft::$app->getView()->setNamespace($namespace); |
343
|
|
|
|
344
|
|
|
// Create a fake meta so the field types have a way to get at the owner element, if there is one |
345
|
|
|
$meta = new Meta(); |
346
|
|
|
$meta->fieldId = $field->id; |
|
|
|
|
347
|
|
|
|
348
|
|
|
if ($element) { |
349
|
|
|
$meta->setOwner($element); |
350
|
|
|
$meta->siteId = $element->siteId; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
$fieldLayoutFields = $field->getFieldLayout()->getFields(); |
354
|
|
|
|
355
|
|
|
// Set $_isFresh's |
356
|
|
|
foreach ($fieldLayoutFields as $field) { |
357
|
|
|
$field->setIsFresh(true); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
Craft::$app->getView()->startJsBuffer(); |
361
|
|
|
|
362
|
|
|
$bodyHtml = Craft::$app->getView()->namespaceInputs( |
363
|
|
|
Craft::$app->getView()->renderTemplate( |
364
|
|
|
'_includes/fields', |
365
|
|
|
[ |
366
|
|
|
'namespace' => null, |
367
|
|
|
'fields' => $fieldLayoutFields, |
368
|
|
|
'element' => $meta |
369
|
|
|
] |
370
|
|
|
) |
371
|
|
|
); |
372
|
|
|
|
373
|
|
|
// Reset $_isFresh's |
374
|
|
|
foreach ($fieldLayoutFields as $field) { |
375
|
|
|
$field->setIsFresh(null); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
$footHtml = Craft::$app->getView()->clearJsBuffer(); |
379
|
|
|
|
380
|
|
|
$fields = [ |
381
|
|
|
'bodyHtml' => $bodyHtml, |
382
|
|
|
'footHtml' => $footHtml, |
383
|
|
|
]; |
384
|
|
|
|
385
|
|
|
// Revert namespace |
386
|
|
|
Craft::$app->getView()->setNamespace($originalNamespace); |
387
|
|
|
|
388
|
|
|
return $fields; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* |
393
|
|
|
* TODO - eliminate this and render configuration via ajax call |
394
|
|
|
* |
395
|
|
|
* Returns info about each field type for the configurator. |
396
|
|
|
* |
397
|
|
|
* @return array |
398
|
|
|
*/ |
399
|
|
|
private function getFieldOptionsForConfiguration() |
400
|
|
|
{ |
401
|
|
|
$disallowedFields = [ |
402
|
|
|
MetaField::class, |
403
|
|
|
Matrix::class |
404
|
|
|
]; |
405
|
|
|
|
406
|
|
|
if (Craft::$app->getPlugins()->getPlugin('super-table')) { |
407
|
|
|
$disallowedFields[] = \verbb\supertable\fields\SuperTableField::class; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
$fieldTypes = []; |
411
|
|
|
|
412
|
|
|
// Set a temporary namespace for these |
413
|
|
|
$originalNamespace = Craft::$app->getView()->getNamespace(); |
414
|
|
|
$namespace = Craft::$app->getView()->namespaceInputName('fields[__META_FIELD__][settings]', $originalNamespace); |
415
|
|
|
Craft::$app->getView()->setNamespace($namespace); |
416
|
|
|
|
417
|
|
|
/** @var Field|string $class */ |
418
|
|
|
foreach (Craft::$app->getFields()->getAllFieldTypes() as $class) { |
419
|
|
|
// Ignore disallowed fields |
420
|
|
|
if (in_array($class, $disallowedFields)) { |
421
|
|
|
continue; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
Craft::$app->getView()->startJsBuffer(); |
425
|
|
|
|
426
|
|
|
/** @var FieldInterface $field */ |
427
|
|
|
$field = new $class(); |
428
|
|
|
|
429
|
|
|
if ($settingsHtml = (string)$field->getSettingsHtml()) { |
430
|
|
|
$settingsHtml = Craft::$app->getView()->namespaceInputs($settingsHtml); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
$settingsBodyHtml = $settingsHtml; |
434
|
|
|
$settingsFootHtml = Craft::$app->getView()->clearJsBuffer(); |
435
|
|
|
|
436
|
|
|
$fieldTypes[] = [ |
437
|
|
|
'type' => $class, |
438
|
|
|
'name' => $class::displayName(), |
439
|
|
|
'settingsBodyHtml' => $settingsBodyHtml, |
440
|
|
|
'settingsFootHtml' => $settingsFootHtml, |
441
|
|
|
]; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
Craft::$app->getView()->setNamespace($originalNamespace); |
445
|
|
|
|
446
|
|
|
return $fieldTypes; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* @param MetaField $metaField |
451
|
|
|
* @throws Exception |
452
|
|
|
* @throws \Throwable |
453
|
|
|
*/ |
454
|
|
|
private function deleteOldFields(MetaField $metaField) |
455
|
|
|
{ |
456
|
|
|
/** @var \craft\services\Fields $fieldsService */ |
457
|
|
|
$fieldsService = Craft::$app->getFields(); |
458
|
|
|
|
459
|
|
|
// Get existing fields |
460
|
|
|
$oldFields = $fieldsService->getAllFields(FieldHelper::getContextById($metaField->id)); |
461
|
|
|
$oldFieldsById = ArrayHelper::index($oldFields, 'id'); |
462
|
|
|
|
463
|
|
|
/** @var \craft\base\Field $field */ |
464
|
|
|
foreach ($metaField->getFieldLayout()->getFields() as $field) { |
465
|
|
|
if (!$field->getIsNew()) { |
466
|
|
|
ArrayHelper::remove($oldFieldsById, $field->id); |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// Drop the old fields |
471
|
|
|
foreach ($oldFieldsById as $field) { |
472
|
|
|
if (!$fieldsService->deleteField($field)) { |
473
|
|
|
throw new Exception(Craft::t('app', 'An error occurred while deleting this Meta field.')); |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
// Refresh the schema cache |
478
|
|
|
Craft::$app->getDb()->getSchema()->refresh(); |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* @param MetaField $metaField |
483
|
|
|
* @throws Exception |
484
|
|
|
* @throws \Throwable |
485
|
|
|
*/ |
486
|
|
|
private function saveNewFields(MetaField $metaField) |
487
|
|
|
{ |
488
|
|
|
$fieldLayoutFields = []; |
489
|
|
|
$sortOrder = 0; |
490
|
|
|
|
491
|
|
|
/** @var \craft\services\Fields $fieldsService */ |
492
|
|
|
$fieldsService = Craft::$app->getFields(); |
493
|
|
|
|
494
|
|
|
// Save field |
495
|
|
|
/** @var \craft\base\Field $field */ |
496
|
|
|
foreach ($metaField->getFieldLayout()->getFields() as $field) { |
497
|
|
|
// Save field (we validated earlier) |
498
|
|
|
if (!$fieldsService->saveField($field, false)) { |
499
|
|
|
throw new Exception('An error occurred while saving this Meta field.'); |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
// Set sort order |
503
|
|
|
$field->sortOrder = ++$sortOrder; |
504
|
|
|
|
505
|
|
|
$fieldLayoutFields[] = $field; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
$fieldLayout = $metaField->getFieldLayout(); |
509
|
|
|
|
510
|
|
|
$fieldLayoutTab = new FieldLayoutTab([ |
511
|
|
|
'name' => 'Fields', |
512
|
|
|
'sortOrder' => 1, |
513
|
|
|
'fields' => $fieldLayoutFields |
514
|
|
|
]); |
515
|
|
|
|
516
|
|
|
$fieldLayout->setTabs([$fieldLayoutTab]); |
517
|
|
|
$fieldLayout->setFields($fieldLayoutFields); |
518
|
|
|
|
519
|
|
|
$fieldsService->saveLayout($fieldLayout); |
520
|
|
|
|
521
|
|
|
// Update the element & record with our new field layout ID |
522
|
|
|
$metaField->setFieldLayout($fieldLayout); |
523
|
|
|
$metaField->fieldLayoutId = (int)$fieldLayout->id; |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* @param MetaField $metaField |
528
|
|
|
*/ |
529
|
|
|
private function ensureTable(MetaField $metaField) |
530
|
|
|
{ |
531
|
|
|
// Create the content table first since the element fields will need it |
532
|
|
|
$contentTable = FieldHelper::getContentTableName($metaField->id); |
533
|
|
|
|
534
|
|
|
// Do we need to create/rename the content table? |
535
|
|
|
if (!Craft::$app->getDb()->tableExists($contentTable)) { |
536
|
|
|
$this->createContentTable($contentTable); |
537
|
|
|
Craft::$app->getDb()->getSchema()->refresh(); |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.