Hi,
Issue: Save entity variable in a Loop through a view result variable saves only 1 entity
The first loop goes ok, the second loop re-saves the first entity with new values.

So my issue is when you have a save entity variable action on a loop it's saving the same entity instead of creating a new entity. I believe it has something to do with the uuid.

Also receive an warning when you loop:

Warning: array_flip(): Can only flip STRING and INTEGER values! in Drupal\Core\Entity\EntityStorageBase->loadMultiple() (line 264 of core/lib/Drupal/Core/Entity/EntityStorageBase.php).
Drupal\Core\Entity\EntityStorageBase->loadMultiple(Array) (Line: 249)
Drupal\Core\Entity\EntityStorageBase->load(NULL) (Line: 527)
Drupal\Core\Entity\Entity::load(NULL) (Line: 643)
Drupal\business_rules\Util\BusinessRulesProcessor->evaluateVariables(Object, Object) (Line: 473)
Drupal\business_rules\Util\BusinessRulesProcessor->executeAction(Object, Object) (Line: 316)
Drupal\business_rules\Util\BusinessRulesProcessor->processItems(Array, Object, 'entity_insert_ato') (Line: 261)
Drupal\business_rules\Util\BusinessRulesProcessor->processTriggeredRules(Array, Object) (Line: 168)
Drupal\business_rules\Util\BusinessRulesProcessor->process(Object) (Line: 136)
Drupal\business_rules\EventSubscriber\BusinessRulesListener->process(Object, 'business_rules.entity_insert', Object)
call_user_func(Array, Object, 'business_rules.entity_insert', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('business_rules.entity_insert', Object) (Line: 115)
business_rules_entity_insert(Object)
call_user_func_array('business_rules_entity_insert', Array) (Line: 403)

Comments

lexsoft created an issue. See original summary.

pepe roni’s picture

Did you reset the entity fields (especially uuid and id) when filling the entity fields again?

c.e.a’s picture

Can you please provide step by step on how to reproduce this issue ?

c.e.a’s picture

Category: Bug report » Support request
c.e.a’s picture

Status: Active » Postponed (maintainer needs more info)
lexsoft00’s picture

Hi @C.E.A,

* 3 content types: Article, Basic Page, Test.
1. Create 5 Basic Pages and display their NID in a view
2. Create a views variable result variable
3. Create a empty entity variable for Article
4. Create a empty entity variable for Basic Page
5. Create a loop variable result action
6. Create action to Fetch entity variable by id and add the {{views-variable-result->nid}}
7. Create action Fetch and set values to entity variable with empty entity variable Article, Article bundle and content type, add field title with {{empty-entity-variable-basic-page->title}}
8. Create action Save entity variable to Article variable
Add 6. 7. and 8. to 5. loop variable result action
Add a Rule entity update on Test content type node and add 5. loop variable result action to it.
Test by create a Test node and update it.

lexsoft00’s picture

Status: Postponed (maintainer needs more info) » Needs review
lexsoft00’s picture

StatusFileSize
new1.2 KB

I've got something working.

lexsoft00’s picture

StatusFileSize
new1.21 KB

Missing 1 closure

Deno’s picture

This still isn't fixed in beta8. Patch works AFAIK, at least it did before - we will test on beta8 tomorrow.

Deno’s picture

My assumption is that the entity variable gets assigned a nid when saved the first time and then this nid is used in all subsequent saves. This is what I see when debugging block is enabled.

Unfortunately, there is no way (that I can see) to reset a field value that was set before. In general, the GUI will not allow you to assign empty value to a field. In my opinion, this is the actual bug and the failure to use this in a loop is then just a logical consequence.

Deno’s picture

Category: Support request » Bug report
Deno’s picture

Apparently not working in beta8.

Btw, I took a look at the patch and it confirmed what I already suspected: rules are confused weather to save this as a new node or to update old one. In the meantime I'm convinced that the approach described in #3076481: "Save as new entity" option is a way to go: define two actions (one for adding new entities and one for trying to update if possible) or a Boolean switch that forces adding new entities rather than try to be clever about this.

People writing the rules know what they want (e.g. add new entities in the loop) and if they have an "always add new nodes" option to choose, they will do it.

In which case the code simplifies to something like this:

if ($always_add) {
$entity->uuid->value = $uuid_new;
$entity->nid->value = NULL;
$entity->save();
}

Where "$always_add" is a boolean.

No checking if uuids already exist or not, we simply force the system to generate a new item. As a bonus, we get a nice new functionality: the nodes can be easily used as templates - just load a "template" node, change a parameter or two as needed and store it as new node. That would resolve #3076481: "Save as new entity" option and #3076410: Fetch, update and save as new node not possible, while making the #3076450: Assigning values to fields in an entity variable is badly broken far less urgent.

Deno’s picture

Update: https://www.drupal.org/docs/8/api/entity-api/working-with-the-entity-api..., and the documentation states the following:

// To save an entity.
$entity->save();
That works for both new and existing entities, the entity itself keeps track whether it's new or not. By default, for content entities, that depends on whether it has an ID or not. To save an entity that has an ID as a new entity (e.g, when importing something), the isNew flag can be enforced.

// The following will attempt to insert a new node with the ID 5, this will fail if that node already exists.
$node->nid->value = 5;
$node->enforceIsNew(TRUE);
$node->save();

I suppose something like this would then work to force saving as new:

 if ($always_add) {
              $entity->uuid->value = NULL;
              $entity->nid->value = NULL;
              $entity->enforceIsNew(TRUE);
              $entity->save();
            }

Not sure about uuid, maybe one has to create a new one instead of setting it to NULL?
Maybe $entity->uuid->generate();?

Deno’s picture

OK, there was a misunderstanding earlier... I had a good look at the patch again (sorry, I'm not a developer) and realized that it checks if the UUID changed and if it did saves the entity as new. This is pretty much what I suggested earlier..

I added a "put random number in UUID" as a part of the set action and it still didn't work. Then I double-checked and found out that the patch still wasn't applied on the server because it doesn't apply correctly on beta 8. However, the patch is really simple so we applied it "per hand" and now the rule works as expected.

As I said, I'm assigning a random number to UUID in a loop, which results in an invalid but definitely assures that the new uuid is always different from the old one. Before patching this resulted in ONE node that had invalid UUID in it (I'm surprised that this isn't checked by entity code). After patching, this results in several nodes generated and they have valid UUIDs

All in all, this works now. Moreover, it probably resolves #3076410: Fetch, update and save as new node not possible too, I'll test that next.

Deno’s picture

Good news: it is indeed possible to "save as new" entity variables that were loaded from existing entities in a loop (and outside it) once this patch is applied. Thus cloning with rules is suddenly possible, this is a very cool feature.

One little thing could (should) be added in the patch though: setting the time fields to the current values. See #3076410: Fetch, update and save as new node not possible for more details.

totolearn’s picture

OK, I think it is related to this problem for I did the search to all related issues.

In this process, I did applied #9 patch on current dev/beta 9 version but I still getting error:

Warning: array_flip(): Can only flip STRING and INTEGER values! 位於 Drupal\Core\Entity\EntityStorageBase->loadMultiple()

It has detail error with :
core/lib/Drupal/Core/Entity/EntityBase.php(531): Drupal\Core\Entity\EntityStorageBase->load(NULL) #5 ..../business_rules/src/Util/BusinessRulesProcessor.php(643): Drupal\Core\Entity\EntityBase::load(NULL) #6 ..../business_rules/src/Util/BusinessRulesProcessor.php(473) .....business_rules/src/Util/BusinessRulesProcessor.php(473): Drupal\business_rules\Util\BusinessRulesProcessor->evaluateVariables(Object(Drupal\business_rules\VariablesSet), Object(Drupal\business_rules\Events\BusinessRulesEvent)) #7....

total # form 30 to #52. the rest after #7 seems ok.

The setup to got this error is:
01. Create Level01: empty entity variable
02. Create Level02: empty entity variable
03. using Loop through a multi-value field variable, to get a multi-value referenced ID, in the loop
03-1: for each ref-ID fetch the entity put into 01's Level01 entity
03-2: set Entity variables to Level02 entity (with the values from Level01 entity and other calculated results)
03-3: Save the Level02 entity

the error is casued by 03-3

I planned to save multiple 03-3 entities, but even one already got the error.
On 03-2 it even put the uuid with random numbers but still getting the error.

Any advise will be appreciated.

totolearn’s picture

OK, the update for this issue, and it relates to https://www.drupal.org/project/business_rules/issues/3052265

Now, I used Beta-9 Feb/07/2020 version, the situation is:

when I applied #9 patch, it will create the new entities in the loop OK. But it will cause the problem of "New node created instead of updating existing node" when I modified an existing entity field when do a new entity by "before saving an entity", the same issue happened as in 3052265.

So, it has to do with the code added that need to separate the existed entity inside the loop from the new entity trigger the rule.

Anyone have the idea what to do?

Deno’s picture

I can confirm that my nicely working set of rules stopped working after update to beta9. The reason is not clear to me.

First, looking at my updates, the error must be either in entity or in busines rules. Second, the patch we are applying only changes one file in busines rules and this file has not changed from b3 to b4.

Here is the whole patch:

diff --git a/src/Plugin/BusinessRulesAction/SaveEntityVariableAction.php b/src/Plugin/BusinessRulesAction/SaveEntityVariableAction.php
index 66ad8bc..a5b4664 100644
--- a/src/Plugin/BusinessRulesAction/SaveEntityVariableAction.php
+++ b/src/Plugin/BusinessRulesAction/SaveEntityVariableAction.php
@@ -11,6 +11,7 @@ use Drupal\business_rules\Plugin\BusinessRulesActionPlugin;
 use Drupal\business_rules\VariableObject;
 use Drupal\Core\Entity\Entity;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Component\Uuid\Uuid;
 
 /**
  * Class SaveEntityVariableAction.
@@ -112,7 +113,17 @@ class SaveEntityVariableAction extends BusinessRulesActionPlugin {
 
         if ($entity->uuid->getValue() !== $saved_uuid) {
           $key_value->set($uuid, $uuid);
-          $entity->save();
+
+          if ($entity->isNew()){
+            $entity->save();
+          } else {
+            $uuid_service = \Drupal::service('uuid');
+            $uuid_new = $uuid_service->generate();
+            if(Uuid::isValid($uuid_new) == TRUE){
+              $entity->uuid->value = $uuid_new;
+              $entity->nid->value = NULL;
+              $entity->save();
+            }
+          }
 
           $result = [
             '#type' => 'markup',

What the patch does is the following:

1. If someone changes the UUID while manipulating the in-memory copy of an entity, then a new entity should be generated.
2. To do so, a new UUID should be generated before saving.
3. This can be done by calling:

  $uuid_service = \Drupal::service('uuid');
  $uuid_new = $uuid_service->generate();

4. And a new entity is generated on save if the nid is unset and the uuid is given the new vaue that we just generated.

If any of this fails, things will not work.

Moreover, the changes from b3 to b4 appear to be minor. Here are the files that changed:

Only in business_rules-B9: business_rules.drush.inc
Files business_rules-B8/business_rules.info.yml and business_rules-B9/business_rules.info.yml differ
Files business_rules-B8/business_rules.services.yml and business_rules-B9/business_rules.services.yml differ
Files business_rules-B8/modules/br_group/br_group.info.yml and business_rules-B9/modules/br_group/br_group.info.yml differ
Files business_rules-B8/modules/br_sms/br_sms.info.yml and business_rules-B9/modules/br_sms/br_sms.info.yml differ
Files business_rules-B8/src/Controller/ScheduleController.php and business_rules-B9/src/Controller/ScheduleController.php differ
Only in business_rules-B9/src/Events: BusinessRulesDrushEvent.php
Files business_rules-B8/src/EventSubscriber/BusinessRulesListener.php and business_rules-B9/src/EventSubscriber/BusinessRulesListener.php differ
Files business_rules-B8/src/Plugin/BusinessRulesAction/SetFieldValue.php and business_rules-B9/src/Plugin/BusinessRulesAction/SetFieldValue.php differ
Files business_rules-B8/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php and business_rules-B9/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php differ

Mostly yaml files or related to drush, this leaves just a few files that might be causing the issue. SetFieldValue.php maybe?

Alternatively, it could be a change in entity module?

totolearn’s picture

in #18 my example, it should never need to create new UUID, the UUID changed duriing the processes.
03-1 step, fetch the 1st UUID to the first Empty Entity Variable, and
03-2 step, by the loop will get 2 UUID, to the Empty Entity Variables created and filled by fetched entities by ID referenced from 03-1's Entity.
And, when to saved the updated field value at 2nd level UUIID, instead of using the UUID, it got generated new UUID.

Then, it resulted 2 new Entities were created with all the value from 03-2 steps fetched entities.

So, what should be modified in the patch #9 to know the different level's UUID? Should we stacked the Entity Variables, I am not familiar with to coding format. Anyone?

djween’s picture

Hi,

Use Case:
For every content id in Bundle A returned in view results, create a new node in bundle B and copy some values from A to B, such as title.

My Setup:
-View A - outputs a set of NIDs of type Bundle A
-View results variable A references View A, argument passed [node:nid]
-Empty entity variable A for Bundle A
-Empty entity variable B for Bundle B
-Rule: entity insert event. Target bundle: C
-- Loop action: loop view A.
---Fetch entity variable by id action: entity id field: nid, target bundle: A, entity variable: empty entity variable A, value: {{empty entity variable A->nid}}
---Set values to entity variable action: target bundle: B, entity variable: empty entity variable B, updating title field with token referencing bundle A title.
---Save entity variable action: target bundle: B, entity variable: empty entity variable B

Result:
-Views successfully passes array of multiple bundle A NIDs to variable. Confirmed with BR Debug.
-Bundle C node is created as expected.
-only 1 new bundle B node is created (it should create multiple bundle B nodes though based on view results). New node contains title and other data from bundle C (it should use data values from bundle A).

I applied patch but wasn't working. Anyone having success with patch on latest release or dev version at this point?

lexsoft00’s picture

Are you using the action "Set values to entity variable" ?

I've added a patch for updating an entity but it's not creating a new one.

djween’s picture

@lexsoft00

Are you using the action "Set values to entity variable" ?

Yes, I am using "Set values to entity variable". I have updated my comment #21 to clarify this further.

Without your patch in #22, I have already been able to loop through a view and update the fetched entities. However, I was only able to do so when target bundle did not have other rules applied to it that leveraged variables - even if not the same variable. I have this issue posted regarding that matter: https://www.drupal.org/project/business_rules/issues/3136784

I am using BR version: 8.x-1.0-beta9+2-dev on D8.8.5

lexsoft00’s picture

I use

 "drupal/business_rules": "1.x-dev#eb2fa5927405510571d7af4b0791ff37bf1fe0e5",

With the following patches:
        "Issue #3067844: Mail overwritten in busines_rules.module" : "https://www.drupal.org/files/issues/2019-07-15/mail-overwritten-3067844-2_0.patch",
        "Issue #3068553: Use site mail as sender value is never saved": "https://www.drupal.org/files/issues/2019-07-17/Use_site_mail_as_sender_value_is_never_saved-3068553-2.patch"
        "Issue #3093597: Actions Intermitently stop firing until cache is cleared again": "https://www.drupal.org/files/issues/2019-11-12/actions-intermitently-stop-firing-until-cache-is-cleared-3093597-4.patch",
        "Issue #3087989: Duplicate condition": "https://www.drupal.org/files/issues/2020-02-06/duplicate-condition-3087989-3.patch"
      },
djween’s picture

@lexsoft00 I updated my installed BR module with all patches mentioned in #24.
I also updated it with patch in #22.
Using: D8.8.6, BR 8.x-1.0-beta9+2-dev

I am coming across 2 issues after testing further:

1) Unwanted duplicated node
I believe I am seeing same issue as Totolearn in #18...
when I have patch #9 installed, BR will create a duplicate node rather than just updating it. I use the fetch, set, and save actions (without any loop action) to update the current node on view entity event. The field I need updated gets updated successfully but it also duplicates the node which I do not want it to do. When I remove patch #9 it just updates the field as per my desired requirement and as I would expect it to do.
Either there is an issue here or it is not clear how I would specify to update vs create or do both with patch #9 in place.

2) New nodes successfully created but can't set field values with variables
In my setup mentioned in comment #21 and with patch #9, #22, and the others mentioned above installed, my BR rule only partially works. It correctly creates new nodes during the loop action however, it doesn't set field values with variables. Below are comments on my steps from my setup described in comment #21 which provide more detail on the issue:

-View A - outputs a set of NIDs of type Bundle A

confirmed setup OK

-View results variable A references View A, argument passed [node:nid]

confirmed setup OK

-Empty entity variable A created for Bundle A

confirmed setup OK

-Empty entity variable B created for Bundle B

confirmed setup OK

-Rule: entity insert event. Target bundle: C

This occurs OK and kicks off the rule

-- Loop action: loop view A.

This occurs OK

---Fetch entity variable by id action: entity id field: nid, target bundle: A, entity variable: empty entity variable A, value: {{empty entity variable A->nid}}.

This occurs OK. I see in debug that my set of nodes (NIDs) from Bundle A are fetched

---Set values to entity variable action: target bundle: B, entity variable: empty entity variable B, updating title field with token referencing bundle A title.

In Debug, I see an empty entity variable for Bundle A but no value(s) e.g. for Title. I think this is where issue occurs. I don't know if use case is too complex where it can't set {{empty entity variable A->title}} in to title field of empty entity variable B? However, it seems this is same use case as totolearn's comment #17 point: 03-2 so I assume it is not too uncommon. Null value is being returned for the variables.
I also tested by adding an action to set values to target bundle: B, entity variable: empty entity variable B in addition to setting values to bundle A variable just in case they needed to be set in variable B before they could be pulled in to variable A. But no luck.

---Save entity variable action: target bundle: B, entity variable: empty entity variable B

The correct number of new nodes are created of type B which is correct and OK, however, as per above point the new nodes have NULL values - e.g. title field is null. I can set values if static but can't seem to use variables/tokens here.

lexsoft00’s picture

Leaving the bugs for a second, I'm trying to understand why are you using "Fetch entity variable by id action" when you are already looping?

Can you not have all the data present in the view and pass it down?

If I understand correctly you have 3 content types
Let's call them:
1. Articles
2. Basic Page
3. Duplicate Page

You have the content type Articles that has an entity reference to the Basic Page.
On entity insert rule used on the bundle Articles you would like to clone the Basic Page to Duplicate Page?

djween’s picture

...I'm trying to understand why are you using "Fetch entity variable by id action" when you are already looping?

For several reasons that are irrelevant now, I was doing it incorrectly. I added more fields to my view and am using view results variables to set my fields. This rule on its own works without issue now. Thank you @lexsoft00.

To hopefully save someone else some time, here is my working setup for reference:

Use Case: there is templated content and each user can create their own instance of this content.
Uses 4 content types:

  1. Template Agenda Parent
  2. Template Agenda Children (contains entity reference field to Template Agenda Parent)
  3. User Agenda Parent (has an entity reference field to Template Agenda Parent)
  4. User Agenda Children (has entity reference field to User Agenda Parent)


Rule Setup

  1. View setup: outputs results with fields from content type Template Agenda Children: Content ID (nid), Title field, and some other custom fields. A contextual filter is setup based on the Content ID of User Agenda Parent. Via the entity reference fields on the User Agenda Parent and Template Agenda Children nodes, several Relationships are setup to enable the view to output the aforementioned Template Agenda Children fields.
  2. Setup a view results variable referencing the view above. Argument passed is [node:nid]. This node ID is that of the current User Agenda Parent node the user is creating and which triggers the entity insert event.
  3. Setup an empty entity variable for content type User Agenda Children
  4. Setup a loop action: loop the above view. Setup the following actions and add them inside the loop action:
  5. Setup a set values to entity variable action: target content of type User Agenda Children, select the empty entity variable setup above, and add the fields to be updated here. For example, I want each new User Agenda Children node to have the title of a corresponding Template Agenda Children node. So select the title field and add value: "{{view result variable name->title}}". The fields setup in the view should be available by using the views result variable...e.g. {{view result variable name->title}}, {{view result variable name->nid}}, etc.
  6. Setup a save entity variable action: target content of type User Agenda Children and select the empty entity variable used above.
  7. Setup rule: entity insert event. Target content of type User Agenda Parent. Add the loop action.
  8. Results: new User Agenda Parent node is created. A set of User Agenda Children nodes are created based on view results and each node's entity reference field is set to reference the User Agenda Parent node to tie them together. These children pages are also set with data duplicated from Template Agenda Children nodes.
djween’s picture

@lexsoft00 - I didn't realize initially that patch #22 adds a new action to set and save (and not create new node). In my comment #25 point #1:

1) Unwanted duplicated node

your patch resolves this issue for me from my initial testing. I was able to set and save static data, tokens, and BR variables and no new node is created. I replaced my empty entity variable, and variable actions: fetch, set, and save with the single new action: "Set entity fields values to any entity and save". Much easier! Thank you.

lexsoft00’s picture

Hi @djween,

Glad that it works!

djween’s picture

Anyone know if this issue is resolved in version 2 release as I see version 2 does not include the action: Set entity fields values to any entity and save.

djween’s picture

Save entity variable in a Loop through a view result variable saves only 1 entity still occurs in version 2 of BR.
None of the patches here resolve this feature of business rules.
Curious if anyone has a solution to resolve?
patch #9 works. just need to manually update file as it failed via composer.

coaston’s picture

Hi djween,

I tried to replicate your case #27 due to learning process, however I am getting following issue once I want to create a new node with User Agenda Parent

Drupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null: INSERT INTO "node_field_data" ("nid", "vid", "type", "langcode", "status", "uid", "title", "created", "changed", "promote", "sticky", "default_langcode", "revision_translation_affected") VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12); Array ( [:db_insert_placeholder_0] => 157630 [:db_insert_placeholder_1] => 219667 [:db_insert_placeholder_2] => user_agenda_children [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => 1 [:db_insert_placeholder_5] => 3 [:db_insert_placeholder_6] => [:db_insert_placeholder_7] => 1676649418 [:db_insert_placeholder_8] => 1676649418 [:db_insert_placeholder_9] => 0 [:db_insert_placeholder_10] => 0 [:db_insert_placeholder_11] => 1 [:db_insert_placeholder_12] => 1 ) in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 811 of core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

I am not sure why the title is null - i have configured "set values to entity variable action" with {{setup_a_view_results_variable_->title}}

Also I am not sure about contextual filter , have you configured "provide the default value" ??

djween’s picture

@coaston
--First check your view outside of business rules. As in create a page from your view and make sure there are actual results.
--yes, in my case I have provided default value in the contextual filter. I am using "content id from url" in my particular case to filter the view.
--And in BR in the views variable, for the argument I have: [node:nid].
--Also make sure you have patch #9 installed.