8

I need to move some terms from one vocabulary to another by retaining all their data: translations, aliases, and node references.

Is it acceptable to change its vid column value directly in the database? The terms don't have hierarchy relationships which needs to be handled.

11
+50

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.

4

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.

  • A only problem i am seeing in this is - if in case, there are fields attached to a term with some values and you change its vid, then the data stored in the field will be lost. – Ashish Deynap Dec 16 '16 at 11:34
  • Yes, that's why I also mentioned that, if there are any nodes attached then those needs to be handled as well. – Yogesh Dec 16 '16 at 11:54
  • But does any data is attached to term by vid too? not only by tid key? – Molfar Dec 16 '16 at 15:03
  • No, any data attached to term is related to tid. vid is used to reference only vocabulary id, nothing else. – Yogesh Dec 17 '16 at 6:30
4

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).

  • You assume that views, VBO and rules are already installed, otherwise you need to install 3 modules for this solution. In addition the config "code" you need to implement is not less complex than what would be needed to do in an update hook. My point is that using rules and views is not as simple as you make it sound. It's also worth remembering that while you can make almost anything with Views and Rules, doing so is not always a good idea. – googletorp Dec 22 '16 at 15:47
  • 1
    Yes indeed: to use Views, Rules or VBO, the module has to be installed (+ enabled). But from the about 990K D7 sites, there are about 810K sites that also have Views, that's over 80%. And for sites that don't have a reason to use Rules or VBO: those modules are only needed to do such conversion (when done they can be removed if you want). About simplicity: IMO that's rather an experience issue. The advantage of this approach, is that it only requires site building skills (an experienced PHP developer may not be available / affordable). – Pierre.Vriens Dec 22 '16 at 16:04
2

I know that you say programmatically but if you want to use a module you can use Taxonomy Manager

This module provides a powerful interface for managing taxonomies. A vocabulary gets displayed in a dynamic tree view, where parent terms can be expanded to list their nested child terms or can be collapsed.

The Taxonomy Manager has following operations and key features:

  • dynamic treeview
  • mass deleting
  • mass adding of new terms
  • moving of terms in hierarchies merging of terms (using the Term merge module in 7.x)
  • fast weight changing with up and down arrows (and AJAX saving)
  • AJAX powered term editing form
  • simple search interface
  • CSV Export of terms
  • i18n support for multilingual vocabularies (per language terms)
  • Double Tree interface for moving terms in hierarchies, adding new translations and switching terms between different vocabularies

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.