Problem/Motivation
While working on #3217531: Deprecate usage of Connection::getDriverClass for some classes, and use standard autoloading instead, we came across the fact that Connection::getDriverClass is also used to resolve overrides to select query extenders. This is not easily solvable with direct autoloading because the to-be-overridden class resides outside of the database driver namespaces; also, while the db driver specific classes are all necessarily extensions of the Core\Database ones, the extenders may be freely added by any module.
Proposed resolution
Try using backend-overrideable services, adding a factory for each extender that will then instantiate extender objects.
Remaining tasks
Needs followup, see #58
User interface changes
None
API changes
Using the full class name as the parameter value in the method Drupal\Core\Database\Query\Select::extend() has been deprecated. Also the method Drupal\Core\Database\Connection::getPagerManager() has been deprecated.
Data model changes
None
Release notes snippet
Select query extenders are now managed through backend-overrideable services. When extending a query, consuming code needs to switch from hardcoding the extension class to calling the extender service with the type of extension required. Contrib and custom database drivers overriding the extenders need to implement their own service. See https://www.drupal.org/node/3218001
| Comment | File | Size | Author |
|---|
Issue fork drupal-3217699
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 #3
mondrakeA Proof of Concept patch to test the waters. Feedback appreciated... is this making sense?
A factory backend-overridable service is introduced, which creates instances of the extender. Done for the PagerSelectExtender here.
Comment #4
mondrakeComment #5
andypostLooks like duplicate of #3086647: Allow Select query builder to use class resolver so we can use dependency injection
Comment #6
mondrake#5 thanks @andypost for the research.
What's changed since those issues, probably, is that now contrib database drivers should be modules (will be a must in D10), and therefore it's simpler to implement the extenders now through backend-overrideable factories.
Comment #7
mondrakeInteresting hint from @berdir on Slack, with this approach we can use DI in the factory and pass services to the extender more easily. If we had this before, we would not have had to add the
Connection::getPagerManager()method that really sounds a bit off in the Connection class.Comment #8
mondrakeNeeds some work for BC, CR, and proper deprecation testing, I'll be working on that.
Comment #9
mondrakeCR placeholder @ https://www.drupal.org/node/3218001
Comment #10
mondrakeNow reviewable again. The CR is just a stub, opened to get a number to put in the patch, but IMO better wait for feedback before really start drafting it.
Comment #11
andypostPatch still applies and needs CR update
Comment #12
daffie commentedThe patch looks good, almost RTBC.
I have created the text for the CR and I have updated the IS.
Comment #13
mondrakeThanks for reviewing @daffie
Comment #14
daffie commentedAll the code changes look good to me.
For all the code changes is testing added.
There are 2 deprecations with deprecation message testing.
For me it is RTBC.
@mondrake: I am happy to review.
Comment #15
mondrakeNW for pgsql and sqlite failures
Comment #16
mondrakeComment #17
daffie commentedThe testbot is now happy with PostgreSQL and SQLite.
The change looks good to me.
Back to RTBC.
@mondrake: Any idea why the MR was failing for PostgreSQL and SQLite?
Comment #18
mondrake@daffie re #17: I do not know. Actually given that it was failing on PHP typehint inheritance checks and hence with a fatal, it is surprising it was passing on MySQL.
Comment #19
mondrakeRerolled
Comment #20
mondrakeRerolled
Comment #21
mondrakeRerolled
Comment #22
mondrakeRerolled
Comment #24
mondrakeRerolled and update deprecation messages to 9.4.0 for removal in 11.0.0
Comment #25
alexpottI don't think the deprecations here warrant being Drupal 11. The API is not a really common one to use. Also we need a patch for Drupal 10 as the MR doesn't apply there - maybe the D10 patch could have the deprecations already removed.
Comment #26
mondrakeRebased the MR and retargeted removal to D10. D10 patch will follow once MR green. Can we please do the D10 removal in the follow-up #3260284: Remove deprecated query select extenders code, here it would be a completely separate patch and it would be hard to keep both deprecation MR and removal patch in sync for rerolls (i.e. please save developers' mental sanity).
Comment #27
mondrakeComment #28
mondrakeJust updates of the removal version in deprecation messages + rerolls + D10 patch; I think it's fine for me to RTBC this back.
Comment #29
mondrakeCS fix
Comment #30
mondrakeSo
MR 759 => D9.4
Patch in #29 => D10
Comment #31
mondrakeComment #32
alexpott@mondrake I think given we have a separate D10 patch that patch should not have the deprecations at all - there's no need.
Comment #33
alexpottCan use property promotion in D10.
Same here - all this can use use protected property promotion. See https://stitcher.io/blog/constructor-promotion-in-php-8
Comment #34
mondrakeOn it
Comment #35
mondrakeThis patch does #33.
Curious to see if the new language feature passes through commit checks :)
Comment #36
mondrakeComment #37
daffie commentedThe patch for D10 really cleans up a lot! :-)
Back to RTBC.
@mondrake: Great work.
Comment #38
alexpottCommitted d0f7178 and pushed to 10.0.x. Thanks!
Committed 96cfccf and pushed to 9.4.x. Thanks!
Comment #41
andypostThank you!!! Published CR https://www.drupal.org/node/3218001
Comment #43
mondrakeUpdated release code snippet.
Comment #45
xjmReverting to fix two things:
Our current coding standards specify type-hinting interfaces (not classes except under special circumstances). #3260507: Improve the discoverability of select extenders is trying to abstract a service for these, but it can't provide an interface for them with any return typehint because the specific return typehints cannot be compatible with a different return typehint on the parent. (PHP 7.4 fixes this, but Drupal 9 still has to not have fatals on PHP 7.3.)
For now, let's remove the return typehints, at least from Drupal 9.4. They can typehint the interface if we do #3260507: Improve the discoverability of select extenders. (Drupal 10 could still potentially have the specific class typehints, but I haven't worked through yet whether 9.4 would need to raise a deprecation warning somewhere if children of the individual getters returned other than their specific type or not. Please discuss!)
Here (and elsewhere) in the D10 patch, the method formatting violates our coding standards. The method signature should be on a single line. (Edit: And the curlies too, for an empty method.) TBH I was surprised we don't already have a phpcs rule enabled for that. :)
Thanks!
Comment #46
xjmI'm also not totally sure we should be introducing protected property promotion yet (cool as it is); it adds a bit of delay to the contribution process and makes backports a bit harder. This type of thing didn't come up during 8.9/9.0 development because API additions were frozen at the time. We don't have a documented best practice one way or the other, though, so I brought this up with the other committers for additional feedback.
Comment #47
xjmHold off on addressing #45 for now as well -- folks are discussing whether the followup is desirable. Would love input from the folks who contributed to this issue on it as well.
Comment #48
mondrakeI'll start adding extender specific interfaces.
Comment #49
mondrakeActually, after looking into it, I do not think #48 is worth doing. What the backend overridable service allows you to do is to override the factory, not the extender classes. The extender classes instantiated are kinda hardcoded, or strongly coupled if you want, with the factory. Which IMHO is OK given that when you override the factory you will also override the
get()to return an extender class specifc for your backend.In other words, if you need a different extender for your backend for an existing extender type ('pager', 'table_sort', etc), override the factory and in the factory get() specify the extender class you need. If you need a new extender type, create a base implementation factory+class and mark the service as backend-overrideable.
Comment #50
mondrakeLet's try something anyway.
Comment #51
mondrakeThis adds interfaces for both the factory and the extender instance - here just for the pager one. Sharing to get feedback.
Comment #52
daffie commentedThe added interfaces are a bit more formal, only every new interface is going to be used only once. As somebody who will overrides query extenders, I am not going to use those new interfaces. I will extend the PagerSelectExtenderFactory class and extend the PagerSelectExtender class. For me the new interfaces are not necessary. I am curous what @xjm thinks of the added interfaces.
Comment #53
mondrakeLooks like this will have to wait longer, see #3278431: Use PHP 8 constructor property promotion for existing code.
Comment #54
alexpott@mondrake well we don't have to do constructor promotion in here - we can always do it the old way and then convert in that issue. If something is 10.0.x only imo it should use constructor promotion today. I still think it is okay to use it in 10.x now and have a different patch for 9.x - the linked issue is about applying a rector rule to all of core. That's a completely different scope. But in order to proceed here let's just go the extra code and keep 9.x and 10.x the same.
The bigger issue here is how to proceed given the architecture question that emerged after the previous commit.
Comment #55
mondrakeFTR, related Slack threads:
https://drupal.slack.com/archives/C1BMUQ9U6/p1643113911159700
https://drupal.slack.com/archives/C1BMUQ9U6/p1644562126309409
Comment #56
mondrakeThis is given for done in 9.4.0-alpha1 release notes, but it’s not true.
Comment #57
xjmUgh really sorry this didn't get back in for 9.4; I had hoped this would only be a couple weeks to sort out how to address the followup's needs without committing us permanently to a new API where BC would be an issue. In a way I guess it worked as intended because we didn't come up with a compromise in time for 9.4.0-beta1, but unfortunate anyway.
I think #49 and #52 raise good points. (The framework managers raised similar points when we discussed this issue after the revert.) The question is, is there a different way we could address the discoverability concerns raised in the related issue without the interface pollution, as it were?
Comment #58
xjmAlso a related task that is still outstanding is to formally amend our coding standards to not require interfaces when it doesn't make sense to have interfaces, and replace it with basic guidance about when to use an interface or not. We sort of agreed to do that when we discussed this issue earlier, but haven't yet implemented it (and it's difficult to do so when the TWG is defunct, but since this is about architecture I think in this case framework manager agreement on a new standard would be sufficient).
Comment #59
mondrakeThanks, but how do we practically move on here? Is a patch like the commit in #38, less promoted properties syntax, targeting 10.0.x, acceptable?
Comment #60
mondrakeAlso, 9.4.0-alpha1 release notes amended, so this is no longer critical I think.
Comment #61
mondrakeTagging for framework manager review re #58 and #59.
Comment #62
bhanu951 commentedComment #64
mondrakeUnpublished CR - this was not done when the commit was reverted.
Comment #69
mondrakeMR 4452 is another attempt at this. Starting from the previous approach, this uses service aliasing to allow keeping calling the extension by class name, and finally deprecates
Connection::getDriverClass().Comment #70
daffie commentedThe solution looks good to me.
Comment #71
mondrakeI think I addressed all of @daffie's points. Thanks.
Comment #72
daffie commentedThe last couple of nitpicks.
After this it is RTBC for me.
Comment #74
mondrakeComment #75
daffie commentedAll code changes look good to me.
All my points have been addressed.
The IS and the CR are in order.
For me it is RTBC.
Comment #76
quietone commentedI'm triaging RTBC issues. I workd on this a few days ago as well.
I corrected a typo in the release snippet I saw when reading the Issue Summary.
I read the comments the other day and refreshed my memory just now. I did not find any unanswered queries there. The only thing is the followup to create at some point.
I am leaving this at RTBC.
Comment #77
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 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 #78
mondrakeRebased and added
@phpstan-ignore-next-lineto calls of deprecated methods that are left in for BC purposes. Back to RTBC.Comment #79
mondrakeRebased after commit of the transaction refactor issue.
Comment #80
quietone commentedUpdated the branch/version on the CR. I did not review the CR.
Comment #81
xjmThe followup is still needed. It's a followup, not a blocker, so we can still go ahead here. We've also made coding standards progress on allowing the protected property promotion and the multi-line constructor format.
We do still need that additional framework manager feedback.
Meanwhile, there are various quite small coding standards issues with the MR, plus any number of inline comments we don't actually need. Plus, the scope of the D11 removal of the BC layer is sufficiently beyond a simple deprecation that we should go ahead and file the D11 followup now. (An existing issue was closed as outdated -- either it should be reopened and rescoped to be a part of the D11 beta requirements, or it should be replaced by an issue that is.)
Thanks for keeping this moving forward!