Skip to content

Commit f4ac188

Browse files
authored
[4.0] Migrate data for repeatable fields (#32611)
1 parent 6d1601b commit f4ac188

File tree

1 file changed

+88
-19
lines changed

1 file changed

+88
-19
lines changed

administrator/components/com_admin/script.php

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ protected function updateDatabaseMysql()
236236
protected function uninstallRepeatableFieldsPlugin()
237237
{
238238
$app = Factory::getApplication();
239-
$db = Factory::getDbo();
239+
$db = Factory::getDbo();
240240

241241
// Check if the plg_fields_repeatable plugin is present
242242
$extensionId = $db->setQuery(
@@ -285,10 +285,16 @@ protected function uninstallRepeatableFieldsPlugin()
285285
* holds the `fieldparams` of the `repeatable` type, $newFieldparams shall hold the `fieldparams`
286286
* of the `subfields` type.
287287
*/
288-
$newFieldparams = array(
288+
$newFieldparams = [
289289
'repeat' => '1',
290-
'options' => array(),
291-
);
290+
'options' => [],
291+
];
292+
293+
/**
294+
* This array is used to store the mapping between the name of form fields from Repeatable field
295+
* with ID of the child-fields. It will then be used to migrate data later
296+
*/
297+
$mapping = [];
292298

293299
// If this repeatable fields actually had child-fields (normally this is always the case)
294300
if (isset($oldFieldparams->fields) && is_object($oldFieldparams->fields))
@@ -311,24 +317,24 @@ protected function uninstallRepeatableFieldsPlugin()
311317
* of the `repeatable` instance. This is what we use $data for, we create a new custom field
312318
* for each of the sub fields of the `repeatable` instance.
313319
*/
314-
$data = array(
315-
'context' => $row->context,
316-
'group_id' => $row->group_id,
317-
'title' => $oldField->fieldname,
318-
'name' => (
320+
$data = [
321+
'context' => $row->context,
322+
'group_id' => $row->group_id,
323+
'title' => $oldField->fieldname,
324+
'name' => (
319325
$fieldname_prefix
320326
. $oldField->fieldname
321327
. ($fieldname_suffix > 0 ? ('_' . $fieldname_suffix) : '')
322328
),
323-
'label' => $oldField->fieldname,
324-
'default_value' => $row->default_value,
325-
'type' => $oldField->fieldtype,
326-
'description' => $row->description,
327-
'state' => '1',
328-
'params' => $row->params,
329-
'language' => '*',
329+
'label' => $oldField->fieldname,
330+
'default_value' => $row->default_value,
331+
'type' => $oldField->fieldtype,
332+
'description' => $row->description,
333+
'state' => '1',
334+
'params' => $row->params,
335+
'language' => '*',
330336
'assigned_cat_ids' => [-1],
331-
);
337+
];
332338

333339
// `number` is not a valid custom field type, so use `text` instead.
334340
if ($data['type'] == 'number')
@@ -381,12 +387,14 @@ protected function uninstallRepeatableFieldsPlugin()
381387
}
382388

383389
// And tell our new `subfields` field about his child
384-
$newFieldparams['options'][('option' . $newFieldCount)] = array(
390+
$newFieldparams['options'][('option' . $newFieldCount)] = [
385391
'customfield' => $subfield_id,
386392
'render_values' => '1',
387-
);
393+
];
388394

389395
$newFieldCount++;
396+
397+
$mapping[$oldField->fieldname] = 'field' . $subfield_id;
390398
}
391399
}
392400

@@ -398,6 +406,67 @@ protected function uninstallRepeatableFieldsPlugin()
398406
->set($db->quoteName('fieldparams') . ' = ' . $db->quote(json_encode($newFieldparams)))
399407
->where($db->quoteName('id') . ' = ' . $db->quote($row->id))
400408
)->execute();
409+
410+
// Migrate data for this field
411+
$query = $db->getQuery(true)
412+
->select('*')
413+
->from($db->quoteName('#__fields_values'))
414+
->where($db->quoteName('field_id') . ' = ' . $row->id);
415+
$db->setQuery($query);
416+
417+
foreach ($db->loadObjectList() as $rowFieldValue)
418+
{
419+
// Do not do the version if no data is entered for the custom field this item
420+
if (!$rowFieldValue->value)
421+
{
422+
continue;
423+
}
424+
425+
/**
426+
* Here we will have to update the stored value of the field to new format
427+
* The key for each row changes from repeatable to row, for example repeatable0 to row0, and so on
428+
* The key for each sub-field change from name of field to field + ID of the new sub-field
429+
* Example data format stored in J3: {"repeatable0":{"id":"1","username":"admin"}}
430+
* Example data format stored in J4: {"row0":{"field1":"1","field2":"admin"}}
431+
*/
432+
$newFieldValue = [];
433+
434+
// Convert to array to change key
435+
$fieldValue = json_decode($rowFieldValue->value, true);
436+
437+
// If data could not be decoded for some reason, ignore
438+
if (!$fieldValue)
439+
{
440+
continue;
441+
}
442+
443+
foreach ($fieldValue as $rowKey => $rowValue)
444+
{
445+
$rowKey = str_replace('repeatable', 'row', $rowKey);
446+
$newFieldValue[$rowKey] = [];
447+
448+
foreach ($rowValue as $subFieldName => $subFieldValue)
449+
{
450+
if (isset($mapping[$subFieldName]))
451+
{
452+
$newFieldValue[$rowKey][$mapping[$subFieldName]] = $subFieldValue;
453+
}
454+
else
455+
{
456+
// Not found, use the old key to avoid data lost
457+
$newFieldValue[$subFieldName] = $subFieldValue;
458+
}
459+
}
460+
}
461+
462+
$query->clear()
463+
->update($db->quoteName('#__fields_values'))
464+
->set($db->quoteName('value') . ' = ' . $db->quote(json_encode($newFieldValue)))
465+
->where($db->quoteName('field_id') . ' = ' . $rowFieldValue->field_id)
466+
->where($db->quoteName('item_id') . ' =' . $rowFieldValue->item_id);
467+
$db->setQuery($query)
468+
->execute();
469+
}
401470
}
402471

403472
// Now, unprotect the plugin so we can uninstall it

0 commit comments

Comments
 (0)