yorkshirepudding's picture

Has anyone got any experience with switching bundle types of entities, whether in D7 or B?  My particular use case is a desire to switch paragraph types.  I'm open to doing in the database as this is not something I plan to do much of but don't want to break anything; from what I can tell I need to change the bundle in `paragraphs_item` (for each item) and  `field_data_field_name` and `field_revision_field_name` for each field within the item; is that right?.  I note there is https://www.drupal.org/project/bundleswitcher for D7, https://github.com/backdrop-contrib/node_convert for B and https://www.drupal.org/project/convert_bundles for D8+ but nothing for B that will directly do things.

Most helpful answers

Hi @yorkshirepudding. 

As you say, I don't think there is a contrib module available for Backdrop to do this, and yes, it's a process that can break things if not done carefully (especially if you have other contrib modules that create satellite tables or config files around your fields or your entity bundles).

Your steps also are somewhat incomplete (off the top of my head):

You have two options. Option 1 ("cloning" - produces new entity IDs): you can create new entities (e.g. clone nodes), or Option 2 ("switching" bundles - keeps the original entity IDs): you can modify the existing entries in several databases to update the bundle. Option 2 is a bit easier, but has the problem that you may lose all your original entities if something goes wrong (you are not cloning them)

  • For Option 2 ("switching"): In the table that stores your entities (e.g. "node" for Content entities), you have to change the value in the column currently used for storing the bundle. This could vary according to entity type. For example, in the table "node",  Backdrop uses the column called "type" to store the bundle of each node (e.g. "page", "post"). Paragraph Items (table paragraph_items) uses the column name "bundle". This name, by the way, is defined in hook_entity_info(), under the keys ['entity keys']['bundle'] (also available by running entity_get_info(''ENTITY_TYPE');)
  • For both Option 1 and 2, if your entity uses a way to store the definitions of entity bundles (all bundeable entities typically do), you need to be sure that your new bundle has been properly defined and stored. For example, Content Type definitions are stored as json files in "node.type.TYPE.json". This happens to be also the case with Paragraph Items bundle definitions, stored in with the same pattern of file name. BUT other custom entities may actually store those bundle definitions in the database. E.g. in Organic Groups, the og_membership_type table stores OG membership types bundle definitions, so if you want to switch bundles for OG membership entities, you need to be sure that the new OG membership type definition exists in the db. If you are doing all of this programmatically, you must create your bundle definition programmatically as well. If you are doing this manually, you can create your bundle through the UI (e.g. Structure > Paragraph types)
  • For both Option 1 and 2: Something you don't list above are field instance definitions. In Backdrop those are stored as config file, with the pattern field.instance.ENTITY_TYPE.BUNDLE_NAME.FIELD_NAME.json. So, in order for your switch to work with fields, you need to be sure to create those field instance definition files (most likely cloning the ones for the source bundle to the target bundle)
  • For both Option 1 and 2: In addition, as you list above, all tables storing the data of fields attached to an entity bundle must be either cloned (for Option 1) or modified (for Option 2). In particulars, for Option 2 ("switching") the "bundle" column for field data for those source entities must be switch to the new bundle name (see for example the column "bundle" in the table field_data_body that stores data for the "bundle" field). If you are using Option 1 ("cloning"), you must clone the entries in the field data tables, and be sure the cloned entries have the new bundle AND the correct entity ID 
  • For Option 1: If your entities use revisions, and if you want to preserve your revisions after cloning, you'll also need to create new entries in your entity revision table . For example, in table node_revision you have to find the nid of the source node, and clone those with the new nid of the switched node after saving it. 
  • And finally, if you also have field revision data stored in the db (e.g. field_revision_body table), those need to be adjusted as above

I think that covers it, but I may be forgetting stuff related to languages... which I haven't dealt with much.

   

Comments

Hi @yorkshirepudding. 

As you say, I don't think there is a contrib module available for Backdrop to do this, and yes, it's a process that can break things if not done carefully (especially if you have other contrib modules that create satellite tables or config files around your fields or your entity bundles).

Your steps also are somewhat incomplete (off the top of my head):

You have two options. Option 1 ("cloning" - produces new entity IDs): you can create new entities (e.g. clone nodes), or Option 2 ("switching" bundles - keeps the original entity IDs): you can modify the existing entries in several databases to update the bundle. Option 2 is a bit easier, but has the problem that you may lose all your original entities if something goes wrong (you are not cloning them)

  • For Option 2 ("switching"): In the table that stores your entities (e.g. "node" for Content entities), you have to change the value in the column currently used for storing the bundle. This could vary according to entity type. For example, in the table "node",  Backdrop uses the column called "type" to store the bundle of each node (e.g. "page", "post"). Paragraph Items (table paragraph_items) uses the column name "bundle". This name, by the way, is defined in hook_entity_info(), under the keys ['entity keys']['bundle'] (also available by running entity_get_info(''ENTITY_TYPE');)
  • For both Option 1 and 2, if your entity uses a way to store the definitions of entity bundles (all bundeable entities typically do), you need to be sure that your new bundle has been properly defined and stored. For example, Content Type definitions are stored as json files in "node.type.TYPE.json". This happens to be also the case with Paragraph Items bundle definitions, stored in with the same pattern of file name. BUT other custom entities may actually store those bundle definitions in the database. E.g. in Organic Groups, the og_membership_type table stores OG membership types bundle definitions, so if you want to switch bundles for OG membership entities, you need to be sure that the new OG membership type definition exists in the db. If you are doing all of this programmatically, you must create your bundle definition programmatically as well. If you are doing this manually, you can create your bundle through the UI (e.g. Structure > Paragraph types)
  • For both Option 1 and 2: Something you don't list above are field instance definitions. In Backdrop those are stored as config file, with the pattern field.instance.ENTITY_TYPE.BUNDLE_NAME.FIELD_NAME.json. So, in order for your switch to work with fields, you need to be sure to create those field instance definition files (most likely cloning the ones for the source bundle to the target bundle)
  • For both Option 1 and 2: In addition, as you list above, all tables storing the data of fields attached to an entity bundle must be either cloned (for Option 1) or modified (for Option 2). In particulars, for Option 2 ("switching") the "bundle" column for field data for those source entities must be switch to the new bundle name (see for example the column "bundle" in the table field_data_body that stores data for the "bundle" field). If you are using Option 1 ("cloning"), you must clone the entries in the field data tables, and be sure the cloned entries have the new bundle AND the correct entity ID 
  • For Option 1: If your entities use revisions, and if you want to preserve your revisions after cloning, you'll also need to create new entries in your entity revision table . For example, in table node_revision you have to find the nid of the source node, and clone those with the new nid of the switched node after saving it. 
  • And finally, if you also have field revision data stored in the db (e.g. field_revision_body table), those need to be adjusted as above

I think that covers it, but I may be forgetting stuff related to languages... which I haven't dealt with much.

   

Thanks @argiepiano - that is really comprehensive though I think my use case is much simpler; the paragraph types are already defined and share mostly similar fields, in some cases identical fields, just different ways of displaying.

As an example, I might have a text and title paragraph type and I want to move it into a text, title and image paragraph type so I can then add the image, but without the manual copying and pasting to a new paragraph item before deleting the old.

Another example is where the fields are the same, but one is intended to be displayed almost normally, using a template to arrange fields, and one is displayed as an accordion item.

In both those scenarios, the field instance config is unaffected as I'm just moving content from one bundle to another with the same fields.

I'm leaning towards option 2 and writing a series of chained SQL statements to update the bundle for listed paragraphs_item entity ids in a parameter to new bundle (also a parameter).  In the examples above, I think I would just need to do on paragraphs_item table, the field_data_field_name tables and the field_revision_field_name tables.

Most of my sites are single language so I don't need to worry too much about those.

As an example, I might have a text and title paragraph type and I want to move it into a text, title and image paragraph type so I can then add the image, but without the manual copying and pasting to a new paragraph item before deleting the old.

Wouldn't it be easier to add the image field to the existing paragraph type?

Another example is where the fields are the same, but one is intended to be displayed almost normally, using a template to arrange fields, and one is displayed as an accordion item.

For this, instead of moving to a new bundle, it may be possible to create a new Display type (under Manage display in the Paragraph type UI) and then using different templates for the different display types (you may need to create add the new template suggestions manually by using a preprocess function in template.php).

In both those scenarios, the field instance config is unaffected as I'm just moving content from one bundle to another with the same fields.

Right. This will only be the case if those paragraph types have already been created and the fields already added. 

 

One additional definition file I forgot to include above: field.bundle.ENTITY_TYPE.BUNDLE_NAME.json

Those would need to be created for the new bundle as well.