Drupal - Is it acceptable to change the vid column of a term programmatically in the database?

Database

It's possible to do a lot of things in Drupal simply by making SQL queries, either through Drupal or externally. Generally, you never want to take this approach. There are some cases where it can work just fine, but most of the time there is no reason to do it this way.

API

Drupal has a rich API, this is true for Drupal 6, 7 and 8, as they has always been a key feature in Drupal. In your concreate example, you could use taxonomy_term_load and taxonomy_term_save to facilitate an update of a term. Doing it this way, you can edit any data part including the vid. Just because you do it with API's doing forbidden things wont just automatically work, but the chance of things going well is drastically improved.

In this concrete example the API doesn't do anything that is necessarily needed. It sets some internal data on the term and invoke hooks letter module know that is has been updated.

You should note that things can break, if the term you want to change is part of a hierarchy. Things can also break for nodes referencing the term, if the field is not allowed to reference terms in the new vocabulary.

Migration

Migration of data is the bullet proof solution, and unless you have a huge data set, it can be developed and executed pretty easily. The idea is to create a new term and migrate the content you want to migrate and then delete the old term. As an update hook, sample code could look like this:

/**
 * Put in modules .install file, replace xxxx with 7000 or higher
 */
function MODULE_NAME_update_XXXX(&$sandbox) {
  $term = taxonomy_term_load(CONSTANT_WITH_TID);
  $new_term = clone $term;
  unset($new_term->tid);
  unset($new_term->tid);
  $new_term->vid = CONSTANT_WITH_VID;
  taxonomy_term_save($term);
  // Find all nodes we want to update and update them.
  // If there is a lot of nodes, can use $sandbox to make this a batch job.
  $query = new EntityFieldQuery();
  $query->entityCondition('entity_type', 'node')
    ->fieldCondition('field_which_is_term_reference', 'tid', $term->tid);
  $result = $query->execute();
  foreach (node_load_multiple(array_keys($result['node'])) as $node) {
    $node->field_which_is_term_reference[LANGUAGE_NONE][0]['tid'] = $term->tid;
    node_save($node);
  }
  // Migration all done - delete the old term.
  taxonomy_term_delete($term->tid);
}

Please note that the above code is pure example code, written by heart. Might have syntax error or other errors and makes a lot of assumptions. This is just to illustrate an idea and demonstrate that a migration might not be a big deal.


It is not advisable to do direct database changes when working with Drupal. But Yes, if we know where all it can impact and do necessary changes accordingly, it is OK to do direct changes in database. Because in this case this is not possible through UI. NOTE: If you've nodes associated with Term, you'll have to manually handle that as well.

Check out this link that explains how we can change the term vocabulary in Drupal 7: Change the vocabulary of a taxonomy term in Drupal 7 using the database.


I would not recommend to change that term the way you described it in your question. Instead I'd use an alternative approach to achieve a similar result (in the order specified), which is further detailed below.

Step 1 - Start using the new term in future node updates

Create a new taxonomy term field, so that "from now on" any future node updates (or new nodes being created) will use that new field. I assume these terms are used for nodes (if you use it for some other entity type, like users, etc), the same approach can be used for those entities also.

Use the Rules module to create a rule like so:

  • Rules event: before saving content.
  • Rules conditions:
    • entity has field, with field = the old field.
    • AND NOT (entity has field, with field = the new field).
  • Rules Action: set Drupal message, which contains some instructions that the old field must be blanked out, and the new field should contain the appropriate value(s).

Step 2 - Use Rules to speed up the process

Obviously, the approach in Step 1 will take "some" time if this has to be done manually, 1 node at a time. But using Views (to build a list of similar nodes to be updated) and VBO (to mass update such lists) you might (should!) be able to speed up this process quite a bit.

Especially if you'd use Rules to create a custom bulk operation for such VBO view, as explained in the answer to "How to use Rules to create a custom bulk operation for a VBO view?". Here is a prototype of a Rules Component that should help to implement such custom bulk operation (in Rules Export format):

{ "rules_replace_a_term_field_by_another_term_field" : {
    "LABEL" : "Replace a term field by another term field",
    "PLUGIN" : "rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules" ],
    "USES VARIABLES" : { "node" : { "label" : "Node", "type" : "node" } },
    "IF" : [
      { "entity_has_field" : { "entity" : [ "node" ], "field" : "field_sample_tags" } },
      { "entity_has_field" : { "entity" : [ "node" ], "field" : "field_demo_tags" } },
      { "data_is" : { "data" : [ "node:field-demo-tags" ], "value" : "1" } }
    ],
    "DO" : [
      { "data_set" : { "data" : [ "node:field-sample-tags" ], "value" : "31" } },
      { "drupal_message" : { "message" : "Term updated in node with id = [node:nid]" } }
    ]
  }
}

Some more details to explain the above prototype:

  • It uses a node as a parameter.
  • These are the Rules Conditions:

    • check if the entity (= node) has a field field_sample_tags.
    • check if the entity (= node) has a field field_demo_tags.
    • check if the value of the field field_demo_tags corresponds to the term we want to replace (in this example, the term has id = 1). Note that if this condition is not satisfied, no Rules Actions will be performed.
  • These are the Rules Actions:

    • Set the value of field field_sample_tags equal to the term with term id = 31 (which is the term in the newly created vocabulary that matches the term in the vocabulary that is to be replaced).
    • Display a message on the site like Term updated in node with id = 72, whereas 72 is the node id of the updated node.

If you want, adapt the machine names of the field names in the above prototype, and the used term IDs. Then import it in your own site (using the Rules UI), and QA-test it using the "execute" link to the right of the imported Rules Component (and enter some node id to test it, after you switch to "direct input mode" to be able to specify a node id). If during your testing you don't get such a Term updated in node ... message, it must be because the node you selected did not use the term value specified in your rules Condition.

Step 3 - Use VBO as the finishing touch

After you're finished QA-testing this Rules Component from Step 2, create a VBO-view of nodes to be processed, in which you refer to the Rules prototype above (or a variation of it to fit your needs).

Benefit of this approach

Using this approach, you minimize the risk to introduce data inconsistencies (as compared to updating the database directly), with zero custom code involved (you'd only use the Views UI and Rules UI).