Problem/Motivation
EntityFieldQuery in D7 had an alter hook. When we rewrote it for D8, we forgot to add an alter hook. The dead docs were removed in #2611638: hook_entity_query_alter() is dead, remove it from entity.api.php.
However, we definitely need it. It is currently a blocker for anyone looking to hardcode their own filtering behavior. Without hook_entity_query_alter() we need to alter the underlying SQL query, which is not trivial due to field storage (requires using Tables to add the joins, and copying the EntityQuery Condition logic for selecting a LEFT vs INNER join).
For examples of the difficulty in modifying entity queries using only the current hook_query_entity_reference() see block_content_query_entity_reference_alter() and the Query Access API (#777578: Add an entity query access API and deprecate hook_query_ENTITY_TYPE_access_alter()).
Proposed resolution
Let's re-add the hook.
User interface changes
None.
API changes
Add four hooks to enable custom query filtering:
- hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query)
- hook_entity_query_ENTITY_TYPE_alter(\Drupal\Core\Entity\Query\QueryInterface $query)
- hook_entity_query_tag__TAG_alter(\Drupal\Core\Entity\Query\QueryInterface $query)
- hook_entity_query_tag__ENTITY_TYPE__TAG_alter(\Drupal\Core\Entity\Query\QueryInterface $query)
Note the double underscores to avoid potential hook collisions for hook_entity_query_tag implementations.
Data model changes
Entity queries can now be altered by hooks.
Release notes snippet
Alter hooks have been added for entity queries, making it easier for modules to modify entity queries executed by other code.
| Comment | File | Size | Author |
|---|---|---|---|
| #83 | 3001496-nr-bot.txt | 3.88 KB | needs-review-queue-bot |
Issue fork drupal-3001496
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
effulgentsia commented+1 to the idea. Can you post a patch?
Comment #6
jonathanshawAdding a hook is very simple. But on its own its of modest value, because entity queries are black boxes, you can put things into them but you can't see what's inside them; they have setters but not getters.For example, while normal queries implement ConditionInterface and have a conditions() method that returns the conditions array, entity query does not.Perhaps entity query could implement ConditionInterface, or perhaps it could have a bunch of getters like getConditions(), getSort(), getRange(), etc.Comment #7
jonathanshawMy bad, there's an issue for that:
#2502363: Add getters to EntityQuery
Comment #8
jonathanshawSomething like this.
Comment #9
jonathanshawFix lint.
Comment #10
jonathanshawAdd tagged hooks too.
Comment #11
jonathanshawComment #13
jonathanshawMissing a use statement, also omitted 2 hook invocations from field_test.module
Comment #14
jonathanshawWe now need to mock the module handler in entity query unit tests.
Comment #15
jonathanshawComment #16
jonathanshawTest results show 17 coding standards need fixing
Comment #17
jonathanshawFixed coding standards
Comment #18
tedbow@jonathanshaw thanks for reviving the issue and the patch.
Probably not the kind of review you were looking for 😞 but I think there is a case for not adding this alter.
I have been trying to think of ways we could make Drupal less complicated. I think not adding alter hooks when there is not a clear use case or even a case that it would be widely used is one way we can not add more complexity.
Comment #19
bojanz commented@tedbow
The issue summary is still valid. Once again revisited in #3086409: Provide a default query_access handler for core (maybe all?) entity types. Entity API has over 60k installs and has the only query access implementation in the entire ecosystem (which is why both Commerce and Group use it). Because there is no alter hook, we were forced to write what Berdir calls "heavy dark magic", applying access conditions directly to the underlying SQL query. Not having the hook does not make Drupal simpler, it shifts complexity elsewhere.
We still want and need to pull the Query Access API into core. And that patch should ideally be dark magic free.
Comment #20
tedbow@bojanz thanks for the extra info. I didn't realize you were solving this Entity API and it has so much usage.
Yes I would agree that it does show it is more needed than my #18. I disregard #18 unless somebody else thinks it makes a difference. I think now #19 shows the importance
Comment #21
rp7 commentedSuccessfully tested patch in #17. Makes it far more easier & elegant to alter entity queries, I welcome this :)
Comment #24
ytsurk#3250973: Compatibility With Entity Query Field Exists Method could probably make use of this.
Comment #28
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #29
smovs commentedRerolled 3001496-15.patch to 10.1.x-dev.
Comment #30
smovs commentedComment #31
smovs commentedinterdiff_15_29.txt
Comment #32
smovs commentedAdded accessCheck() to Drupal\KernelTests\Core\Entity\EntityQueryTest::testAlterHook
Comment #33
smovs commentedComment #35
smustgrave commentedWill require a change record for the api change.
Comment #36
elberChange record added https://www.drupal.org/node/3363939
Comment #37
smustgrave commentedSeems to be a copy and paste of the description on this ticket. Leaving the tag.
Comment #38
robin.houtevelts commentedI stumbled upon this issue through my RSS feed on the change records.
However it doesn't read like a CR.
I agree that it needs a different text and an example usage.
Comment #39
elberI'm sorry please. Can you give me an example of usage?
Comment #40
markdorisonThe change record for this issue has been published. It should still be a draft at this point, right?
Comment #41
larowlanYes it should be unpublished
Comment #42
jonathanshaw@elber more information on how to write a change record is here:
https://www.drupal.org/community/contributor-guide/task/write-a-change-r...
Comment #43
ptmkenny commentedI rewrote the change record and used an example from the tests. I think it would be great to have a more illustrative example if anyone has any ideas.
Comment #44
smustgrave commentedNew functions believe should be typehinted please.
Tagging for issue summary to have the standard issue template applied, especially the api section of the template.
There appear to be a few hooks being added can they all be listed in the CR please.
Comment #46
ptmkenny commentedComment #47
ptmkenny commentedOk, I updated the change record to list all hooks, and I updated the issue summary. I also made an MR of patch #32 to make it easier for myself to understand the changes.
Please review the new issue summary and change record.
Comment #48
smustgrave commentedIssue summary looks good.
Slightly tweaked the CR to include the full changes from the entity.api. May be overkill be figured it would save someone time from having to open the file.
Saved credit for ptmkenny for the issue summary update, cr changes,
Hiding patches as fix is in the MR
Ran the test only feature to verify the coverage
Also see that all 4 new hooks are being altered in the test module.
Believe this is good to go.
Comment #49
quietone commentedI'm triaging RTBC issues. I read the IS, the comments, the change record. I didn't find any unanswered questions. After reading #18 I thought that this would be a won't fix but the next comment explains why this is useful.
However, I do not see any comments that discuss a code review nor are they any comments in the MR.
I then applied the diff but there are conflicts, so this needs work. I read the MR and left two comments. Also, new functions and methods should have return type hints. See https://www.drupal.org/docs/develop/standards/php/php-coding-standards#s....
I tried to improve the change record so it was not mostly a copy/paste of code. To do that I searched the change records for others where a hook was added and used those as a guide. It is not complete, so I am tagging for change record updates.
Just a bit of finalizing to do here.
Comment #51
prashant.cComment #52
smustgrave commentedleft some comments.
Appears to have a merge conflict.
Also was tagged for CR updates which appear to still be needed.
Comment #53
ghost of drupal pastI reviewed https://www.drupal.org/project/drupal/issues/1801726 and it never actually existed and we didn't catch it in time despite the dozens of revisions I did and the dozens who reviewed it. The documentation clearly shows I initially wanted to add it but didn't.
I am sorry :(
Comment #54
jonathanshawI fixed the change record
Comment #55
jonathanshawI fixed the comments, but a rebase is still needed.
Comment #56
prashant.cComment #57
jonathanshawThanks @prashant.
My last fixes were trivial, so I'm RTBC per #48.
Comment #58
jonathanshawDoh
Comment #59
alexpottLooking at how SQL queries are altered - it only triggers the alter hook if the query is tagged. See \Drupal\Core\Database\Query\Select::preExecute(). I'm wondering if we should do the same. Ie. change the code to
Hmmm are all entity queries tagged... ah interesting, is that some tags that you'd expect to be there like ENTITY_TYPE_ID_access are only added in \Drupal\Core\Entity\Query\Sql\Query::prepare() which is called after this. Unfortunately in the current design it does not seem possible for a hook implementation to know if \Drupal\Core\Entity\Query\QueryBase::$accessCheck is TRUE or not.
Also I'm wondering about the impact on the existing entity_query and entity_query_ENTITY_TYPE_ID tags. They feel somewhat superfluous and duplicates.
Another thought is in what order should the hooks be called. Obviously hook_entity_query_alter() first... but then I think it should be hook_entity_query_TAG_alter() then hook_entity_query_ENTITY_TYPE_alter() and then hook_entity_query_ENTITY_TYPE_TAG_alter()
Comment #60
jonathanshawI'm not sure why we would do that. Mind you, I'm not sure why SQL queries do that. Any ideas why it would be good to do this?
Being able to alter absolutely any entity query would seem like a useful feature for modules like DER or workspace that want to globally enhance entity queries.
I think that might be good. In some sense these access tags seem like part of a deeper level of the API. If you want to mess with an entity query's access should do do it through accessCheck() which is it's API for the purpose; or you accept the greater responsibility of altering the Sql query.
Agreed. This is one symptom of the need identified in #2502363: Add getters to EntityQuery. Once this issue is fixed, it will get more valuable to fix that one.
Maybe - although they allow a deeper level of sql modification than entity queries currently do. Shall I open a follow-up to explore deprecating them?
Entity/Query/Sql/Query::prepare does this:
Which means that sql queries alter hooks fire in the order ENTITY_TYPE then TAG (as is the current MR) not the TAG then ENTITY_TYPE you propose. I suggest we keep to the same pattern as sql queries unless we have a good reason to be different.
Comment #61
alexpottRe the last part of #60 it's not quite the same as sql's alter tags though... because you are adding the tag on to both entity_query and entity_query_ENTITY_TYPE_ID
Also if a tag and entity type ID clash your site will have problems and that means this needs work.
I think it needs to be something like
to avoid clashes.
Comment #62
jonathanshawI don't have a strong sense either way about the order.
Good point. I'm a bit unsure about this one though:
$hooks[] = 'entity_query_tag_' . $this->getEntityTypeId() . '_' . $tag;Strictly
_tag_is not needed here to avoid collision but I can see that it might be more consistent to have it.I wondered if this is better:
$hooks[] = 'entity_query_' . $this->getEntityTypeId() . '_tag_' . $tag;Comment #63
jonathanshawI'd like is to to decide the hook name structure before I change the code as it will need changing in quite a few places.
Comment #64
alexpottThe problem with your suggestion is that it still allows for clashes consider a site the following entity type names:
two
two_tag_alpha
If there's a query with the tag alpha there will be a clash. Yes this is very contrived but someone somewhere will have a set up like this. This is why there are no hooks with SOMETHING_hardcode_SOMETHING. It's really hard to come up with something that will not clash. Also this is why we should move away from hooks and to something like hux.
Comment #65
jonathanshawThe spellchecking CI seems to be broken ... but I think this is right.
Comment #66
smustgrave commentedMR is showing as unmergable. The spell-check has been fixed a week or two ago, branch needs to be rebased with latest 11.x
Comment #67
jonathanshawThanks @smustgrave, that sorted it.
Comment #68
smustgrave commentedBelieve feedback has been addressed
Comment #69
boobaaSorry, but this only affects SQL-based entity types. I think entity queries for remote entities (like the ones in the Apigee Edge module) should also be supported. The entity.api.php changes and the change record also suggest that all entity queries would become alterable, not only the SQL-based ones.
Comment #70
berdirWe can't change an implementation that's not in core? But I do agree that this needs to be clarified/extended, and implementation that is in core are config entity queries.
Comment #71
jonathanshawUnfortunately entity query classes do not inherit their execute() method from the abstract class QueryBase, so there's no way to guarantee all entity queries have this.
Added support for config entity queries and test coverage for that, documented in CR and entity.api.php the limitations on which entity queries have these.
Thanks @berdir for thinking of config entities.
Comment #72
smustgrave commentedFor the 2 open threads from @amateescu
Comment #73
jonathanshawComment #74
smustgrave commentedCan you rebase the MR appears to have a unit failure.
Comment #75
jonathanshawComment #76
smustgrave commentedComment #78
manish-31 commentedI have rebased the MR, needs review. Thanks!
Comment #79
jonathanshawThanks @manish-31. Tests are now green. This is still NR for the changes made in response to #70, but otherwise RTBC per #68.
Comment #80
smustgrave commentedApplied some nitpicky missing :void returns.
Appears all feedback has been addressed though.
Comment #81
alexpottAdded suggestion to MR to resolve possible hook clashes suing double underscores. Note we use this technique elsewhere in core. See \Drupal\Core\Plugin\FilteredPluginManagerTrait::getFilteredDefinitions and theme hooks.
Comment #82
jonathanshawImplemented double underscores.
Although
does uses only 1 double underscore, I agree with @alexpott that we should do
The reason I can see is that 'tag_something' is a possible entity type id.
Comment #83
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #84
jonathanshawComment #85
smustgrave commentedTheme suggestion update seems good.
Have always seen the double underscore but didn't consider it was the standard, sorry!
Comment #86
alexpottUpdated change record and issue summary for the new hook patterns.
Comment #87
alexpottAdding issue credit for reviews, comments that helped shape the MR and writing the change record.
Comment #88
alexpottThe release note could do with finessing into something that can be added to something like https://www.drupal.org/project/drupal/releases/10.2.0 - the current release note is not fit for that.
Committed c536598 and pushed to 11.x. Thanks!
Committed 19ae323 and pushed to 10.3.x. Thanks!
Comment #91
jonathanshawImproved release snippet in IS
Comment #92
mondrakeThis introduced a mock method that is deprecated in PHPUnit 10, can a follow up be opened to fix it.
Comment #93
mondrake#3433052: Fix remaining return*() methods of class PHPUnit\Framework\TestCase deprecated in PHPUnit 10
Comment #94
alexpott@mondrake sure open a follow-up but I don't think we should re-open this issue. PHPUnit 10 is less important than our our code. This should remain fixed.
Comment #96
amateescu commentedAggregate entity queries were somehow missed in this issue, opened a followup for them: #3489929: Aggregate entity queries are missing the alter hooks