Updated: Comment #8

Problem/Motivation

Is there a hasData() or isEmpty() method available in entity_metadata_wrapper()? I haven't seen one. I'd like to avoid messages similar to the error mentioned in #1928884: Provide more info when EntityMetadataWrapperException is invoked when a field happens to be empty. Right now I have to mix field access [if (field_get_items($entity_type, $entity, $field_name) ...] in with my metadata access just to avoid that error.

Proposed resolution

Create a new public method called hasProperty to be able to do something like:

if (!$wrapper->hasProperty('field_name')) {...}

Create a new public method called isEmpty to be able to do something like:

if (!$wrapper->field_name->isEmpty()) {...}

Remaining tasks

  • create the hasProperty public method.
  • create the isEmpty public method.

API changes

Before this issue

$infos = $wrapper->getPropertyInfo();
if (!empty($infos['field_my_field']) || !empty($infos['field_my_other_field'])) {
  $my_field = $wrapper->field_my_field->value();
  $my_other_field = $wrapper->field_my_other_field->value();
  if (!empty($my_field)) {
    // Do sth.
  }
  elseif (!empty($my_other_field)) {
    // Do sth else.
  }
}

After this issue

if ($wrapper->hasProperty('field_my_field') && !$wrapper->field_my_field->isEmpty()) {
  // Do sth.
}
elseif ($wrapper->hasProperty('field_my_other_field') && !$wrapper->field_my_other_field->isEmpty()) {
  // Do sth else.
}
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

NancyDru’s picture

There is a dataAvailable() method, but it is protected so we can't use it.

DuaelFr’s picture

Title: Add an isEmpty() method to improve DX » hasData() or isEmpty() method?
Category: feature » support
FileSize
665 bytes

Here is a patch adding a new isEmpty public method.
I will update the issue summary to help the maintainer to review this.

This patch is part of the #1day1patch initiative.

DuaelFr’s picture

Title: hasData() or isEmpty() method? » Add an isEmpty() method to improve DX
Category: support » feature
Status: Active » Needs review
DuaelFr’s picture

Title: hasData() or isEmpty() method? » Add an isEmpty() method to improve DX
Category: support » feature
FileSize
497 bytes
665 bytes

I fixed a tiny mistake in the comment.

NancyDru’s picture

Awesome! Thanks, Edouard. It works great for me.

DuaelFr’s picture

Status: Needs review » Needs work

I have some issues with this new method. It's name suggests that it returns FALSE if the value exists and is not empty while it returns TRUE if the value does not exist or is empty (instead of throwing an exception). In fact, I never seen it returning TRUE and it continues to throw exceptions when used on a non-existing property or field...

I tried to replace it by the following code but it only fix the first behavior and still not allow to easily check if a field/property exists.

  public function isEmpty() {
    try {
      $value = $this->value();
    }
    catch (EntityMetadataWrapperException $e) {
      $value = FALSE;
    }
    return empty($value);
  }


May we create another method allowing to do something like this ?

$wrapper->doesExist('my_suspicious_field');


Or may we change the isEmpty method to have something more like this ?

$wrapper->isEmpty('my_suspicious_field');
DuaelFr’s picture

Title: Add an isEmpty() method to improve DX » Add an isEmpty() and hasChild() methods to improve DX
Status: Needs work » Needs review
FileSize
1.23 KB

This patch introduces an EntityStructureWrapper::hasChild() method that checks if the given child exists without throwing an exception.

It allows to do things like this :

if ($wrapper->hasChild('field_my_field') && $wrapper->field_my_field->isEmpty()) {
  // Do something.
}
DuaelFr’s picture

Sorry, I forgot to include the EntityMetadataWrapper::isEmpty() as exposed in #6

Here is a new patch containing the isEmpty() from #6 and the hadChild() from #7

DuaelFr’s picture

Issue summary: View changes

Issue summary update.

NancyDru’s picture

Thanks, Edouard.

As long as we are at it, I ran into one this morning: it would be nice to do $wrapper->id(). In my case I am accessing a node, but I have others where I access a user or a profile. It would be nice to get the entity's id field without having to know what kind of entity it is ($wrapper->nid->value() vs. $wrapper->uid->value()

DuaelFr’s picture

I think you should use the existing getIdentifier() public method.

DuaelFr’s picture

Issue summary: View changes

Updated issue summary.

joachim’s picture

if (!$wrapper->hasChild('field_name')) {...}

I'm not sure hasChild() is the right name for that method. Elsewhere, these are called properties, aren't they? And if, say, I have an entityreference field on users, I get a chain of properties $node_wrapper->author->entityref_to_node... child doesn't seem right when these can double back on themselves and be circular...

DuaelFr’s picture

Hi joachim, thank you for your review.
I am sorry but I don't get the point, probably because of the language barrier.

The purpose of this hasChild() method is to safely check if there is a field or a property named like this on the entity before querying it to avoid having to deal with Exceptions. We could name it hasComponent() if it suits better to the concept.

If you think it could lead to an infinite loop in some cases I may have coded it wrong. I don't remember why I didn't use the getPropertyInfo() method instead of get() but if you think it could be better and safer I can rewrite my patch.

joachim’s picture

My point was entirely about the method name. hasProperty() would be my suggestion.

And I don't mean there's an infinite loop in the code, not at all!

I meant that with the properties that are references to other entities, you can loop round. Eg:

- node has an 'author' property to the user entity
- you can put an entityref field on users that points to nodes, and this will define a property

So if the field on the user points to a node that the user is the author of, you can go $user_wrapper->node_field->author->value() and you're back where you started. Hence 'child' doesn't seem to really fit.

garphy’s picture

hasProperty() seems like a better fit.
it's close to hasOwnProperty() in ECMAScript-ish dynamic languages.

DuaelFr’s picture

Isn't it confusing to use "property" in the name of a method that can check the existence of properties and fields?

@joachim: sorry for the misunderstanding about the circular relations.

joachim’s picture

To Entity API metadata wrappers, everything is a property: $node->title becomes the title property, and a node FieldAPI field field_foo becomes the field_foo property.

DuaelFr’s picture

Title: Add an isEmpty() and hasChild() methods to improve DX » Add an isEmpty() and hasProperty() methods to improve DX
Issue summary: View changes
FileSize
1.36 KB
758 bytes

Here is a new patch.

DuaelFr’s picture

Issue summary: View changes
jimmyko’s picture

It would be a great feature. I don't understand why it is still pending.

mariacha1’s picture

The hasProperty() method is identical to the output you can get from simply doing isset() on an unknown property, so I don't think that adds anything.

In other words:

$wrapper->hasProperty('field_name') === isset($wrapper->field_name)

Meanwhile, the isEmpty() property seems to be equivalent to calling empty($property->value()), which doesn't help us much. I do see the exception catching there, but I don't think that actually would trigger unless you were calling this method on an undefined property, which can be tested by calling isset. So:

if(isset($wrapper->field_name) && !empty($wrapper->field_name->value())) { ... }

Equals

if($wrapper->hasProperty('field_name')) && !$wrapper->field_name->isEmpty())) { ... }

However, when you combine the idea behind hasProperty with isEmpty by passing the name to isEmpty, you do get a helpful function that catches exceptions AND tells you whether the property is empty:

   public function isEmpty($name) {
    try {
      $value = $this->$name->value();
    }
    catch (EntityMetadataWrapperException $e) {
      $value = FALSE;
    }
    return empty($value);
  }

Thus

isset($wrapper->field_name) && !empty($wrapper->field_name) === $wrapper->isEmpty('field_name')

I'm updating the patch to combine the two functions in this way. As well as an interdiff.

joelstein’s picture

Status: Needs review » Reviewed & tested by the community

Love this! I wanted a simple way to know if a node that I tried to load with entity_metadata_wrapper() exists, without attempting to load the whole node and all it's fields with $node->value().

If the node doesn't exist, I can't access $node->nid->value() (it throws an exception), even though the property isset.

Which this patch, I can simply say $node->isEmpty() and get the information I need in a lightweight fashion.

bohemier’s picture

These are a real nice addition, a must for code readability

jimmyko’s picture

I'm not sure if it is good to use exception to handle this case. But a little bit strange to me.

fago’s picture

Status: Reviewed & tested by the community » Needs work

isEmpty() should translate to $value === NULL, if the property does not exist it is not not-empty, it does not exist. I.e., the passed property name is an invalid parameter and should throw an exception. This needs to be documented via @thrown as well.

+   *   The name of the child to check.
+   * @return bool
+   *   TRUE if the child exists, FALSE if it does not.

Misses a new line before @return.

lhridley’s picture

Rerolling the patch from #20, which will apply endlessly creating Fatal errors for duplicate methods.

osopolar’s picture

I think this is also related to the issue #1596594: EntityMetadataWrapperException: Parent Data Structure Not Set and maybe if that one is fixed this won't be necessary.

jacob.embree’s picture

I was getting a fatal error about calling value() on null when using isEmpty() on a property that doesn't exist. A quick !empty() seems like a good fit here.