Problem/Motivation
It is very difficult for modules to check whether it is possible to get property information.
To get a nested property from an entity, I often want to write code like:
// Normal field with properties
$entity->field->property->value()
// Entity reference field with field and properties.
$entity->field_entity_reference->field->property->value();
However, if I am not sure whether the data is available (e.g. if field_entity_reference is not required) then it is likely that my code will throw exceptions. This is because calling
isset($entity->field_entity_reference->field->property)
only returns false if the properties are not defined and
empty($entity->field_entity_reference->field->property)
will trigger the same exception as just trying to get the value (empty() calls __isset followed by __get).
The only ways a developer has to work around this problem are the following:
- Put the data request in a try block
try { $value = $entity->field_entity_reference->field->property->value(); } catch (Exception $e) { // Do nothing. }however, this is a problem because it will ignore the 'Undefined property' exceptions as well as the 'Missing data' exceptions. Also it means that what should be a simple variable assignment takes 6 lines!
- Check the value of every property in the chain
if ($entity->value() && $entity->field_entity_reference->value() && $entity->field_entity_reference->field->value()) { $value = $entity->field_entity_reference->field->property->value(); }
EntityMetadataWrapper actually has a method to check whether a nested property is available, dataAvailable(), but it is protected!
Proposed resolution
Make the dataAvailable method public.
OR
Return NULL in getPropertyValue() when the data is not set rather than throwing an exception.
User interface changes
None
API changes
Make the dataAvailable method public.
Original Report
I have built a function with rules into a Commerce site that deletes a cart order if certain conditions are triggered on the first checkout page. After the recent core upgrade, I have been receiving the following error when this function is called.
EntityMetadataWrapperException: Unable to get the data property commerce_total as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 442 of C:\inetpub\wwwroot\damacofulfillment\profiles\commerce_kickstart\modules\entity\includes\entity.wrapper.inc). Backtrace:
EntityStructureWrapper->getPropertyValue('commerce_total', Array) entity.wrapper.inc:86
EntityMetadataWrapper->value() entity.wrapper.inc:440
EntityStructureWrapper->getPropertyValue('currency_code', Array) entity.wrapper.inc:86
EntityMetadataWrapper->value() entity.wrapper.inc:258
EntityValueWrapper->value() commerce_order.module:1308
commerce_order_calculate_total(Object) commerce_order.controller.inc:81
CommerceOrderEntityController->save(Object) commerce_order.module:737
commerce_order_save(Object) commerce_order.module:1283
commerce_order_status_update(Object, 'checkout_review', , NULL, 'Customer continued to the next checkout page via a submit button.') commerce_checkout.pages.inc:331
commerce_checkout_form_submit(Array, Array) form.inc:1443
form_execute_handlers('submit', Array, Array) form.inc:854
drupal_process_form('commerce_checkout_form_checkout', Array, Array) form.inc:374
drupal_build_form('commerce_checkout_form_checkout', Array) form.inc:131
drupal_get_form('commerce_checkout_form_checkout', Object, Array) commerce_checkout.pages.inc:58
commerce_checkout_router(Object)
call_user_func_array('commerce_checkout_router', Array) menu.inc:516
menu_execute_active_handler() index.php:21
After research, I came across this discuss regarding the core update and similar issues (http://drupal.org/node/1541792), which pointed to this issue with Entity API (http://drupal.org/node/1556192). I believe these issues are related, though I cannot quite connect the dots. Any help is appreciated as this issue is the only thing holding back the site launch.
| Comment | File | Size | Author |
|---|---|---|---|
| #84 | entity-dataAvailable-1596594-84.patch | 498 bytes | solideogloria |
| #62 | entity-on-exception-return-null-1596594-62.patch | 2.56 KB | anthonyleach |
| #49 | entity-on-exception-return-null-1596594-49.patch | 1.18 KB | candelas |
| #41 | interdiff-noexception.txt | 1.18 KB | rlmumford |
| #41 | 1596594-41-no_exception.patch | 2.96 KB | rlmumford |
Comments
Comment #1
TyrelDenison commentedI am also getting this Exception when I take an order through to checkout and then return back to the cart page. A slight difference is that it is with data property type rather than commerce total initially. Once that error has occurred it return to saying data property commerce total every time I try and add something to the cart.
Comment #2
stella commentedI can confirm the same scenario as #1
Comment #3
stella commentedActually upon further debugging, I've a slightly different scenario and can now reproduce it consistently:
At this point you would expect to receive a missing required field error. Instead I get:
I'm not sure if this is for the Entity API module or for Drupal Commerce, but the error message is coming from the entity module so leaving it in this queue.
Comment #4
valderama commentedI had this problem too, and it was caused by some custom code, which caused entity API to through this exception, when a field is not available.
Accessing the body value like this, was throwing the exception, if body is not set at all.
An if-condition helps..
Comment #5
TyrelDenison commentedFor me, it was a rule that comes configured default with Commerce. The 'Delete shipping line items on shopping cart updates' rule was deleting the total, causing things that looked for it (shipping module) to panic when it wasn't there. I checked with Ryan from Commerce Guys and he assured me that disabling that rule would not cause any issues, so I have done so and it has worked well ever since.
Comment #6
vrMarc commented@Stella This fixed it for me - https://www.drupal.org/node/2275495
Comment #7
joelpittetThe entity README.txt suggests you can do this:
But when the body field is empty, the first value is NULL and this exception occurs.
Here is a test only patch to prove this.
Comment #8
joelpittetOk that test should fail but false positives... this is a better test.
I created a note without $this->drupalCreateNode() method because it was messing too much with the output of body and I needed something that got closer to what I get in a real environment.
Also for a manual test:
.
Comment #9
joelpittetSame patch as #8 but with a possible solution. Likely people won't agree with this but seems to pass tests locally, let's see what testbot says.
I really think NULL values are acceptable values and shouldn't throw exceptions. And it seems they are sent that way from fields api, so let's see...
Comment #20
mvonfrie commentedI can confirm this issue with Drupal 7.32 and Entity API 7.x-1.5+7-dev (just updated) for Long Text fields in a normal node content type.
Working with try..catch blocks isn't a nice solution when you're in a template and just want to output the content, e. g.
Comment #21
BillyTom commentedGoogle brought me here. One of our sites is running a daily import with the migrate module. The following error occured today when trying to view a specific node:
After I opened the node manually with node/x/edit and then saved it again the problem was gone.
I am using Drupal 7.19, Entity API 7.x-1.0-rc3 and Field Collection 7.x-1.0-beta4
Comment #22
welly commentedYeah, this exception is pretty annoying. As someone said above, a null field isn't an error. Can we make this break a bit more gracefully? I may have a go at patching it myself but if anyone who knows this module a bit better can look into it, I'm sure you'll make a lot of people happy.
Comment #23
joelpittet@welly that was what I was trying to do in #9 though there is a test that expects the exception thrown which is why it says 1 fail from the testbot.
Comment #24
welly commented@joelpitter I've used your patch and it's all working fine - far better than throwing exceptions for null values although I am curious as to whether this has any repercussions. From what I can tell it shouldn't but we'll see!
Cheers!
Comment #25
joelpittet@welly as am I curious:) I'm not a fan of throwing exceptions on missing dynamic properties, but I could be wrong in this thinking.
Comment #26
arturs.v commentedI am also getting this exception. I have term reference in my node and if there is no term selected page refuses to load and I'm getting exception in Drupal logs. Workaround in #9 fixes it.
Have you noticed removing the statements casing problems elsewhere?
Comment #27
korsakov commentedI just added a reply to a forum topic. No custom parts used. (Drupal 7.34 with Forum and Advanced Forum)
EntityMetadataWrapperException: Unable to get the data property format as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 438 of /.../sites/all/modules/entity/includes/entity.wrapper.inc).
Comment #28
deggertsen commentedThanks for the patch. I probably need a more long term solution, but I'm grateful that this at least allows my site to load.
Comment #29
Exploratus commentedMe too. Is there a solution to this?
Comment #32
cydharttha commentedThe patch worked for us as well. We have a Commerce site where this started happening, seemingly out of the blue, we haven't figured out yet why. Thanks!
Comment #33
deggertsen commentedDo we know why the patch is failing testing? I've run into this problem now on a few sites so it seems like it's pretty significant... Curious why it hasn't received more attention.
Comment #34
joelpittet@deggertsen yes check out what I mentioned in #23 I think that's why.
Comment #35
bsandor commentedHi,
I run into this very same issue which is exist for about 3 years now.
This is how it happened to me:
I used to use commerce_node_checkout modules earlier version that used references module. I updated it. (Current version needs its fields to be converted to entity_reference module.)
Since I am getting the very same error:
EntityMetadataWrapperException: Unable to get the data property currency_code as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue()
Is there anything that might help?
Comment #36
joelpittet@bsandor Try my patch in #9 Let us know what you think
Comment #37
bsandor commented@joelpittet I am using commece_node _checkout module. When I create my first product with it I have no errors while if that is the second product i run into this issue.
Because of that I am not sure if my issue belong to this ticket.
I opened another ticket there.
Comment #38
deggertsen commentedIs there any way to make the error thrown more descriptive so that we can actually find where the data is not being set? For me that is what keeps me returning to this issue and using the patch in #9 that simply removes throw new EntityMetadataWrapperException('Unable to get the data property ' . check_plain($name) . ' as the parent data structure is not set.');
Comment #39
joelpittetThis probably gives way too much info but it helps debug this kind of error.
Comment #40
milos.kroulik commentedI encountered this issue when I tried to delete field collection item with Rules module. Field collection entity iself is deleted, but the field table still has entry pointing to it.
Comment #41
rlmumfordUpdated the issue summary with a description of the core problem and what to do about it.
The first patch is the same tests from #9. The following patches include both ways of solving it. The first of these makes the dataAvailable method public so that developers can check whether the data is available before trying to use it.
The second of the solution patches makes getPropertyValue return null instead of an exception.
Comment #42
rlmumfordComment #46
johnpitcairn commentedIn my case I think something is producing a commerce line item wrapper with the correct type property but a null bundle, and the exception is thrown when code checks $entity_wrapper->type->value(), preventing customers from continuing to payment. Tracking down the code that is producing the malformed line item is proving difficult and I need to move on.
The no-exception patch works for me, thanks.
Comment #47
mustanggb commentedI used 1596594-41-no_exception.patch with services_entity to prevent the error when updating an entity with a file field.
Comment #48
heddnMy vote here is that we go the route of dataAvailable(). Wrappers can be chained and returning NULL in the middle of a chained series of calls across entity reference fields is just going to lead to this issue cropping up in yet another way. Marking dataAvailable public seems less invasive to the API. Folks /might/ already have try/catch out there and built in logic to handle accordingly. If we remove the exception, that effectively breaks their logic.
Comment #49
candelas commentedI am getting this problem too. I have a Commerce Kickstart 7.x-2.37 installation and with Commerce Discount 7.x-1.0-alpha8 (the one that comes with Commerce Kickstart is 7.x-1.0-alpha7 and also gives the error) enabled and several discounts created. When I enable Commerce MOA 7.x.1.6 and go from the cart to the checkout, I get this error:
EntityMetadataWrapperException: Unable to get the data property type as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 438 of /var/www/commerce_kickstart/profiles/commerce_kickstart/modules/contrib/entity/includes/entity.wrapper.inc).
After much reading I got it working changing
to
I agree with heddn that it needs a better solution, but I don't have the knowledge and I need it to work :)
I add the patch for people that could need it. Thanks for your work!
Comment #50
joelpittet@candelas want to see if my test in #9 will hold up with that (my bet is that it does)
Setting to needs review for the testbot to pickup the patch as is.
Comment #52
candelas commentedThanks @joelpittet. I don't understand why it fails the test :)
Comment #53
tontoman commentedConfirming this error with entity 7.x-1.x-dev trying to run FB Oauth
EntityMetadataWrapperException: Unable to get the data property profile_id as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 457 of /home/siteX/public_html/sites/all/modules/entity/includes/entity.wrapper.inc).
Comment #54
benarobinson commentedWhy does it throw exceptions instead of just returning a blank value? Unfilled fields should never generate exceptions, in my opinion, and it doesn't make sense that the logic for ensuring unfilled fields are not attempted should be left to external checks instead of contained inside of the wrapper. It's annoying to have to duck type everything
Comment #55
shraddha404 commentedHello,
I created the field in Line item Type using Customizable Product module.
While creating the line item type for this newly added field to add in order the same error appears for me.
Can anybody suggest the solution?
Comment #56
g33kg1rl commentedI am also experiencing this issue whenever I remove a value from a term reference field. Which patch should I test? :)
Comment #57
g33kg1rl commentedTested patch in #49 and it works without any side effects for me. :)
Comment #58
aj2 commentedRecently started to see this error as well, and had been working just fine. For me, it's getting triggered by a rule which checks for the condition commerce_order_contains_product. The exception gets thrown after the following warnings:
Unable to get a data value. Error: Unable to get the data property sku as the parent data structure is not set.
Unable to evaluate condition commerce_order_contains_product.
EntityMetadataWrapperException: Unable to get the data property product_id as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 457 of /sitename/sites/all/modules/entity/includes/entity.wrapper.inc).
Comment #59
JacksonBison commented#49 works for me
I'm sure it might have other far reaching issues, but being unable to work with blank values without my site throwing a hissy-fit and crashing, is ridiculous.
I really hope we're not celebrating this issue's fifth birthday in a few months...
Comment #60
Chris CharltonFinding myself doing the same as #49. :(
Comment #61
aj2 commentedI applied patch in #49. Have not noticed a problem for two months now.
Comment #62
anthonyleach commentedLooks like the only reason the patch in #49 failed is because there was a test to ensure that getting an nonexistent property value threw an Exception.
The attached path is identical to #49 (with the additional test updates) and when contributed should be contributed to candelas.
Comment #64
Chris Charlton@anthonyleach Patch failed testing. :(
Comment #65
anthonyleach commentedChirs,
That's right, the test file isn't patched before running the tests against the patch. It has to be manually ran, rather than relying on the automated testing procedure :(
Thanks
Anthony
Comment #66
g33kg1rl commentedIs this rtbc?
Comment #67
joelpittetTest needs to pass before rtbc
Comment #68
g33kg1rl commentedIf someone can explain how to manually run a test, I can try to do it. :)
Comment #69
joelpittetsimpletest"Testing" module.Also on the command line: https://www.drupal.org/docs/7/testing/running-tests-through-command-line
Comment #70
Chris CharltonHow shall we qualify/quantifying if the fix is good to be merged in? Do we just each run the test locally and report back, or is/will there be another gate necessary for validation?
Comment #71
heddnWe need to address the feedback in #48. Are we doing the right thing here or is returning NULL just masking the problem.
Comment #72
mikechr commented#49 works for me. At least for a temporary solution
Comment #73
Dimitris Nik commented#49 works for me. My problem was with Commerce Fees. EntityMetadataWrapperException:... EntityStructureWrapper->getPropertyValue() (line 457...
Comment #74
antongp commented+ 1 to #48
Returning NULL may change logic with try/catch. Used this way a few times to fallback to some default values inside
catch.Comment #76
osopolarI have a taxonomy term reference field. On the entity edit form I want to loop over all parents:
If no term was selected I get the error
Making dataAvailable() function public won't help, as it returns TRUE, or maybe I am using it the wrong way?
Returning NULL in EntityStructureWrapper::getPropertyValue() as in #49 does not help either, because than I get the following warning and error:
So finally I had to first check if a term value is present, to get it work:
Comment #77
maxplus commentedThanks,
also facing this issue and using patch from #49 is solving this for me until now.
Comment #78
nickonom commentedI have a custom module that programmatically adds existing commerce product to order as a line item like so:
Everything looks ok on order view page, but as soon as I click on order edit page it is throwing the following notices:
and the log pages is showing:
And I couldn't do anything to the order because the edit page was not opening at all, now after applying #49 at least I am able to edit orders, but the notices on the page are still there, though error on the log page is gone.
Comment #79
sano commentedPatch #49 works for me as well. Thanks.
Comment #80
yazzbe commentedPatch #49 works for me as well.
Comment #81
ludo.rPatch #49 works for me as well, it solves my issue in local, however, I did not try it in production.
Comment #82
mustanggb commentedSo what we need here is for a maintainer to answer #48.
Comment #83
solideogloria commentedI agree with #48. Breaking changes bad.
Making dataAvailable() available sounds useful and the simplest solution all-around.
Comment #84
solideogloria commentedAdded patch making the function public. Not tested.
Comment #85
solideogloria commentedComment #86
rob c commentedRetesting due to code changed in entity.test.
Older versions tested with $this->assertException($wrapper->source, 'title'); in testNodeProperties().
This changed to $this->assertNull($wrapper->source, 'title'); in dev, so retesting the patch.
This is still an issue, empty values should just return NULL i guess, or at least not throw an exception. I guess it will now pass, but lets see.