I have a pretty simple workflow for a rule... it needs to:
- Create a new user account using the e-mail address from an order.
- Toggle the status if necessary.
- Set the uid of the order and its related customer profiles to the new account's uid.
However, I have problems in a several places. First, if I try to toggle the status using the newly created entity, it results in a PDOException, because it tries to re-INSERT the user that was created in step 1. Of course this causes a primary key collision.
Additionally, even though the user is created and saved during step 1, the uid actually isn't available to subsequent actions. If I try to spit it out in a message, it says it's "not yet assigned."
Not a big deal, I thought. I could get around this be fetching the entity by e-mail address after the initial creation to get a copy of the variable that has the uid populated. However, this fetch doesn't actually result in a usable parameter for subsequent actions. Not sure why, but it doesn't show up in my data selectors or token lists.
Any ideas? : )
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | rules_saving.patch | 1.5 KB | fago |
| #3 | 1044342.save_original_wrapper.patch | 796 bytes | rszrama |
| #3 | 1044342.var_name_strtr.patch | 1.52 KB | rszrama |
Comments
Comment #1
rszrama commentedOooh, regarding my last point about fetching, turns out I was forgetting to loop over the fetched entity. It would be nice if I could use the argument without a loop if the limit on the fetch is 1. Still doesn't solve the other problems, unfortunately, but may give me a workaround in the meantime.
Edit: Actually, the workaround won't work, because if you do an entity save on a newly created entity via Rules, the state I suppose is still trying to save the newly created entity as though it were new. Not really sure where to track this down, but it seems to me as soon as an entity is saved, it should satisfy any save requirements for that parameter throughout the Rule unless something else in it changes.
Comment #2
rszrama commentedI may have tracked this down further with copious debug messages in the various RulesState::save*() methods. What appears to be happening is that my initial entity create action populates the state's $save array with a key using the underscore: entity_created. However, subsequent changes to the entity's properties and/or an entity save action add the wrapper again to the $save array using the hyphen: entity-created. My manual save action takes care of entity-created, since that's the actual selector, and I suppose when the rule is done evaluating, it tries again to save entity_created with the stale wrapper (i.e. having the duplicate data). What's interesting, though, is that altering the property does appear to alter it for both wrappers even though saving doesn't seem to populate the uid in both... will keep digging.
Comment #3
rszrama commentedAaaand, victory. Ok, as I suspected in my last post, the duplicate query was caused by a duplicate variable in the $state->save array. The reason the duplicate with the underscore gets there is because even though the RulesState::save*() methods accept a selector as the first argument, sometimes the variable name is passed. I see two ways forward... translate the $selector argument to turn _ to - or do this at the point of the calling. Since the argument is supposed to be a selector already, I think translating it at the point of calling the function is the way forward. The attached patch addresses this for RulesAction::executeCallback(), though there might be other instances. (Note that this patch may conflict with #1044338: Wrong variable name used for finding a state variable in RulesAction::executeCallback() since they both affect a same line.)
Additionally, the reason the saved entity isn't updated even if I force an immediate save through the "Save an entity" action is because RulesState::save() clones the wrapper when it adds it to $state->save (line 199). I'm not sure of the rationale here... why would we want to save a cloned wrapper instead of persisting the same one through the process? If I remove the clone, when I save, I can immediately use the uid in subsequent actions. Is there some drawback that led you to cloning in the first place?
Comment #5
rszrama commentedTestbot fail. :-/ Perhaps because I put two patches in one issue?
Comment #6
fagooh, good catch! That comes with all the '-' replacement crap.. ;)
We need the clone though as the content of wrappers might change else before they are saved. Consider a rule modifying node:author and setting node:author to something else. With the clone in place, we still save the first entity, not the new one. So I've improved your patch to only skip the clone if immediate is TRUE.
Also, let's just convert dashes to underscores in save() and accept both as argument. Patch attached. Hopefully the bot likes it.
@entity_fetched: A good idea, however that would mean changing the limit option changes the data type of the returned variable (list vs no list) - what might break the existing configuration. As one can just use "result:0" anyways, I think it's better to just stay with that? Once #1047296: Improve the Data selector UX and fix it for lists got fixed, using it should be more straight forward anyway.
Comment #7
fagooh, and if you get it working with that, perhaps you want to share the export of the basic rule such that we can add it to the tests? Would be probably a good idea to ensure that it doesn't break again.
Comment #8
rszrama commentedHmm, so, the original issue with not being able to actually save newly created entities has been resolved. That was an assured fix, and doing the strtr() in save() works just fine. However, for some reason, the save immediate accommodation isn't working. When I create and save a user account, its data is not populated. This is funny, because technically it should be working - so perhaps something else that's been committed is prevented it from working?
Basically, the user gets saved by my account_created wrapper's user variable isn't updated accordingly. It still just has the bare properties it received on creation with no uid. My workaround works fine, fetching the newly created account by property (e-mail address) and using the returned list item to populate the $order->uid. But I'm guessing this should work as the patch expected... will look at it a little more.
Comment #9
rszrama commentedInvestigated further to no avail, but I need to move on to other issues since I have a viable workaround. Perhaps the logic regarding when to clone should just be rolled back and addressed in another issue?
Basically, here's what doesn't work in my rule, even with the patch:
My debug messages showed that the wrapper in the saveNow() method is indeed updated. If you dump it before and after the save, it will show that the data in the wrapper is properly updated. However, back in the save() method, if I dump before and after the call to saveNow(), the data is the same. For some reason or another, even without the clone, it's not updated by reference.
I'm pretty certain my patch worked before, even though it would've broken other things... so I'm not sure why this patch doesn't work in my use case. Here's an export of the workaround in action:
Comment #10
rszrama commentedJust a reminder on this one... even though the clone issue wasn't fixed, the strtr() fix is still very important. Can we at least get that committed? I have a workaround for not being able to use a newly saved variable, but there's no working around the selector name issue.
Comment #11
fagoops, somehow this issue got off my radar. Thanks for the reminder :)
> 1. Create a user account.
> 2. Save the user account immediately.
> 3. Use the user account's uid anywhere (or any other property populated on save).
Ah, I can imagine it doesn't work for user accounts only now. The problem is the user API is .. ehm, not ideal. See #1002700: entity_save() should not accept object arguments by reference. and #999004: user_save() relies on $edit instead of $account.
So I'd assume the reference is somewhere lost in the wrapper, so the uid doesn't appear in subsequent actions. If so it should work with the patch from #999004: user_save() relies on $edit instead of $account applied.
Comment #12
rszrama commentedOk, cool. Indeed it does work as expected with that other patch applied. So, I'm wondering... can we get this patch applied today? If so, I can enable my default rule for creating user accounts in checkout as part of the beta. : )
(It'll still use the entity_fetch workaround until that user mess gets sorted out, though.)
Comment #13
fago>Ok, cool. Indeed it does work as expected with that other patch applied.
Great! So, as this patch is fine, let's get it in!
Comment #14
fagothanks, committed.