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.

Comments

TyrelDenison’s picture

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

stella’s picture

I can confirm the same scenario as #1

stella’s picture

Actually upon further debugging, I've a slightly different scenario and can now reproduce it consistently:

  1. Add a product to cart and proceed to checkout
  2. Fill in billing customer profile pane [the customer profile is an entity]
  3. Continue to Review step.
  4. Go back to checkout step
  5. Unset one of the required billing fields - e.g. set state or city to empty.
  6. Continue to Review step again.

At this point you would expect to receive a missing required field error. Instead I get:

EntityMetadataWrapperException: Unable to get the data property type as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 442 of entity/includes/entity.wrapper.inc).

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.

valderama’s picture

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

$bla = $wrapper->body->value->raw();

An if-condition helps..

if(!empty($node->body)){
  $bla = $wrapper->body->value->raw();
}
TyrelDenison’s picture

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

vrMarc’s picture

@Stella This fixed it for me - https://www.drupal.org/node/2275495

joelpittet’s picture

Status: Active » Needs review
StatusFileSize
new1.74 KB

The entity README.txt suggests you can do this:

$wrapper->body->value->value(array('decode' => TRUE));

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.

joelpittet’s picture

Ok 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:

  1. Install entity module
  2. Create a custom module with this block definition:
    /**
     * Implements hook_block_info().
     */
    function custom_block_info() {
      $blocks['testimonials'] = array(
        'info' => t('TEST'),
        'cache' => DRUPAL_CACHE_GLOBAL,
      );
      return $blocks;
    }
    /**
     * Implements hook_block_view().
     */
    function custom_block_view($delta = '') {
      switch ($delta) {
        case 'testimonials':
          $node = menu_get_object();
          $node_wrapper = $node ? entity_metadata_wrapper('node', $node) : NULL;
          if ($node_wrapper) {
            $description = $node_wrapper->body->value->value(array('decode' => TRUE));
          }
      }
    }
    
  3. Add that block to the content region
  4. Create a node with an empty body.

.

joelpittet’s picture

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

The last submitted patch, 7: 1596594-7-empty-body-exception.patch, failed testing.

The last submitted patch, 8: 1596594-8-empty-body-exception-tests-only.patch, failed testing.

The last submitted patch, 9: 1596594-9-empty-body-exception-fail.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 9: 1596594-9-empty-body-exception-pass.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 9: 1596594-9-empty-body-exception-pass.patch, failed testing.

Status: Needs work » Needs review

The last submitted patch, 9: 1596594-9-empty-body-exception-fail.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 9: 1596594-9-empty-body-exception-pass.patch, failed testing.

mvonfrie’s picture

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

  <div class="description">
    <?php print $entity->field_description->value->value(); ?>
    <span class="read-more><!-- ... --></span>
  </div>
BillyTom’s picture

Google 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:

EntityMetadataWrapperException:
Unable to get the data property field_com_type as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (Zeile 442 von /var/data/vhosts/drupal/sites/all/modules/entity/includes/entity.wrapper.inc).

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

welly’s picture

Yeah, 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.

joelpittet’s picture

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

welly’s picture

@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!

joelpittet’s picture

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

arturs.v’s picture

I 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?

korsakov’s picture

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

deggertsen’s picture

Thanks for the patch. I probably need a more long term solution, but I'm grateful that this at least allows my site to load.

Exploratus’s picture

Me too. Is there a solution to this?

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 9: 1596594-9-empty-body-exception-pass.patch, failed testing.

cydharttha’s picture

The 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!

deggertsen’s picture

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

joelpittet’s picture

@deggertsen yes check out what I mentioned in #23 I think that's why.

bsandor’s picture

Hi,

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?

joelpittet’s picture

@bsandor Try my patch in #9 Let us know what you think

bsandor’s picture

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

deggertsen’s picture

Is 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.');

joelpittet’s picture

This probably gives way too much info but it helps debug this kind of error.

$backtrace = debug_backtrace();
$debug = '<pre>' . json_encode($backtrace, JSON_PRETTY_PRINT) . '</pre>';
throw new EntityMetadataWrapperException('Unable to get the data property ' . check_plain($name) . ' as the parent data structure is not set. ' . $debug);
milos.kroulik’s picture

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

rlmumford’s picture

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

if ($entity->field_entity_reference->field->property->dataAvailable()) {
  // do something with the value.
  $value = $entity->field_entity_reference->field->property->value();
}

The second of the solution patches makes getPropertyValue return null instead of an exception.

rlmumford’s picture

Status: Needs work » Needs review

The last submitted patch, 41: 1596594-9-empty-body-exception-fail.patch, failed testing.

The last submitted patch, 41: 1596594-41-public_dataAvailable_method.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 41: 1596594-41-no_exception.patch, failed testing.

johnpitcairn’s picture

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

mustanggb’s picture

I used 1596594-41-no_exception.patch with services_entity to prevent the error when updating an entity with a file field.

heddn’s picture

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

candelas’s picture

I 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

      if (!isset($data)) {
        throw new EntityMetadataWrapperException('Unable to get the data property ' . check_plain($name) . ' as the parent data structure is not set.');
      }

to

      if (!isset($data)) {
        return NULL;
      }

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!

joelpittet’s picture

Status: Needs work » Needs review

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

Status: Needs review » Needs work

The last submitted patch, 49: entity-on-exception-return-null-1596594-49.patch, failed testing.

candelas’s picture

Thanks @joelpittet. I don't understand why it fails the test :)

tontoman’s picture

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

benarobinson’s picture

Why 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

shraddha404’s picture

Hello,

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?

g33kg1rl’s picture

I am also experiencing this issue whenever I remove a value from a term reference field. Which patch should I test? :)

g33kg1rl’s picture

Tested patch in #49 and it works without any side effects for me. :)

aj2’s picture

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

JacksonBison’s picture

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

Chris Charlton’s picture

Finding myself doing the same as #49. :(

aj2’s picture

I applied patch in #49. Have not noticed a problem for two months now.

anthonyleach’s picture

Status: Needs work » Needs review
StatusFileSize
new2.56 KB

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

Status: Needs review » Needs work

The last submitted patch, 62: entity-on-exception-return-null-1596594-62.patch, failed testing.

Chris Charlton’s picture

@anthonyleach Patch failed testing. :(

anthonyleach’s picture

Chirs,

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

g33kg1rl’s picture

Is this rtbc?

joelpittet’s picture

Test needs to pass before rtbc

g33kg1rl’s picture

If someone can explain how to manually run a test, I can try to do it. :)

joelpittet’s picture

  1. Enable the simpletest "Testing" module.
  2. Under configuration > development > testing >Find the test that is failing by name
  3. Run it

Also on the command line: https://www.drupal.org/docs/7/testing/running-tests-through-command-line

Chris Charlton’s picture

Status: Needs work » Needs review

How 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?

heddn’s picture

We need to address the feedback in #48. Are we doing the right thing here or is returning NULL just masking the problem.

mikechr’s picture

#49 works for me. At least for a temporary solution

Dimitris Nik’s picture

#49 works for me. My problem was with Commerce Fees. EntityMetadataWrapperException:... EntityStructureWrapper->getPropertyValue() (line 457...

antongp’s picture

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

Status: Needs review » Needs work

The last submitted patch, 62: entity-on-exception-return-null-1596594-62.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

osopolar’s picture

I have a taxonomy term reference field. On the entity edit form I want to loop over all parents:

if (isset($node_wrapper->field_term)) {
  foreach ($node_wrapper->field_term->parents_all as $parent) {
    // do something
  }
}

If no term was selected I get the error

EntityMetadataWrapperException: Unable to get the data property parents_all as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 460 of .../sites/all/modules/contrib/entity/includes/entity.wrapper.inc).

Making dataAvailable() function public won't help, as it returns TRUE, or maybe I am using it the wrong way?

if (isset($node_wrapper->field_term) $node_wrapper->field_term->parents_all->dataAvailable()) {...

Returning NULL in EntityStructureWrapper::getPropertyValue() as in #49 does not help either, because than I get the following warning and error:

Warning: array_keys() expects parameter 1 to be array, null given in EntityListWrapper->getIterator() (line 1124 of .../sites/all/modules/contrib/entity/includes/entity.wrapper.inc).
Recoverable fatal error: Argument 2 passed to EntityMetadataWrapperIterator::__construct() must be of the type array, null given, called in .../sites/all/modules/contrib/entity/includes/entity.wrapper.inc on line 1124 and defined in EntityMetadataWrapperIterator->__construct() (line 1202 of .../sites/all/modules/contrib/entity/includes/entity.wrapper.inc).

So finally I had to first check if a term value is present, to get it work:

if (isset($node_wrapper->field_term) $node_wrapper->field_term->value()) {...
maxplus’s picture

Thanks,
also facing this issue and using patch from #49 is solving this for me until now.

nickonom’s picture

I have a custom module that programmatically adds existing commerce product to order as a line item like so:

      // Save product as line item.
      $line_item = commerce_product_line_item_new($product, 1, $order->order_id);
      commerce_line_item_save($line_item);
      $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
      $order_wrapper->commerce_line_items[] = $line_item;
      commerce_line_item_save($line_item);
      $order_wrapper->commerce_line_items[] = $line_item;
      commerce_order_save($order);

Everything looks ok on order view page, but as soon as I click on order edit page it is throwing the following notices:

Notice: Undefined index: label in field_multiple_value_form() (line 196 of /home/domain/public_html/modules/field/field.form.inc).
Notice: Undefined index: description in field_multiple_value_form() (line 197 of /home/domain/public_html/modules/field/field.form.inc).
Notice: Undefined index: commerce_unit_price in commerce_line_item_field_widget_form() (line 1063 of /home/domain/public_html/sites/all/modules/commerce/modules/line_item/commerce_line_item.module).

and the log pages is showing:

EntityMetadataWrapperException: Unable to get the data property amount as the parent data structure is not set. in EntityStructureWrapper->getPropertyValue() (line 457 of /home/domain/public_html/sites/all/modules/entity/includes/entity.wrapper.inc).

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.

sano’s picture

Patch #49 works for me as well. Thanks.

yazzbe’s picture

Patch #49 works for me as well.

ludo.r’s picture

Patch #49 works for me as well, it solves my issue in local, however, I did not try it in production.

mustanggb’s picture

So what we need here is for a maintainer to answer #48.

solideogloria’s picture

I agree with #48. Breaking changes bad.

Making dataAvailable() available sounds useful and the simplest solution all-around.

solideogloria’s picture

StatusFileSize
new498 bytes

Added patch making the function public. Not tested.

solideogloria’s picture

Status: Needs work » Needs review
rob c’s picture

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