I have been stymied all day by a strange bug with Countries.

It works more or less fine to install countries and entityreference, then add an entityreference field to a node, and reference any country.

Unfortunately, I need Countries to act as Organic Groups which they should be able to; any entity should be able to be a group.

It more-or-less works fine, except if you change the entityreference widget to the OG-required one, you are no longer able to save a country reference..

The node will fail to save because, when saving, OG goes through all the fields in the node form and finds any OG fields. Then it creates EntityMetadataWrapper objects for each, and runs $wrapper->{$field_name}->set($gids).

In the case of Countries, at least, this calls the core Entity module's set method, which validates that the data being set is correct.

For whatever reason in the case of countries, something goes wacky here.

The Entity module's validate function attempts to find the "name" entity key for the "country" entity. This is set in countries.module to be "iso2". Specifically, countries uses countries_entity_info() to define the "country" entity, and in the "entity keys" section, establishes the "id" and "label" keys, which are required.

It also establishes a "name" key, which it seems is acceptable, but I cannot find documentation on what this is used for.

For whatever reason, however, if this "name" entity key is set, Entity module's entity_property_verify_data_type() (in entity.property.inc) will use that to determine what type of data is being set.

I don't know what exactly occurs here -- the FORM is trying to save the country id (cid) -- but because 'name' is set to 'iso2', the property_info that will be validated against is of type "token". The cid -- an integer -- will fail.

You can change the 'name' key to be 'cid' instead of 'iso2', but no matter what, it seems 'name' entity keys all must be type 'token', and hence will fail.

A simple fix, however, is to just remove the 'name' entity key altogether. This causes the entity module to set properly and does not seem to have other negative repercussions.

I don't fully understand, as I said, what we get from having that 'name' key set to 'iso2', but if it's not required it seems to break other things.

As such, I am boldly submitting this patch which simply removes it, and I'd love to hear that it's no good for some reason; I'll fix it.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Alan D.’s picture

name is the key that the entity api needs to tell it that the db storage is the iso2 key, so without this, the cid will be used and everything will eventually go bang :(

So what is the bug exactly?

"you are no longer able to save a country reference"

Validation error? Nothing is saved to the DB? CID is saved rather than the ISO2?

Offlein’s picture

Thanks for the super-prompt reply, Alan.

You'll get the standard Drupal everything's-broken "The website has encountered an unexpected error" page, with a red status message saying: EntityMetadataWrapperException: Invalid data value given. Be sure it matches the required data type and format. in EntityDrupalWrapper->set() (line 736 of /var/www/clean/sites/all/modules/entity/includes/entity.wrapper.inc).

Nothing will save.

And if you trace out the occurrence of that error, into entity module's includes/entity.property.inc you'll find around line 253 how $info[$type]['entity keys']['name'] will be set to iso2.

...That seems wrong already, because the value that's trying to be saved is a cid.

Then it does $property_info = entity_get_property_info($type) (where $type is "country") and sees that $property['properties'][$key]['type'] is "token". The numeric cid will fail to validate as a token.

Ninja edit: Also, worth noting, I did a clean install of the latest Drupal, and am using dev versions of OG, Entity, and Countries. If you wanna duplicate, get those installed and use the OG Field configuration page to add a "group" field to the country entity, then set one or more countries to be groups. Then go add an entity reference field to a content type, and make it use the OG widget. Choose the country you set as a group and click save.

Alan D.’s picture

Appears to have a corresponding issue in the Entity API queue. Marking as a duplicate as these sound similar.

#1937856: Always allow entity id for EntityDrupalWrapper

There is a patch albeit throwing errors

I have moved the steps to replicate there. Cheers for reporting.

Alan D.’s picture

Status: Needs review » Closed (duplicate)
Related issues: +#1937856: Always allow entity id for EntityDrupalWrapper

Status: Closed (duplicate) » Needs work

The last submitted patch, countries-remove_entity_key_name.patch, failed testing.

Alan D.’s picture

(Copied from the other thread)

I haven't the time to setup / check, but a workaround within the countries module could be to define the country name key type in countries_entity_property_info_alter(). No idea of the format, so start with the devel debug:

dpm($info['country']['properties']['name']);

if set, add this:

$info['country']['properties']['name']['type'] = 'string';

Note that the token type fails as the module uses the ISO standard of using Uppercase and the token validation is the Drupal default of validating the machine codes lower case char followed by * lowercase chars, numbers or underscores :/

Please let me know if this works and I'll update the module and also mark that other thread as fixed ;)

Offlein’s picture

Awesome, thanks for your help. I'm going to try this right now -- but in case you see this before I return, it seems like we'll still have an issue because the value set in the form is a cid, not an ISO2. That is, even if the ISO2 was parsed as a string instead of a token, the value attempting to be saved is an integer.

But maybe this will fix that too.. I'll find out!

Offlein’s picture

OK, so, that didn't work right, I think for a couple reasons.

First, $info['country']['properties']['name'] refers to your country name column ("Canada"), not the "name" entity key. Since name never comes into play (but ISO2 does) we need to modify ...[properties']['iso2']['type'] instead.

Second, you use "string" but I believe the proper type is "text" for entity properties?

Anyway, using your code, I threw a dpm($info['country']) at the end of that function and the array looks like this screenshot: http://imgur.com/InRUcFJ. (Again, to reiterate this did not work.)

Afterward, I went back and used the line $info['country']['properties']['iso2']['type'] = 'text'; and .. it worked! It's now type "text" and I can edit and save the page as expected.

I still don't think this isn't 100% right, though; I am pretty sure the cid value is just being passed through the iso2 filter which is free-wheelin' enough to let an integer fly. It's not verifying through cid, though, which it should be?

Alan D.’s picture

Nice, so there are two things here maybe to alter, iso3 is text or token?

This should be the fix:

$info['country']['properties']['iso2']['type'] = 'text';

and things should just work.

Using Entity Reference, cid is probably used in the db, using the country widgets iso2 should be used in the db. Track a new content item through to see what is happening and if it is returned correctly. Then if there are issues with the old existing content, resaving these should fix those

Offlein’s picture

Hi Alan,

I just wrote a long reply to this and then stepped away from my desk for about two minutes, during which time my office's automated software updater helpfully restarted my computer (and.. also seemingly uninstalled Java for some reason).

I'll try to paraphrase it here and reply in detail later if needed.

Yep, you're right, that will "fix" the problem, but I am misunderstanding your latter meaning, I think, unfortunately. (Sorry!) I've re-tested, and OG Entity Reference definitely keys on CID and will attempt to set CID through the Entity ->set() method. And Entity definitely compares what's being set (again, cid) against the standards for whatever's set to be the "name" entity key (in this case, iso2).

As said, it now works because iso2's "text" type will not deny a cid value.. But it's still not right.

Per you other question, iso3 is "text" as well, and all the other fields seem good!

Alan D.’s picture

Great, if you upload a patch, I can commit it through to dev.

The bit about saving old content was the hope that the ER was using ISO2 rather than CID now (blind hope that some magic somewhere was doing something different). This is clearly not the case from what you have said. If you were using the country fields, (not ER fields) the storage would have been ISO2.

Anyways, there must be an issue with the reference field / entity api itself (outside of our control). So the ER widget uses the ID (cid) but the Entity API part is correctly thinking that it is the NAME (iso2).

I guess that this is working at least and validation probably is not required, and hopefully will continue to work, but maybe one of these two options:

a) followup with the Entity Reference issue queue to extend the field to allow conditional support for the NAME property if present. I say conditionally allow as if it blindly changes to use NAME if set over ID, then all existing sites that use that field will loss their data.

b) find the integration point between Entity API and Entity Reference and somehow make ER tell the Entity API that this is the ID rather than the NAME property being used as a key (...even without looking, this could be hard)

PS: If the ER widget is a select list, it has alternative built in validation that is very hard if not impossible to bypass. I'd stop chasing the bug at that point unless you have free time to help out resolving the root issue / know of another way to trigger the error.

Offlein’s picture

FileSize
353 bytes

Hiya! Got it; understood. I didn't know that about the Country fields.. In fact, I hadn't even noticed that you define a field at all 'til yesterday. (Unrelated, but: why have both? Simplicity?)

I'll peek into why the ER widget fudges this up. I like (a) better.

Anyway, here's ya' patch!

Alan D.’s picture

Status: Needs work » Needs review

"why have both?"

Countries is far older than ER and it is nice to be able to have very customizable widgets without complex additional integration with another module ;)

Offlein’s picture

Good answer!

Alan D.’s picture

If you create / find the ER issue, can you cross post back here for others to track?

I found #2143171: Entity Metadata Wrapper support broken for named, exportable entities., but I'm not sure if this is the answer either

Offlein’s picture

Oh, right -- Sorry, I started my own ticket (#2184763: Entity Reference of custom entity references by wrong parameter) but it was marked as a Duplicate of the one you mentioned (#2143171: Entity Metadata Wrapper support broken for named, exportable entities.) which seemed right to me. But the patch in that one didn't work and I've got less time than I had to investigate deep into the black heart of Entity Reference, so I left my comments there in hopes that it might make a difference for some other poor soul.

Alan D.’s picture

Status: Needs review » Reviewed & tested by the community

black heart of Entity Reference

lol, love the description. I have extended early plugins of this module, only to have api changes that blew everything up and at the point I have limited myself to never extending the plugins again.

Cheers for everything, I'll be looking at doing some real work in the queues over the next month or so. Till then, I think that this is rtbtc

Offlein’s picture

<3 Ha! Sounds right.

Anyway, so far -- seven years in -- this is the most pleasant experience I've had with a contrib maintainer, so the real props go to you. :)

Thanks again, hit me up if you need me to do anything to help!

Offlein’s picture

Damn, duplicate post! First time for that in seven years, too...

Sutharsan’s picture

@Alan D, What needs to be done to get this patch committed?

Alan D.’s picture

A co-maintainer... interested?

Sutharsan’s picture

Yea, I know the feeling. But no thanks.

webflo’s picture

FileSize
676 bytes

I think we fix from #12 is correct. We need to switch to 'text' as data type because tokens are lowercase (see Data Type Reference" or in code entity_property_verify_data_type()). But we can specify an additional callback.

@Alan: OG is not required to reproduce the issue. All you need is "drush php-eval 'print_r(entity_metadata_wrapper('country', "DE")->raw());'".

garphy’s picture

Status: Reviewed & tested by the community » Needs work
+function countries_entity_validate_iso2($data) {
+  return is_scalar($data) && preg_match('!^[A-Z]{3}$!', $data);
 }

iso2 is a 2 chars long string, not 3, isn't it ?

BR0kEN’s picture

Status: Needs work » Needs review
FileSize
3.37 KB
3.4 KB

First of all, validation callback must be available in a global scope because the Entity API module will not load the *.info.inc.

Additionally, I've modified the validation callback allowing it to recognize the objects having __toString() methods.