function comment_entity_info() {
  $return =  array(
    'comment' => array(
      'label' => t('Comment'),
      'base table' => 'comment',
      'uri callback' => 'comment_uri',
      'fieldable' => TRUE,
      'controller class' => 'CommentController',
      'entity keys' => array(
        'id' => 'cid',
        'bundle' => 'node_type',
        'label' => 'subject',
      ),

As you can see, we define the bundle key as "node_type". However, that column doesn't exist in {comment}.
So, EntityFieldQuery tries to select the column, chokes and dies.

Comments

bojanz’s picture

Status: Active » Needs review
StatusFileSize
new435 bytes

Here's a simple patch removing that key.
Let me know if an alternate approach is needed.

chx’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

That's obviously braindead. But why do we have a core that passes all tests?

bojanz’s picture

Thee hook_entity_info docs for ['entity keys']['bundle'] say: This entry can be omitted if this entity type exposes a single bundle.
However, the comment module does expose multiple bundles (one per node type), so the whole thing is just odd.
The key should be in the table, but it isn't, and it isn't used.

But then again, this is my first time looking at comment.module

So, the test would be:
1) Load all modules
2) Load every entity type
3) On each entity type, do an EntityFieldQuery. If it doesn't die, it's cool.

EDIT: And the patch breaks comments completely, so the approach might be wrong ;)

bojanz’s picture

Title: comment_entity_info() defines a bundle key for a non-existent column » Comments don't store the bundle key
Priority: Normal » Major
Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new3.14 KB

This is actually a bigger problem than EntityFieldQuery not being able to query comments.

The comment entity type exposes bundles, has a bundle key, but that bundle key (node_type) is not stored in the database, but calculated in the entity controller instead (by joining {comment} with {node}, getting it's type, and prepending "comment_node" to it). This is plain retarded.

Let's try and do this properly.

1. Added node_type to schema.
2. Added update function that adds the new column and fills it with data.
3. Removed extra join and node type calculation from the comment entity controller.

chx’s picture

Title: Comments don't store the bundle key » Remove EntityFieldQuery::entityCondition bundle
Status: Needs review » Needs work

This can not be supported. The bundle for taxonomy_terms are the vocabulary machine name however the taxonomy_term_data table only has a vid. Comments store their bundle in node. Both use a custom entityload class to assemble the entity and indeed the entity documentation talks of object properties not database keys. The only solution is to remove bundle support. Ppl using EFQ can use propertyCondition vid for terms and... they are screwed on comments. Such is life. There is no way to help that in Drupal 7 core. A contrib can do a schema alter and do the denormalization.

The rest should be ok, entity type, entity id and revision id these are ok but bundles are not.

chx’s picture

Title: Remove EntityFieldQuery::entityCondition bundle » Make EntityFieldQuery propertyCondition bundle work
  1. Change the bundle in taxonomy module to taxonomy_vocabulary_machine_name instead of vocabulary_machine_name.
  2. EntityFieldQuery loads the schema and checks whether bundle key is found in the entity base table.
  3. If not, it goes over the foreign key tables, and finds the one that is prefix of the bundle key. In case, taxonomy_vocabulary and node, and verifies that the rest of the bundle key, in case machine_name and type are fields in the table to be joined.
  4. Document all this in hook_entity_info.

Phew! We saved the bundle with minimal API change and empowered the whole system with information.

catch’s picture

Discussed this in chx with irc, the above plan is option #4 of these:

catch> I don't like this because that leaves views_efq in a bit of a bind.
<catch> 1. Leave it as is and take efq out of the picture.
<catch> 2. add machine_name to comment and taxonomy_term_data tables, use this for bundle key.
<catch> That's a schema change, but it's also a fairly painless one.
<catch> 3. Kill vocabulary id altogether and replace it with machine_name, that's Drupal 8.
<catch> 4. Find a way to tell efq how to query on bundle name when modules like taxonomy or comment do this.

I think that's the best at this stage of the cycle. We need a Drupal 8 issue to figure out a standard for entity bundles (if I remember correctly there was some resistance to storing bundles as strings for taxonomy terms and comments due to the extra storage, queries on ints are more efficient etc., but is that worth the tradeoff to avoid issues like this).

rszrama’s picture

I don't see addressed above the possibility of simply documenting the limitation for Drupal 7, i.e. that you cannot use EFQ::propertyCondition() on Comment or Term bundles. Based on the inline documentation, I see two things that would make this a reasonable plan of action:

  1. As the class documentation says, "It is not possible to query across multiple entity types." So whoever writes the query will know if it's to retrieve Comments or Terms.
  2. As the propertyCondition() documentation for the $column argument says, "A column defined in the hook_schema() of the base table of the entity." In other words, the choking and dying bojanz first reported is by design, since the node_type column isn't defined in the schema for the entity's base table. EFQ developers would just need documentation at the entity definition level on whether or not the bundle is actually defined in the schema (although they could just open the .install and check).

That said, it seems like a fix is preferable. Just wanted to point out that this isn't a technically "unexpected" bug... maybe just an unintentional limitation.

As to applying chx's solution, it seems like it would work. It's uncomfortable, though, to introduce a JOIN based on a naming convention and the use of foreign keys. I suppose it's the least invasive solution where we are now, but it's going to result in some unwieldy API documentation. : P (This should also be accompanied with documentation in the term and comment schema to indicate altering the foreign key definitions would break EFQ::propertyCondition().)

I suppose at the very least we have core examples to point to, but really we can just say any other entity developers should just make sure the bundle has a column in the entity's base table.

I think, too, that bojanz inadvertently raised a related issue above... that of entities that are defined as single bundle entities. In the case of the commerce_order entity, I don't define a bundle entity key and through trial and error found out this means my entity will have a single commerce_order bundle. Now, there is no column in my commerce_order table for the order type, so it seems the EFQ::propertyCondition() won't work for me here anyways. Maybe it's never supposed to work to query based on bundle if there is no bundle entity key. But what if a contributed module alters the entity info, adds a bundle key, creates an order types table, and then uses a third table to relate orders to their order type (i.e. instead of altering the schema to add a column onto the base commerce_order table) and alters the orders on load to include their type. The proposed solution cannot accommodate that, and shouldn't... the documentation for the $column parameter sets that expectation.

In other words, even with the fix, it can still be broken, and unless I'm misunderstanding EFQ (which is quite likely) still won't accommodate entities that don't support bundles. That might tilt this toward "don't fix, document the limitation", but if the fix is preferable for other reasons, then chx's suggestion would work, should be documented, and the method should be discouraged until the problem can be removed in D8. How exactly it should be removed for comment bundles if not just storing the type in the comment table is a little beyond me this late in the evening. ; )

chx’s picture

If your entity type does not have a bundle then dont specify a condition on bundle :) piece of cake, there. But the fix for comment, taxonomy_term and any other entity type that follows the core example of storing the bundle in another table is necessary IMO.

rszrama’s picture

Would it be worse to introduce something other than a [table]_[field] naming convention that gets checked as a fallback? i.e. what if you could specify a bundle entity key of [field] (the regular definition), or [foreign_key]:[field] (which finds the table the foreign key, joins on the key, and adds the [field] to the query)?

Of course, I'm not sure if : is any better (could get even more creative and use ->), but at least people wouldn't normally be using : in bundle names (whereas they're most likely already using _). Also, even if the _ is kept, I wonder if specifying the foreign key outright wouldn't be preferable to specifying a table and then searching the foreign keys for that table. In this regard, you could actually do away with step 1 in chx's solution and instead change the bundle entity key for comments from node_type to comment_node_type (or comment_node:type).

rszrama’s picture

Hmm, of course you can't use a : if you expect the bundle entity key to be a property on the loaded entity... duh. However, perhaps you specify the foreign key to use for the bundle somewhere else in the entity's info array. Just musing... point about referencing the foreign key instead of a table name would still stand.

fago’s picture

We actually miss information about the bundle object here. If we would have the bundle 'db table', it could check for the "bundle keys | bundle" property and query for that using the foreign-keys info. Let's just make any storage-object an entity for d8 so we have a proper and unique place for info about it...

Still, comments and terms are a bit different now. Terms use the usual "entity object - bundle object - bundle name" approach, while comments go directly with the on load determined "node_type" property. It might work to translate the comment case to "entity object - bundle object - bundle name" too, with using "node" as bundle object and "type" as bundle key.

Then for d7, we would just need something like a "bundle db table" property.

1. Change the bundle in taxonomy module to taxonomy_vocabulary_machine_name instead of vocabulary_machine_name.
2. EntityFieldQuery loads the schema and checks whether bundle key is found in the entity base table.
3. If not, it goes over the foreign key tables, and finds the one that is prefix of the bundle key. In case, taxonomy_vocabulary and node, and verifies that the rest of the bundle key, in case machine_name and type are fields in the table to be joined.
4. Document all this in hook_entity_info.

Let's not magically bake more information in a single info-key. The 'entity keys' are about the object properties, if we need information on how to find the property column in the DB, that's something different and thus it should be another info key.

bojanz’s picture

Status: Needs work » Active

Interesting discussion. We definitely need to fix bundles in D8.

Right now, the main problem is that EFQ gives a fatal error if the bundle column is not present in the table even when we have no entityCondition on bundle!
This is because the defined entity keys (such as bundle) are always added to the query in propertyQuery(), even when not used in a condition or an ordering.
The field_sql_storage query builder doesn't have this problem.
This is not a hard thing to fix. We just add an additional check that peeks into the base table schema.

As for the proposed solution with joins, I'm not really a fan of adding hacks to the code for this. Feels a bit wrong.
I'm much more inclined to agree with the previous chx's comment:

Ppl using EFQ can use propertyCondition vid for terms and... they are screwed on comments. Such is life.

We document that those two are problematic like Ryan suggested, and revisit it for D8.

Changing status, since the previous patch is not really getting us where we need to be.

EDIT: Just saw fago's comment, I see that he too is weary of the proposed approach. An additional key might not be a bad idea though, even though it's still a hack.
EDIT2: Also, shouldn't that be "Make EntityFieldQuery entityCondition bundle work"?

amitaibu’s picture

> Then for d7, we would just need something like a "bundle db table" property.

I wonder if it this property doesn't connect too tightly the idea of an abstracted entity that "doesn't care" about it's storage, to the DB?

fago’s picture

>I wonder if it this property doesn't connect too tightly the idea of an abstracted entity that "doesn't care" about it's storage, to the DB?

The whole EFQ-entity-query "backend" is db specific, just as the default drupal entity controller. This properties are used by the db-specific implementations, you still can do others. Though I'm not aware of anyone having done that already?

catch’s picture

There are not many use cases for bundle queries on comments. Recent comments on a node type would often use node_comment_statistics since that gets you just one comment per node. The vid on terms is fine. We can't drop it for nodes. So documenting the limitation seems good here. Well that and fixing the fatal error.

chx’s picture

bojanz then let's fix EFQ not to error out and document. Let's do the absolute minimal necessary.

bojanz’s picture

Title: Make EntityFieldQuery propertyCondition bundle work » Fix EntityFieldQuery fatal error in certain cases and document bundle limitations

I'm on it. Still fighting the flu, should have something tomorrow.

bojanz’s picture

StatusFileSize
new1.87 KB
new3.31 KB

I suck.

Anyway, here's are two possible options. Feel free to massage the docs in the comments bellow.

1) In patch #1:
If a user (developer) specifies an entityCondition('bundle') or entityOrderBy('bundle'), and the resolved column doesn't exist in the db, he gets an exception.
That's more developer friendly.

2) In patch #2, he just gets an sql error. We don't hold his hand for wrong property names, so why do it for bundles?

I'm for #2, but since I wrote #1 first, i thought I'd give it up for consideration as well.

bojanz’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 938462_2.patch, failed testing.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new2.54 KB
new3.98 KB

Once again, now a bit smarter.

chx’s picture

Status: Needs review » Needs work

Calling that variable having is a really bad idea. Anything else is a lot better. bundle_present or whatnot. But having is a SQL keyword and superb confusing.

bojanz’s picture

Status: Needs work » Needs review

That variable was there before me. And true, it's a bit confusing.
However, I don't think it's that bad, since propertyQuery() is tied to the SQL database...

Generally, isn't patch #2 preferable anyway? and it doesn't touch $having at all...

chx’s picture

Status: Needs review » Reviewed & tested by the community
chx’s picture

We can try harder. Field tables store bundle. (MongoDB also does. It's somewhat mandatory for fields to store it because they have no clue whether there is such a thing as an entity base table.) We have an entity type and a bundle here... field_info_instances($entity_type = NULL, $bundle_name = NULL) can deliver us the instances and then we can bring the table of any instance in. Is this something we want to do?

chx’s picture

Discussed with bojanz and we both agree that for Drupal 7, we still want to go ahead with the simple patch indicated above. It'd be a hack to join in a random field table.

bojanz’s picture

Status: Reviewed & tested by the community » Needs work

We agreed on IRC that this should get a test.

Test scenario:
1) Define an entity type that specifies a bundle key not present in the database
2) Do a query. Any query. Check that it succeeds.

We run the test without the current RTBC patch, it fails. We apply the patch, run the test again, it succeeds.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new6.95 KB
new4.45 KB

Here we are.
New patch, plus a patch with just the test changes (which should fail).

The test modifies the rarely used test_entity_bundle entity_type to behave like comment (bundle specified, added in a controller, not present in db).
Also, I sneaked in a change to EFQ tests that catches exceptions, so that we get a nice 1 test fail, and not everything aborted.

bojanz’s picture

+ * Controller class for the test_entity_bundle entity.

This should be Controller class for the test_entity_bundle entity type., but let's see first if there are other things that need to be changed...

chx’s picture

Status: Needs review » Reviewed & tested by the community

That looks good. I would have sworn we fixed that test already like that... good job.

bojanz’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new6.96 KB

Providing the wording fix mentioned in #30.
Everything else is the same, so this goes back to RTBC after the bot gives it a go.

bojanz’s picture

Status: Needs review » Reviewed & tested by the community
dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.