3

This is a followup of my question at How to create a rule to combine the terms of 2 fields to create a new term and store it in a 3rd field?.

How can I prevent that each time a node is viewed, it creates duplicate taxonomy terms, as shown in this example:

enter image description here

Any suggestions?

1
  • @Pierre.Vriens Sorry, because our language problems, my description is not accurate, you said I can only understand a part, because I really do not know how to correctly describe my problem, so the thought of making such a picture, I hope you can understand!
    – LIUJIAHUI
    Commented Dec 7, 2016 at 13:34

2 Answers 2

2

Part 1 - About the actual challenge

I'm assuming you're using a rule that looks similar (equal?) to the rule in the accepted answer in that related question, which looks like so:

{ "rules_improved_combination_of_taxonomy_terms" : {
    "LABEL" : "Improved combination of taxonomy terms",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules" ],
    "ON" : { "node_view--article" : { "bundle" : "article" } },
    "DO" : [
      { "drupal_message" : { "message" : "[node:field-a][node:field-b]" } },
      { "variable_add" : {
          "USING" : { "type" : "text", "value" : "[node:field-a][node:field-b]" },
          "PROVIDE" : { "variable_added" : { "variable_added" : "Added variable" } }
        }
      },
      { "entity_create" : {
          "USING" : {
            "type" : "taxonomy_term",
            "param_name" : "[variable-added:value]",
            "param_vocabulary" : "tags"
          },
          "PROVIDE" : { "entity_created" : { "entity_created" : "Created entity" } }
        }
      },
      { "entity_save" : { "data" : [ "entity-created" ], "immediate" : "1" } },
      { "data_set" : { "data" : [ "node:field-c" ], "value" : [ "entity-created" ] } },
      { "entity_save" : { "data" : [ "node:field-c" ], "immediate" : "1" } }
    ]
  }
}

The above rule does not perform any verification if the term [node:field-a][node:field-b] (= the value that gets stored in "field C") already exists. Instead each time the Rules Actions are triggered (ie each time "an Article is viewed"), it unconditionally creates that term (which may already exist) another time. So that is why you get all those duplicate terms (like helloworld in your example).

The solution (fix) for that is that you add some extra logic in your rule to only perform the 3rd (= entity_create) and 4th (= entity_save of the created term) Rules Action so that those 2 Rules Actions are only performed if the term does not exist yet.

There are basically 2 solutions/approaches to implement such exta logic:

  • Use the the Conditional Rules module, which allows to include the equivalent of a Rules Condition within a Rules Action.
  • Move those 2 Rules Actions to an appropriate (new) Rules Component, in which you can add the required Rules Conditions. And then in your rule you just call (perform) that Rules Component.

The advantage of the first solution is that the entire logic is contained in a single Rule, though (disadvantage) it requires yet another module.

With either of both solutions, you basically improve this rule with something like "If the term for field C does not exist yet, then let's quickly create it now, so that we can use that term as the value for field C".

Easy enough, no? ... All you need to know is how to actually perform a check in Rules to verify "If that term is already contained in the list of terms of your taxonomy".

Part 2 - Solution using Conditional Rules

Below is a possible solution using Conditional Rules. The improved rule looks like so:

{ "rules_combine_taxonomy_terms_without_creating_duplicates" : {
    "LABEL" : "Combine taxonomy terms without creating duplicates",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules", "rules_conditional" ],
    "ON" : { "node_view--article" : { "bundle" : "article" } },
    "DO" : [
      { "drupal_message" : { "message" : "Value of term for field C will be set to: \u003Cstrong\u003E[node:field-a][node:field-b]\u003C\/strong\u003E." } },
      { "variable_add" : {
          "USING" : { "type" : "text", "value" : "[node:field-a][node:field-b]" },
          "PROVIDE" : { "variable_added" : { "combined_term" : "Combined Term" } }
        }
      },
      { "entity_query" : {
          "USING" : {
            "type" : "taxonomy_term",
            "property" : "vocabulary",
            "value" : [ "node:field-a:vocabulary" ]
          },
          "PROVIDE" : { "entity_fetched" : { "existing_terms" : "Existing Terms" } }
        }
      },
      { "variable_add" : {
          "USING" : { "type" : "boolean", "value" : "0" },
          "PROVIDE" : { "variable_added" : { "new_term_exist" : "New term exists" } }
        }
      },
      { "LOOP" : {
          "USING" : { "list" : [ "existing-terms" ] },
          "ITEM" : { "current_term" : "Current term" },
          "DO" : [
            { "CONDITIONAL" : [
                {
                  "IF" : { "text_matches" : { "text" : [ "current-term:name" ], "match" : "[combined-term:value]" } },
                  "DO" : [
                    { "data_set" : { "data" : [ "new-term-exist" ], "value" : "1" } },
                    { "data_set" : { "data" : [ "node:field-c" ], "value" : [ "current-term" ] } },
                    { "drupal_message" : { "message" : "Term \u003Cstrong\u003E[node:field-a][node:field-b]\u003C\/strong\u003E already exists (no need to create it now)." } }
                  ]
                }
              ]
            }
          ]
        }
      },
      { "CONDITIONAL" : [
          {
            "IF" : { "data_is" : { "data" : [ "new-term-exist" ], "value" : "0" } },
            "DO" : [
              { "entity_create" : {
                  "USING" : {
                    "type" : "taxonomy_term",
                    "param_name" : "[combined-term:value]",
                    "param_vocabulary" : "tags"
                  },
                  "PROVIDE" : { "entity_created" : { "term_created" : "Created term" } }
                }
              },
              { "entity_save" : { "data" : [ "term-created" ], "immediate" : "1" } },
              { "data_set" : { "data" : [ "node:field-c" ], "value" : [ "term-created" ] } },
              { "drupal_message" : { "message" : "Term \u003Cstrong\u003E[node:field-a][node:field-b]\u003C\/strong\u003E did not yet exist and was created just now." } }
            ]
          }
        ]
      },
      { "entity_save" : { "data" : [ "node:field-c" ], "immediate" : "1" } },
      { "drupal_message" : { "message" : "\u003Cstrong\u003ENote\u003C\/strong\u003E: After updating field A or B, a page refresh is needed to actually SEE a value of field C = \u003Cstrong\u003E[node:field-a][node:field-b]\u003C\/strong\u003E." } }
    ]
  }
}

Even though it's not an obvious rule to digest (I think), it mostly does these things:

  • Perform an entity_query to create a list of existing terms (stored in existing_terms), for the vocabulary used in Field A (as per the node:field-a:vocabulary in that entity_query). So the assumption is that Field A and Field C use the same vocabulary (there are no details about that in the question). If that's not the case, just replace that vocabulary with whatever fits, or maybe just hardcode it.
  • Calculate a boolean variable with machine name new_term_exist, which verifies if the term to be saved in Field C already exists.
  • Set the value for field C to an existing term (if it already exist).
  • Create the term for field C on the fly (if it does not yet exist) and then use that to set the value for field C.

However, there is 1 minor detail to be aware of, as detailed in the message of the very last Rules Action ... For anybody interested in addressing that (to make that message obsolete), please post an extra answer and feel free to use this rule here to start from.

Note: even though, in theory, you should also be able to use Rules Components instead of Conditional Rules (refer to the answer to "What's the alternative to using the Conditional Rules module?" for more details on that), the key challenge with that is that you run in a tough-to-debug issue when passing values to such Rules Components, in the cases where the value of Field C is still blank (eg when creating a new node).

PS: Q: How do you eat an elephant? A: 1 bite at a time ... (though there is only 1 bite left of it now).

4
  • Comments have been removed. Please don't use them for extended discussion.
    – avpaderno
    Commented Dec 6, 2016 at 12:34
  • Have been testing,But the first condition is not always effective, Repeat create term.
    – LIUJIAHUI
    Commented Dec 6, 2016 at 23:52
  • @LIUJIAHUI I don't understand your last comment. Please EDIT your question by adding "update" to the end, followed by something like "I tried the rule in the answer below, but the results I get are like shown in the screenprint I added below now". Commented Dec 7, 2016 at 8:09
  • @Pierre.Vriens if is not effective, I really do not know how to change the problem.
    – LIUJIAHUI
    Commented Dec 7, 2016 at 11:48
3

I think the Rules condition, URL Alias Exists is perfect for this scenario if put into two rules, one for yes and one for no. Taxonomy terms will have an alias once created so it's easy to check if /taxonomy_vocabulary/[A][B] exists before doing anything with C.

When the node is created or updated, the rules check for A and B added together to see if that AB taxonomy term already exists since every taxonomy term has an alias. If it already exists (yes), the rule fetches it and uses it and if not (no), it creates it.

For example, the first time a node is created or updated with A: New and B: York, "/cities/newyork" wouldn't exist so we create it. The next time we use A: New and B: York, the rule finds the existing term and fetches it.

The below assumes a single-language taxonomy vocabulary. The nodes can be multi-lingual.

PSUEDO-RULES

Rule 1 - Taxonomy C exists
Events: Node is saved / Node is updated
Condition: URL Alias Exists > Use replacement patterns to join A and B in Value field
              eg. taxonomy/[a][b]
Action: Create variable > [a][b] 
Action: Fetch Entity by Property > Taxonomy Term > Name > Created Variable
Action: Set Data Value > C: Fetched Entity:0

Rule 2 - Taxonomy C doesn't exist yet
Events: Node is saved / Node is updated
Condition: NOT URL Alias Exists > Use replacement patterns to join A and B in Value field
Action: Create entity > Taxonomy Term > "AB" using replacement patterns 
              (not the drop-down list!) 
Action: Set Data Value > C: Created Entity

Rules exports
To test these rules, you'll need a Drupal install with Articles, Tags and no Pathauto messing with the urls.
Add three term reference fields to Article for the Tags Vocabulary.

Machine Names
field_tax_tags_a 
field_tax_tags_b 
field_tax_tags_c

Rule 1 - AB Term exists so fetch it

{ "rules_exists" : {
    "LABEL" : "Exists",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules", "path" ],
    "ON" : {
      "node_insert--article" : { "bundle" : "article" },
      "node_update--article" : { "bundle" : "article" }
    },
    "IF" : [
      { "path_alias_exists" : { "alias" : "tags\/[node:field-tax-tags-a][node:field-tax-tags-b]" } }
    ],
    "DO" : [
      { "variable_add" : {
          "USING" : {
            "type" : "text",
            "value" : "[node:field-tax-tags-a][node:field-tax-tags-b]"
          },
          "PROVIDE" : { "variable_added" : { "variable_added" : "Added variable" } }
        }
      },
      { "entity_query" : {
          "USING" : {
            "type" : "taxonomy_term",
            "property" : "name",
            "value" : [ "variable-added" ],
            "limit" : "1"
          },
          "PROVIDE" : { "entity_fetched" : { "entity_fetched" : "Fetched entity" } }
        }
      },
      { "data_set" : { "data" : [ "node:field-tax-tags-c" ], "value" : [ "entity-fetched:0" ] } }
    ]
  }
}

Rule 2 - AB Term doesn't exist so create it

{ "rules_doesn_t_exist" : {
    "LABEL" : "Doesn\u0027t exist",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules", "path" ],
    "ON" : {
      "node_insert--article" : { "bundle" : "article" },
      "node_update--article" : { "bundle" : "article" }
    },
    "IF" : [
      { "NOT path_alias_exists" : { "alias" : "tags\/[node:field-tax-tags-a][node:field-tax-tags-b]" } }
    ],
    "DO" : [
      { "entity_create" : {
          "USING" : {
            "type" : "taxonomy_term",
            "param_name" : "[node:field-tax-tags-a][node:field-tax-tags-b]",
            "param_vocabulary" : "tags"
          },
          "PROVIDE" : { "entity_created" : { "entity_created" : "Created entity" } }
        }
      },
      { "data_set" : { "data" : [ "node:field-tax-tags-c" ], "value" : [ "entity-created" ] } }
    ]
  }
}

After that, create and edit some articles only using A and B. C matches and instead of duplicating, will fetch the existing term.

14
  • thank you very much for your help, but your answer seems to be only suitable for english.
    – LIUJIAHUI
    Commented Dec 6, 2016 at 6:54
  • Hey Niall, must be me, but I don't understand this answer (which intriges me!). Maybe add to 2nd parg (at end) "... Whereas I think the question is so and so"? What are those aliases about? Also, do you have An export of those 2 Rules so that if I'd experiment with them I might get it anyway? Commented Dec 6, 2016 at 7:14
  • 1
    Ah ok, now I understand the posts. It would be better if the comments were removed after the entire problem has been fixed! What I'm getting from this is that the Mandarin language would get converted to English for the url of the taxonomy? I don't know much about transliteration yet but there must be some logic that can be used. Commented Dec 7, 2016 at 9:50
  • 1
    Since my way doesn't seem to work for this scenario, I think the original question in this thread can be answered using an extra rule that when a taxonomy term is created, it checks if the name already exists using some conditional rules method or VBO views results check (we talked about it in another answer a long time ago), and if it does, add the existing term to the node and delete the newly created term. Commented Dec 7, 2016 at 9:52
  • 1
    There actually seems to be modules that deal with this automatically.. Whoops. Commented Dec 7, 2016 at 9:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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