Help migrating from jsonb to json_field

Created on 12 October 2023, 12 months ago
Updated 19 April 2024, 5 months ago

Problem/Motivation

Recently started the process of moving our site to Drupal 10 and discovered that jsonb was deprecated with this module identified as the appropriate alternative. However, it isn't clear how to migrate my existing json content into this module.

Is it as simple as an update hook that swaps out the module property in my field storage config and widget properties in my view and form displays? Or will there be some more complex storage adjustments involved?

Any pointers, tips, or potential gotchas would be greatly appreciated.

πŸ’¬ Support request
Status

Closed: outdated

Version

1.3

Component

Documentation

Created by

πŸ‡ΊπŸ‡ΈUnited States seth.e.shaw

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @seth.e.shaw
  • πŸ‡ΊπŸ‡ΈUnited States seth.e.shaw

    I should have also noted that we are running on MySQL 8.0.

  • πŸ‡ΊπŸ‡ΈUnited States oadaeh

    I created an update hook that uses guidance from this SE answer:
    https://drupal.stackexchange.com/questions/208946/how-do-i-change-a-fiel...

    It requires some manual set up operations, so I don't know that this could be included as a generic update for the module, but if someone wanted to explore that, this should help them quite a bit.

    Start with a backup and practice on a development copy of the site to convert. You will probably need to rebuild the site from the database backup a few times before you get your finished product.

    Follow the steps in that SE post.

    You need to modify the config of the fields, and that is done by deleting and recreating the fields, exporting them, then changing the UUIDs back to what the original fields were. What you are targeting is updating the config of the existing fields, not creating new ones, but you might need to manually delete and create them to know what config changes need to be made. You could also manually modify the files, if you know what needs to be changed.

    When you are done, you should have an update hook and modified config that will convert your site with a drush deploy command

    Here is my code, which differs slightly from the code in the post:

    function custom_module_update_1234() {
      $database = Drupal::database();
      $fields = [
        'node' => [
          'field_fieldname1',
        ],
        'block_content' => [
          'field_fieldname2',
          'field_fieldname3',
        ],
      ];
    
      foreach ($fields as $entity_type => $field_names) {
        foreach ($field_names as $field_name) {
          \Drupal::logger('custom_module_update')->debug('Converting ' . $entity_type . '__' . $field_name . ' from JSON/JSONB to JSON Field.');
          $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);
          if (is_null($field_storage)) {
            return;
          }
    
          // Include the revisions as well.
          $tables = [
            $entity_type . '__' . $field_name,
            $entity_type . '_revision__' . $field_name,
          ];
    
          $rows = [];
          foreach ($tables as $table) {
            $rows[$table] = NULL;
          }
    
          // Store the data to restore after the update is completed.
          foreach ($tables as $table) {
            if ($database->schema()->tableExists($table)) {
              $rows[$table] = $database->select($table, 'e')
                ->fields('e')
                ->execute()
                ->fetchAll();
            }
          }
    
          $new_fields = [];
    
          // Use existing field config for new field.
          foreach ($field_storage->getBundles() as $bundle => $label) {
            $field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
            $new_field = $field->toArray();
            $new_field['field_type'] = 'json_native';
            $new_field['settings'] = [];
    
            $new_fields[] = $new_field;
          }
    
          // Deleting field storage which will also delete bundles(fields).
          $new_field_storage = $field_storage->toArray();
          $new_field_storage['type'] = 'json_native';
    
          $field_storage->delete();
    
          // Purge field data now to allow new field and field_storage with same name
          // to be created. (The batch size might need to be increased.)
          field_purge_batch(10);
    
          // Create new field storage.
          $new_field_storage = FieldStorageConfig::create($new_field_storage);
          $new_field_storage->save();
    
          // Create new fields.
          foreach ($new_fields as $new_field) {
            $new_field = FieldConfig::create($new_field);
            $new_field->save();
          }
    
          // Restore existing data in the same table.
          foreach ($tables as $table) {
            if (!is_null($rows[$table])) {
              foreach ($rows[$table] as $row) {
                $database->insert($table)
                  ->fields((array)$row)
                  ->execute();
              }
            }
          }
        }
      }
    }
    
  • Status changed to Closed: outdated 5 months ago
  • πŸ‡ΊπŸ‡ΈUnited States seth.e.shaw

    I should have come back and posted what I ended up doing...

    These were the steps taken:

    1. Install json_field: composer require drupal/json_field
    2. Enable the module: drush {site url} en -y json_field
    3. Clear cache: drush {site url} cr
    4. Run the update script (below): drush scr ~/update.php
    5. Uninstall jsonb: drush pm:uninstall -y jsonb
    6. Remove jsonb: composer remove drupal/jsonb

    Update script:

    <?php
    
    /**
     * @file
     */
    
    if (!$field_storage_configs = \Drupal::entityTypeManager()->getStorage('field_storage_config')->loadByProperties(['type' => 'json'])) {
      return;
    }
    
    foreach ($field_storage_configs as $field_storage) {
      $new_field_storage = $field_storage->toArray();
      $new_field_storage['type'] = 'json_native';
      $new_field_storage['module'] = 'json_field';
    
      $new_field_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config')->create($new_field_storage);
      $new_field_storage->original = $new_field_storage;
      $new_field_storage->enforceIsNew(FALSE);
    
      $new_field_storage->save();
    
      $field_name = $field_storage->getName();
      if (!$fields = \Drupal::entityTypeManager()->getStorage('field_config')->loadByProperties(['field_name' => $field_name])) {
        continue;
      }
    
      foreach ($fields as $field) {
        $new_field = $field->toArray();
        $new_field['field_type'] = 'json_native';
        $new_field['dependencies']['module'] = 'json_field';
    
        $new_field = \Drupal::entityTypeManager()->getStorage('field_config')->create($new_field);
        $new_field->original = $field;
        $new_field->enforceIsNew(FALSE);
        $new_field->save();
    
        // Update view displays.
        $properties = [
          'targetEntityType' => 'node',
          'bundle' => 'asu_repository_item',
        ];
        if ($view_displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties($properties)) {
          foreach ($view_displays as $view_display) {
            if ($component = $view_display->getComponent($field_name)) {
              $view_display->setComponent($field_name, [
                'type' => 'json',
              ] + $component);
              $view_display->calculateDependencies();
              $view_display->save();
            }
          }
        }
    
        // Update form displays.
        if ($form_displays = \Drupal::entityTypeManager()->getStorage('entity_form_display')->loadByProperties($properties)) {
          foreach ($form_displays as $form_display) {
            if ($component = $form_display->getComponent($field_name)) {
              $form_display->setComponent($field_name, [
                'type' => 'json_textarea',
              ] + $component);
              $form_display->calculateDependencies();
              $form_display->save();
            }
          }
        }
    
      }
    }
Production build 0.71.5 2024