From #2547833: Pager.inc -- add tests, clean it up, convert to a service, use it!:
Problem/Motivation
pager.inc uses global variables, which we are trying to get rid of, as they make it impossible to manage pagers in a OO manner. All the pager related API are procedural functions.
Proposed resolution
Make Drupal's default pager an OO service, using the capabilities introduced in Drupal 8. This will make it OO and will open it up to the possibility to replace it by alternative implementations.
Unfortunately proper cleanup would include removing the four globals in pager.inc. Since that's an API change, the best we can do in 8.x is deprecate them and include (deprecated) wrappers for the new service to maintain backwards comparability until 9.x.
In detail:
1) A 'pager' is a classed object that contains data and methods relative to a pager element. A 'pager manager' service creates 'pager' objects and keeps track of the objects created so that they can be traversed to produce the 'page' URL querystring for the pager links.
2) The global variables are retained at this stage, wrapped into the Pager methods so that methods can alter them, but also, for BC, other code can alter them independently, and in this case the values stored in the global override any value stored in the Pager object.
3) pager.inc procedural functions remain for BC as wrappers to PagerManagerInterface service or Pager methods. There are no longer global variables declared in this file!
4) template_preprocess_pager() use the new service methods instead of directly accessing the globals.
5) A PagerParameters helper service is used to extract the pager parameters from a request.
Remaining tasks
- review
User interface changes
None.
API changes
All pager_* global variables and procedural function will be deprecated. New Drupal\Core\Pager\PagerInterface and Drupal\Core\Pager\PagerManagerInterface and their default implementations will be added. BC layer introduced to allow deprecated global variables and functions to stay with same functionality until Drupal 9.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #290 | 2044435-290-interdiff.txt | 20.49 KB | kim.pepper |
| #290 | 2044435-290.patch | 62.32 KB | kim.pepper |
Comments
Comment #1
mondrakePatch
Comment #3
mondrake#1: 2044435-pager_component-1.patch queued for re-testing.
Comment #5
mondrakeJust a char encoding problem?
Comment #6
mondrakeComment #8
mondrake#5: 2044435-pager_component-5.patch queued for re-testing.
Comment #9
mondrakeWith some cleanup.
Comment #10
larowlanTagging
Comment #11
larowlanFirstly *awesome* more globals down.
A few questions/observations.
Does this warrant a helper class? Having a first class object would make a stronger API thank an associative array.
This should use Request but would that mean it's no longer drupal agnostic?
I think we prefer static over self, it allows for the class to be extended
Drupal specific shouldn't be used in Drupal\Component
For the static can we just make a protected property?
Any reason for mixing self and static?
Comment #12
mondrake#12 - not fully experienced/aware of the design decisions, so please bear with me.
Agree, can you point to an example to check?
Yes I am watching #1998696: Use Symfony Request for core includes which is addressing same thing.
OK fixed in attached patch
Aha ... if not component what would be the alternative?
OK in attached
Yes, ignorance :)
Comment #13
mondrakeMore cleanup, maybe a bit too radical... let's see bot's mood.
When this gets green I will start incorporating tests from #1588138: pager_query_add_page() [in D7, theme_pager_link()] overrides parameters passed programmatically . More test will be needed to test non-0 pager elements... I have not seen any.
Comment #14
Crell commentedI am doubtful that this issue would be accepted at this point in the cycle. It's an API change, doesn't buy us much (just lazy loading; it doesn't fix that our pager system is AFU to begin with), and there's nothing critical or major blocked on it.
Comment #16
mondrakeThis patch should fix the test failure in #13.
@14 - I know it's late but on the other hand I could find only 2 contrib modules in DrupalContrib using any of the Drupal 7 pager API functions. Is there a process to check if this could be finally accepted? Before I put any more effort here.
Thanks
Comment #17
mondrakeComment #18
Crell commentedThe verification process is here: https://drupal.org/node/2045345
Comment #19
mondrakeThanks @Crell, clear enough :)
So this won't have a chance at this stage, marking postponed.
BTW - I learn this cannot be a 'component' as it requires to access the Request object to determine the current page to position in the needed recordset page, not being Drupal agnostic then. Would it then be a 'service'?
Comment #20
Crell commentedThings in Component can be registered as a service. It would just have to live in Drupal\Core\Pager instead. A service just means "a read only object available via the container" (more or less).
Now, if we could simplify and less-fugly the pager without a hard API break, that's something to consider. Do we have options there to at least clean up the code?
Comment #21
mondrakeWell, we may still do this AND keep the current API as procedural wrappers, marked as deprecated. We'll have to retain the 4 globals though, which would buy us even less IMHO.
Comment #22
larowlanTagging to get some exposure, it might be allowable anyway given the main use for the pager (views) is in core anyway.
Comment #23
xjmSo, re-filing based on the current consensus on this issue. This is used two places in core: Views UI, and the demented hydra that is the taxonomy overview form (see #1974492: Convert taxonomy_overview_terms to a Form Controller). I'm actually sort of with Crell in that doing this this doesn't gain us this much immediately--at least, from the Taxonomy side, the form that uses this is not going to be cleanly decoupled any time soon anyway. I'll ping Views UI co-maintainers to see what they thing about the globals.
Comment #24
mondrakeFor a discussion on the Mini pager, and the need to have an 'open-ended' pager i.e. where total number of pages in the recordset is unknown, please see #2049723: Mini pager shows "Page 1" if there is non item at all available.
Comment #25
oadaeh commentedChanging the tests tag to the more universally used one.
Comment #26
mondrakeJust a reroll of #16 to keep up with changes in 8.x HEAD. No tests, no additional logic.
Comment #27
gagarine commentedWhy not using https://github.com/whiteoctober/Pagerfanta ? I'm using it for symfony project and it's pretty simple and solid.
Comment #28
larowlanComment #29
Crell commentedAt least in the abstract I'd be fine with dropping our pager and using Pagerfanta.
Comment #30
dawehner@Crell
Pagerfanta does not even support our default pager which is the mini pager. That one cannot support the AdapterInterface they provide (see https://github.com/whiteoctober/Pagerfanta/issues/145)
Comment #31
mondrakeWorking on a new patch.
Comment #32
xanoWe are on the brink of releasing RC1. You may want to check on IRC (#drupal-contribute) to see if this issue will be able to make it into Drupal 8 at all, before you spend time on this.
Comment #33
mondrakeI know this is not for D 8.0.0 :)
Still I have some ideas I'd like to share for future thoughts ;)
Comment #34
mondrakeSo, here's the concept.
1) as per previous comments, let me remark I am aware this is not for Drupal 8.0, so I have no expectations in this sense. I am setting this issue to NR for 8.0.x to let bot gnaw the patch, and I will re-postpone later.
2) this patch is based on a similar approach implemented in the D8 version of the Pagerer contrib module. The idea is that a 'pager' is a classed object that contains data and methods relative to a pager element. A 'pager factory' creates 'pager' objects and keeps a directory of the objects created so that they can be traversed to produce the 'page' URL querystring for the pager links.
3) the global variables are retained at this stage, wrapped into the pager methods so that methods can alter them, but also, for BC, other code can alter them independently, and in this case the values stored in the global override any value stored in the pager object.
4)
pager.incfunctions remain for BC as wrappers topager.factoryservice methods. There are no longer global variables declared in this file!5)
template_preprocess_pager()andtemplate_preprocess_views_mini_pager()use the new service methods instead of directly accessing the globals.Comment #35
mondrakeMaybe we can discuss this for 8.1.x, given there is BC
Comment #36
mondrakeAdding some docs.
Comment #37
mondrakeComment #38
mondrakeSome changes, mostly around the factory so that it can use a parameter to define the class to be used to instantiate Pager objects. This way in the future an alternative pager engine (here I am thinking about Pagerer module) can use the same factory class to get pager objects that are an alternative implementation class of PagerInterface, without having to swap the factory class too. Not sure this is the right way though...
Comment #39
mondrakeReroll after #2530296: Fix up docs in core/includes/pager.inc.
Comment #40
mondrakeThis would benefit from additional tests for multiple pagers proposed in #2572533: Add tests for multiple pagers on a given page.
Here I am adding a parameter in the service container, being the class of the 'Pager' objects to be instantiated by the 'PagerFactory'. This way, contrib modules (see #38) can override that class in a MyModuleServiceProvider modifier, without touching the service definition -- just pointing to a different implementation of the 'PagerInterface' interface, like e.g.
Comment #41
mondrakeComment #42
mondrakeUpdated IS and tags
Comment #43
mondrakeComment #44
mondrakeComment #45
mondrake#2572533: Add tests for multiple pagers on a given page would help but it's not a blocker.
Comment #48
mondrakePlain reroll, will fail as #2641682: permalink breaks in comment introduced in 8.1 a #route_parameters variable to the pager preprocess, need to reflect that in the patch.
Comment #49
mondrakeAdded management of the route parameters and updated the deprecation messages.
Comment #52
mondrakePatch in #49 is green for review.
Comment #53
mondrakeSimplified the Pager object instantiation, and using directly Url object instead of injected url_generator service for building links.Comment #54
mondrakeUsing directly Url object instead of injected url_generator service for building links. Disregard #53.
Comment #55
martin107 commentedThis looks like a good conversion to me so far... it does need tests :(
Just picked out a few ultra trivial nits while following along.
[mostly @file is deprecated ]
Comment #56
mondrakeThanks for review and patch, @martin107!
I think there should be an empty line between
<?phpandnamespace Drupal\Core\Pager;. Same for other files.Comment #57
mondrake@martin107 can you by any chance review #2572533: Add tests for multiple pagers on a given page? This would add a significant test for this issue too. Then yes, PHPUnit tests would be needed. CNW for #56.
Comment #58
martin107 commentedI have added the newline.
Thanks for the nudge I will have a look at
#2572533: Add tests for multiple pagers on a given page
sometime this weekend.
@mondrake I have only looked at the surface of that issue.. but between here and there there is a lot of work of yours that looks good and is unreviewed ... that is a shame. Is there anything else you want me to look at?
Comment #59
mondrakeCleaned up a bit the Pager class.
Comment #60
andypost+interface PagerInterface extends ContainerInjectionInterfaceSuppose this interface should define only pager related methods, so container injection is not needed
Comment #61
mondrake#60 OK thanks
Comment #62
mondrakeAdding a
PagerFactoryInterface::getFirstAvailableElementId()method to return the first pager element ID free for use.Would be useful in #1202484: Improve usage of 'pager.manager' service in PagerSelectExtender, allow code to know the pager element ID used, but I think it's better to have it in the interface earlier than later - in case this lands in a minor but the other not, then it will be difficult to change the interface.
Comment #63
mondrakeAdding PHPUnit tests for the
PagerFactoryclass. Next, tests for thePagerclass.Comment #64
mondrakeAdding PHPUnit tests for the
Pagerclass.Comment #65
mondrakeComment #67
mondrakeComment #68
mondrakeHere I am dropping the::findCurrentPagemethod fromPagerInterface. We can work exposing only::getCurrentPage.Comment #69
mondrakeSorry this should be right.
Here I am dropping the
::findCurrentPagemethod fromPagerInterface. We can work exposing only::getCurrentPage.Comment #70
daffie commentedMy first review for the pager service:
I am not sure if this right or not, but I have noticed that there are no other classes defined like this. Can you explain.
The name of this method is a bit vague. Not sure what would be better. Maybe something like getPager()?!
The name of this method is a bit vague. Not sure what would be better. Maybe something like getAllPagers()?!
Can you add some more documentation what exactly is a pager element. Also in relation to the other parts of a pager.
Maybe a stupid question, but why not use the create() method instead of init()?
You do not define those variable in the interface , so what the interface is concerned they do not exist. They are just method parameters (@param).
Can you add some more documentation what exactly is a pager link. Also in relation to the other parts of a pager.
The Pager::create() method get it inheritance from ContainerInjectionInterface only you add an extra parameter to the method. Not sure if that is allowed. What is this method return value?
Can we add typecasting:
$this->variable = (int) $variable.The file core/core.service.php had some additions, so small reroll is needed.
Good work mondrake. Lets work on this together and get this fixed.
Comment #72
mondrakeThank you for review @daffie, will reply soon. Bumping to 8.3.x.
Comment #73
mondrake#69:
1. Please see #38 and #40. The idea is to make the actual class used by the pager factory configurable, so that it could be swapped by alternative implementation without having to swap the factory too. This would be useful for e.g. #1818040: Pager should start counting from 1, not 0. Using config or plugin system seems to me too heavy. I'm open to any alternative, but I would like to avoid a situation like image.factory where the use of Drupal\Core\Image\Image is hardcoded.
2. and 3. 'image.factory' has a 'get' method to fetch an image. Symfony Request uses 'get' and 'all' to retrieve individual or all sub-components of a request. I was following that pattern. I am open to any change, but let's get more input here before doing any change in code.
4. OK, done
5. 'init' is the equivalent of 'pager_default_initialize'. Sometimes (see for example #2770649: [PP-1] Refactor OverviewTerms form to use the pager.factory service) you may need to retrieve the current page from the URL before you can initialize the pager. Also, if we were to put $total, $limit in the pager 'create' method, we would have to expose it also in the factory 'get' method. But that's not desirable: the 'get' should just return the object in its current status, because when it's used in theme preprocessing we are not going to set any variables but just getting them.
6. Copy/past error, sorry. Should be OK now.
7. OK, done
8. Shall we have a separate 'create' method in 'PagerInterface' then? Return value is an instance of the Pager object itself.
9. OK, done
10. AFAIK 'deprecation' means signalling that methods/functions should no longer be used, not that we should remove all their usages (yet). If I do that we will just scare people away from review :) as the patch will grow very large. I have already a couple of followup issues here, see #2770649: [PP-1] Refactor OverviewTerms form to use the pager.factory service and #1202484: Improve usage of 'pager.manager' service in PagerSelectExtender, allow code to know the pager element ID used. My suggestion would be to file all other followups based on scanning usages and refer to them in the parent issue #2547833: Pager.inc -- add tests, clean it up, convert to a service, use it!.
Comment #74
daffie commentedMy second review for the pager service:
Minor problem with these function variables. I am fine with using these variables or with using the one you get from the pager object. But now we are mixing them in the rest of the function and that is confusing. Like $pager_current and $pager->getCurrentPage(). If we choose for using the pager object maybe we should add some extra method to the pager object.
I have seen that there is an issue to change the pager from 0-indexed to 1-indexed. If we want to use the same PagerInterface for that, then we should remove the "0-index" parts from the PagerInterface.
Not 100% sure, but I think that the pager objects are indexed by their element. Can you add that to the documentation.
Are the pagers indexed by their Element or by their ElementId? Or rename the method to getFirstAvailablePagerId?
Array of pager object indexed by their element.
Todo: review the tests.
Comment #75
daffie commentedMy review of the tests for the pager service:
Pager element should be 2.
Can we also check that the elements are 4 and 6.
These tests are not testing much. With the method getCurrentRequestQuery() being mocked.
Comment #76
mondrakeThanks @daffie!
#74
1. Let's not confuse the properties of the Pager object with the local variables used by the preprocess to determine the actual pager links to be shown. I've renamed the latter.
2. 'element' comes from the Pager render element (Drupal\Core\Render\Element\Pager) and previously from the 'pager' theme. I agree it's not ideal and 'id' would be better. OTOH, it will be confusing for themers as we need to keep the 'element' naming on renders/themes.
3. Actually I think that alternative implementations SHOULD keep the 0-index internally, and only use a 1-index version of the pege number in href links.
4. OK, done
5. Renamed the method.
6. OK, done
7, 8, 10. OK
9. OK, done
11. let's do when we have maintainer's feedback on the patch here
#75
1, 2 . OK done
3. Understand but OTOH we're not in the business of testing the Request object here
Comment #77
mondrakeComment #79
mondrake#76 is green.
Comment #80
daffie commentedI think the patch looks great now and almost ready for RTBC. I have one point left:
I think we had a miscommunication. The change I was looking for was
$this->assertSame([4, 6], array_keys($this->pagerFactory->all()));. We are testing the method PagerFactory::all().Thank you for explaining why we cannot rename the element part.
I have added a change record.
Comment #81
mondrake@daffie thanks a lot for review and for adding the CR.
Here I am doing #80, plus added an additional parameter in the container to be able to specify the querystring parameter to be used by the pager to prepare the URL, i.e. the 'page' piece. Alternative implementation may want to use a different string for that (see the discussion for 0-index vs 1-index). Looking ahead at followups, caching will retrieve via 'page' hardcoded ATM, so I added a method from the factory that tells what the string in use is.
Followups suggested to remove all usages of deprecated variables/functions:
Comment #83
mondrakeTrying to fix the test failure.
Comment #84
mondrakeComment #86
mondrake#83 is green.
Comment #87
daffie commentedIt all looks good to me. All my remarks are explained or addressed, so RTBC from me.
Good work mondrake!
Comment #89
daffie commentedBack to RTBC.
Comment #90
dawehnerSorry to move it back to needs work, but given we can improve method names and what not, I think its not the worst idea to move it to to needs work :(
It doesn't mean at all that the patch is wrong, but rather it needs some additional work.
It is a bit weird to make the class configurable, as we don't do it anywhere else.
This comment is also a bit not helpful. Maybe just skip it is not too bad.
At least for me this comment doesn't really add value. It would be otherwise better to explain WHY the calculation is done in that way.
This method is returning a URL, so it should be named getUrl() or maybe even toUrl() which seems to be a pattern we use more and more.
Can we clarify what current means on the interface? Is it every initialized one or is it every available in the current query parameter of the request?
It is a bit odd that we need both.
We could maybe replace all those methods with a method toUrl($route_name, $route_parameters, $options), which then also sets the query parameters of the page properly
As written before, I think getLink is not the best name for it :)
Can't use use
https://phpunit.de/manual/5.5/en/appendixes.annotations.html#appendixes.annotations.backupGlobalsdirectly?PHP is case insensitive, but still let's use
getFirstAvailableElement()Comment #91
mondrakeThanks for your review @dawehner.
1. See #73.1, #38, #40. My intention is to be able to swap the implementation of the
Pagerclass without having to swapPagerFactorytoo. If there are better ways, let me know. I will surely use this in contrib.2. OK
3. Was there in
pager_default_initialize, changed.4, 7, 8. OK, done
5. Changed the doc
6. One method returns the value of the parameter in a specific request (e.g. 0,1,6), the other the name of the parameter (i.e., 'page'). Changed the method name of the latter to avoid confusion.
9. I thought about that. but that would apply for any global. Since we know the exact names of the globals involved, I thought it is more accurate to make the test excluding those only. No problem to change that if you insist.
10. OK
Comment #93
mondrakeSorry, I misnamed the interdiff in #91.
Comment #94
dawehnerWell, its a pattern we don't use anywhere else in core. A reason why I personally believe its not that useful is that if you replace a class, you also often need new dependencies. When this is the case, the container parameter doesn't help you.
It just feels safer to let PHPUnit handle it directly, as they maybe more things, for example when a test fails ... one never knows.
Comment #95
mondrakeThat's why in the patch I am passing the entire container to
Pager::createso that the class itself can load any service it needs.Anyway. This is minor, workaround is possible in contrib. I will post a new patch without this and with the backupGlobals change.
Comment #96
dawehnerThank you @mondrake!
Comment #97
mondrakeHere we go, let's see testbot.
Comment #99
mondrakeStrange, let's see this
Comment #101
mondrakeLooks like backupGlobals disable on class level is not enough
Comment #103
mondrake... and that values of global variables are not cleaned between different tests within a same test class ...
Comment #104
dawehner@mondrake
Sorry for causing that trouble :(
Comment #105
mondrake@dawehner no problem, it's all good learning. Anything left?
Comment #106
daffie commented@mondrake: Can you tell me how you are going to override the Pager class in contrib?
Comment #107
mondrake@daffie we will need to override the PagerFactory class with another one with a call to $container->set in the contrib module service provider class. That class will extend from core PagerFactory. In that class we will change the ::create method from using the Pager class to the one we need. (theoretically, have not done that yet, but that should do:))
Comment #108
mondrakeActually, see https://www.drupal.org/node/2026959
Comment #109
daffie commented@mondrake: Thank you for the explanation.
The patch is almost RTBC for me. I do have some minor documentation remarks:
Can we change the test to be the same as with Pagar::init().
Ceil() is not the nearest integer, the next integer by rounding up the calculated value.
We are not using the pager globals in the PagetTest, so these comments are not nessecary.
Comment #110
mondrake#109
1. ? we are removing that comment from pager.inc with this patch. Or I do not understand?
2. OK, thanks, done
3. Actually we are using the globals in the test, indirectly, when doing
we are calling
Pager::createwhich uses the globalsComment #111
daffie commented@mondrake: For 110.1 you are right and my mistake. For 110.3, thank you for explaining.
The patch look great again.
All remarks of @dawehner are addressed. Thank you for your help @dawehner!
For me it is back to RTBC.
Great work @mondrake!
Comment #113
mondrakeLooks like #112 was a random failure.
Comment #115
daffie commentedTestbot problems
Comment #116
alexpottThis is really icky change. The change from pager_ to list_ doesn't help understanding at all. Is there no way that more of the logic here can be encapsulated in the class? In an ideal would the template_preprocess_pager would not exist or have the minimum logic possible.
Comment #117
mondrake@alexpott thanks for feedback
IMHO I will not move code from preprocess to the Pager class. The classes introduced here are about managing paging in 'abstract' i.e. independently from what of a pager we want to render on a page. With the classes here we answer 'how many pagers on a request', 'how many items/pages is pager x managing', 'how many items per page has pager y', 'give me a link to go to page z for pager w', etc.
Whether we want to render
<< First < Previous ... 8 9 10 11 12 ... Next > Last >>(like full pager) or< Page 10 >(like views mini pager)is a matter of styling/theming, and may differ significantly from theme to theme, so I would not move the theme specific code to the 'general' classes.
In the Pagerer contrib module that's more or less what I am doing - introducing a plugin system so that different 'pager styles' can all be accommodated through a single theme - the theme preprocess in that case handles over preprocess to each 'style' plugin.
We can explore this for core if desired, but it'd be a separate issue IMO - not about converting pager.inc to a service but to provide a flexible mechanism for styling pagers.
That said, I can easily revert the variables' name change back from list_* to pager_*. Please set to CNW if we need that.
Comment #118
daffie commentedTalked to @alexpott on IRC and wants the changes to core/includes/pager.inc changed back as he stated in comment #116. This patch does that.
Comment #119
mondrake@daffie this won't work because you are re-introducing usage of global variables.
I suggest to just change back $list_* to *$pager_*
Comment #120
mondrakeWorking on it
Comment #121
mondrakeHere it comes. Sorry @daffie to step on your toes, and thanks for checking with @alexpott.
Interdiff still against #110.
Comment #123
daffie commented@mondrake: You did not step on my toes. I should have looked better add the patch. Thanks for fixing it. Just happy that we got this issue moving again.
The changes that @alexpott wanted are done.
Comment #124
alexpottThis is odd... we're borrowing from the \Drupal\Core\DependencyInjection\ContainerInjectionInterface but we're not implementing it. Why is this not just all in the constructor? Seems unnecessarily complex and if someone did just used the constructor atm it wouldn't work. Especially since this method will be largely redundant once the bc layer is removed.
Comment #125
mondrakeThanks @alexpott. Changed to not use ContainerInterface and moved BC layer into constructor.
Comment #126
mondrakeComment #127
daffie commentedLooks good to me.
All wishes of @alexpott are implemented.
For me it is RTBC.
@alexpott: Thank you for reviewing.
Comment #128
daffie commentedWrong status.
Comment #129
alexpottThese all need to be set up correctly for BC to be maintained but it does not look as thought they are.
Why is this not part of construction? What's the point of an uninitialised pager?
This should validate that it is a possible page.
I don't think this should be changeable after construction.
Comment #130
mondrakeI need to think in depth about #129. In the meantime, if anyone want to take this on, feel free.
Some remarks:
In theory, I agree. But here we have the
$pager_*global variables in the equation, at least until D9. Contrib/custom code can alter them directly without using the API, either creating new pager elements outside of the Pager objects, or chaning settings of instantiated ones. These methods are there to allow changing them through the API and avoiding direct calls.See #73.5 - you may have the case where you need to know the current page before to calculate the total items to be paged. See #2770649: [PP-1] Refactor OverviewTerms form to use the pager.factory service for example. Need to think if there is an alternative.
Comment #131
mondrake#129:
1) made the Pager immutable after instantiation. We now have a
PagerFactory::setmethod that creates new pagers, and aPagerFactory::getmethod to retrieve the Pager object during theme preprocessing.2) removed
Pager::init, ::setCurrentPage, ::setTotalPages, ::setLimitand::setTotalItemsfrom the interface.3) Fixed a bug that prevents taxonomy/src/Form/OverviewTerms to produce a pager, at least with this patch applied: the global
$pager_limitswas not set by code. This class uses the globals directly instead of the current API; refactoring in follow-up #2770649: [PP-1] Refactor OverviewTerms form to use the pager.factory service4) adjusted PHPUnit tests.
It will be interesting to refactor
views/pager/SqlBasethat also manages globals directly. But that will be a follow-up.Comment #132
daffie commentedLooks good @mondrake. I think that that all points of @alexpott are addressed. I do have some other points:
Here are the pager global variables set and we are removing that. The problem for me is that AFAIK in the Pager service they are not set anywhere.
Can we move the line
->setLimit($limit);2 lines up. So that we can replace $limit with $this->getLimit() in the other line.Why are we using the protected method create? Can we not just call
new Pager()directly. Can we then remove the method create?Nitpick: should be "// max(array_keys($this->pagers)) + 1 directly."
I am missing why this is in this patch. If you found a bug in OverviewTerms, then create a new issue for it. It will then also need testing for the bug.
Comment #133
mondrakeThank you @daffie for your reviews and continued support, really appreciated.
1. They are. The magic lies in this BC layer code:
By using the & operator, we are instructing PHP to actually read/write the values of the Pager properties from/to the corresponding global variable array element. In other words: by doing
$this->limit = 50we are actually doing$pager_limits[$this->element] = 50.2. Done, but see point 5 below.
3. This is just there for DX purposes: it's much more convenient to have this one line method in case you override the class (and I am planning to do so in contrib), as you would just have to replace this method with instantiating the PagerInterface class you need instead. Otherwise you'd have to replace the entire
setmethod, but this may become difficult to keep in sync if it will change in the future.4. Done, thank you.
5. This is actually a problem only since we have changed Pager to be immutable. It's not a bug of OverviewTerms in itself, it's related to the fact that that form handles directly global
$pager_*variables, and how we treat such case. This gives me the opportunity to explain how the BC layer works:$pager_*variables directly, without using the existing APIpager_default_initialize, for element 0template_preprocess_pagerstarts processing the pager, itgetthe Pager object for element 0 from the pager.factory service$pager_*variables have values for this element, the BC layer kicks-in:$pager_*variables, and then allowtemplate_preprocess_pagerto use it for its own purposes.Now, in the specific of OverviewTerms, the global
$pager_*variables are set with the exception of $pager_limits[0]. This causes a division by zero in the code that is calculating the total pages:->setTotalPages((int) ceil($this->getTotalItems() / $limit))But I agree, we should work around this rather than fixing it, since other code in contrib/custom may do the same and we cannot expect that code to change if we are to provide BC.
So here I reverted that hunk, and made sure that if we have a 0 limit or no limit at all, we do not calculate
$this->totalPages. Since$pager_total[$element]is already set by calling code, we are fine.In any case, we should follow-up to remove usage of the
$pager_globals from the code base. For OverviewTerms, this is done in #2770649: [PP-1] Refactor OverviewTerms form to use the pager.factory service. See the do-not-test patch in #9, that's very simple once we will have this in.Comment #134
mondrakeand the new patch...
Comment #135
daffie commented@mondrake: Thank for your new patch and your responce in comment #133/#134. For points 2-5 thank for fixing/explaining.
If I take the line
$this->limit = &$pager_limits[$element];then AFAIK the value$this->limitis set to&$pager_limits[$element];and that is it. The value of&$pager_limits[$element];does not change in this line. For me the point from comment #132.1 still stands.Comment #136
mondrake@daffie re. #135:
1. Using the & ampersand we set up a reference, see http://php.net/manual/en/language.references.whatdo.php. This means that
$this->limitand$pager_limits[$element]share the same storage.$this->limit = &$pager_limits[$element];sets the reference in __construct$this->setLimit($limit), still from __constructsets the $limit property of $this, which is mapped to $pager_limits[$element].
Believe me, if this was not the case we would have tons of failures :)
2. Fine, done here, we can do in follow-up but then please update the issue summary which is still declaring
Comment #137
mondrakeComment #138
daffie commentedNow do I get it. In this part are the variables linked to each other:
And in the next part are the values set:
Sorry for being a bit slow.
I am happy with the patch now and all of @alexpott points are addressed. So back to RTBC.
Comment #139
alexpottAre these really necessary as public API it feel totally wrong to be exposing this here. The only external use is in page_get_query_paramaters() and afaics we should have a method that does this on the factory and not these other methods. Also you are removing the static cache - why? Furthermore if we moved all of this internal to the factory it wouldn't be necessary to expose the getPagerParameterName() either. Hence the entire implementation would become internal to the factory and the implementation details would not be being exposed by the factory interface.
Comment #140
mondrakeThanks @alexpott.
#139: all done, with exception of getPagerParameterName() that I suggest to keep on the interface. Other code needs to know what is the parameter used for 'page', that alternative implementations may change. See for example Cache/Context/PagersCacheContext. In follow-ups of this issue we should remove hardcoded 'page' strings with calls to getPagerParameterName().
Comment #141
mondrakeComment #142
daffie commentedAll points of comment #139 from @alexpott are addressed. And it all looks good to me. So back to RTBC.
Comment #144
daffie commentedBack to RTBC.
Comment #145
pwolanin commentedbump - has been ready for 3 months?
Comment #146
pwolanin commentedThe service name doesn't seem right - it's not a factory if you are working directly with it? Looks like it more like a pager manager?
Also - should $element default to 0 in some or all of the methods? Seems you are relying on the the functional wrappers for that. The name "element" also seems a little odd and confusing in terms of form or html elements. why node something clearer like
$pager_id?Comment #148
daffie commentedThe testbot failures are not related to this patch. So, back to RTBC.
Comment #149
mondrakeMy comments on #146:
This patch has been sitting in the RTBC queue for the most part of the last three months, and this was not raised. Maybe let's see further opinion before making a change. BTW
image.factoryalso has methods called directly.IMHO we should move away from defaulting $element to 0 - this causes confusion I think when people want to place multiple pagers on a page. Generally I think code should not have a problem to know which is the element it needs to use.
This question was asked already in #74.2 and answered in #76.2.
Comment #151
mondrake#140 is still green after some unrelated test failures. Back to RTBC
Comment #153
daffie commentedThe testbot failures are not related to this patch. So back to RTBC.
Comment #155
mondrakeComment #157
daffie commentedComment #158
catchI agree with @pwolanin's review on factory vs. not.
The pager parameter name should be a constant on the interface rather than a method. Then the various places hard-coding 'page' now would just have to use the constant rather than get the service. I thought I'd posted this weeks ago, but apparently not :( I think that would resolve all of @alexpott's concerns in #139 then, although I haven't discussed this recently with him.
Comment #159
mondrake#158:
1. That is 'pager.manager' and PagerManager / PagerManagerInterface in place of 'pager.factory' and PagerFactory / PagerFactory Interface, right?
2. I disagree on setting a const on the PagerManagerInterface for the 'page' string. This would anyway force usage of 'page' all across. My intent was to be able to have an alternative string to be used in a contrib module, but with all the rest of core code still being able to use it.
For example: instead of a URL
?page=1,,3, have an URL?pagx=2,,4(in this case, to have 1-based page indexes on the URL, see #1818040: Pager should start counting from 1, not 0).Comment #160
mondrakeThis is doing just the rename as per #159.1. Let's have this then I will suggest a compromise for #159.2.
Comment #161
mondrakeComment #162
mondrakeLet's see if this passes, comments later.
Comment #163
mondrake#162:
PagerManagerInterface::PAGER_QUERY_PARAMETERconstant value equal to 'page';PagerManagerInterface::getPagerParameterNamefrom public to protected, removing from PagerManagerInterface. I keep the method so that alternative implementation of the service inheriting from PagerManager can simply change that method to change the parameter name without having to adjust all other methods too;::getLinkQueryParametersfromPagerInterfacetoPagerManagerInterface, so that Pager does not need to know the query parameter name to use;PagerManagerInterface::getPagerQueryParametermethod to be able to retrieve the entire value of the pager query parameter from the URL (not the page specific for the element), and uses it in Cache\Context\PagersCacheContext.Comment #165
mondrakePatch in #162 is green, for review.
The Php 7 test failure is actually failing the same in HEAD atm.
Comment #166
andypostI'd better get rid of "manager" - it makes no sense and tells nothing about what this service doing also visualy it's reads like page manager
More over it's a factory so 'pager.factory' and PagerFactoryInterface imo is what it should be
Comment #167
mondrakeRe. #167:
'pager.factory' is ruled out by core committer's comment (@catch) in #158 after input from @pwolanin in #146. 'pager.manager' is following on @pwolanin suggestion in #146.
Comment #168
mondrakeSmall tuning of the PagerManagerTest.
Comment #169
mondrakeUpdated IS.
Comment #170
daffie commented@Mondrake: Would it be an idea to remove the method getPagerParameterName() from the PagerManager. You say that the method will be needed with #1818040: Pager should start counting from 1, not 0. If that is so then we can add the method in that issue. I understand why you want the method, but the problem is that the core committers have a different opinion.
I must say that all the changes that you have made in the last few patches look good to me. Thank you for all your work on this issue.
Comment #171
mondrakeThanks for support @daffie. What to say. A' la guerre comme à la guerre. This patch is removing getPagerParameterName.
Comment #172
daffie commentedAll changes look good to me.
All problems that @catch has pointed to in comment #158 are addressed.
So back to RTBC.
Comment #173
alexpottSo the problem with this patch and the reason why it is taking so long to get done is that the new architecture is not well thought out yet. If these methods where not here then this object would indeed just be a factory for pagers. Part of the new architecture needs to be thought out so that it makes sense and does not overload the responsibilities of each thing. When responsibilities are overloaded you end up with @andypost wanting something to be called a factory and @catch wanting it to be called a manager.
I really recommend stepping back and looking at the responsibilities of each new thing introduced by the patch and asking ourselves what's the best way to organise it.
Comment #174
alexpottWhy does this exist? There are no usages in any runtime code.
Comment #175
daffie commentedI have asked @alexpott on IRC for some guidance for getting this issue solved. His response was:
and
Comment #180
mondrakeComment #181
mile23This needed a re-roll, so I started over and simplified based on #175.
The scope here is to make all the functions in pager.inc into something else, so they can be deprecated.
With that in mind, we make a relatively simple shim service which has a bunch of protected accessors for the globals, for BC. The accessors could be easily replaced with class properties at some point in the future, when we don't have to provide BC.
Turning the associated pager arrays into a Pager class ends up being overkill because we're required to synchronize both the objects and the globals for BC. This way we provide a thin layer which can be modified in the future without breaking BC.
Future use-cases can become their own public methods as needed, using the 'backend' protected accessors. See the many closed issues in #2547833: Pager.inc -- add tests, clean it up, convert to a service, use it! which should really be postponed on this one.
Since there's not much test coverage for this stuff, a follow-up would convert existing code to use the new API, because this issue should only prove that the new API is compatible with code that manages the globals. There are also some issues to convert system tests dealing with the pager to BTB, so let's not @trigger_error() yet. #2984185: Convert system functional tests to phpunit for page and pager
And after all that, a subsequent follow-up for D9 will remove the use of globals, assuming we still need it.
Comment #182
mile23Comment #183
mile23Updated some docs, CS, added obvious test, added interface so it's clear we've got a public face vs private sausage-making.
Comment #184
mile23Switching the parent so we focus on deprecation as the scope.
Comment #185
kim.pepper#2984185: Convert system functional tests to phpunit for page and pager is fixed so we can add the trigger_error() now :-)
Comment #186
mile23Added @trigger_error(), added a deprecation-only test.
I expect there are functional tests that use paging that are going to fail based on the deprecation errors.
Comment #187
mile23Comment #189
mile23Removes usages.
Comment #190
kim.pepperI'm not sure what the policy is. Should this be \Drupal\Core\Pager\PagerManager::findPage() instead?
ditto
ditto
ditto
Comment #191
martin107 commentedI am afraid the suggestion will not work.
PageManager::findPage() is a call to a static method.
Internally findPage makes use of $this->requestStack - having a $this->xyz make this un-static.
same for defaultInitialize(), getQueryParameters(), queryAddPage()
I hope this helps.
Comment #192
mile23Fixed inline docs usages.
Updated CR.
If someone wants to update the IS that'd be great.
I'll file the follow-up for D9 after RTBC.
Re: #190 The thing you're supposed to do instead of calling those functions is to get the service and use it, rather than make a new PagerManager object.
Comment #193
kim.pepperYes, I know. I was talking about specifying the class the method is on rather than the service.
Comment #194
darvanenI think @kim.pepper is right.
Services can be accessed in multiple ways, not just side-loading as shown in the example. We should be referencing the class and leaving the implementation of that up to the developer.
For example:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Session%2...
Comment #195
mile23This is a WIP patch. Feel free to review despite test fails. :-)
Fixed references to service names to be the interfaces for #191-#194.
Added another service:
pager.request. This separates concerns for the pager information you get from the request, as distinct from the pager info in the globals.Needs to decide on names for things, because
pager.requestforRequestPagerInterfaceisn't that great a set of names.Since part of the focus here is factoring pager.inc away, converted some methods to public to support
template_preprocess_pager()and others.Still need to figure out where to put
template_preprocess_pager().I worked on removing some usages of the globals, in order to find use-cases to round out the API, but that work is not included here because some of it is quite complex. Let’s do it in follow-ups.
Drupal\taxonomy\Form\OverviewTerms::buildForm()references this issue in a @todo, so update that for the follow-up.Follow-ups:
Remove usages of the globals.
Bring views' pager handling into harmony with the service or vice-versa.
D9 remove the globals and use something else as a 'backend.'
Comment #197
mile23This should be the patch.
Still needs followups from #195 and elsewhere.
Comment #198
mile23Comment #200
martin107 commented2 minor refinements....test failures remain.
a) Plumbed requetsStack down into PagerManager.
b) Removed unused use statements.
Comment #202
mile23Actually the problem is that the one test wasn't updated. So now is has been.
This patch comes off of #197.
Comment #204
andypostThat could lead to serialization issues
Comment #205
kim.pepperShould these link to the change record?
Need to add the method on the interface.
The trigger_error should be first?
Same here
This can be injected.
Same here
Missing docs
Unused import
Comment #206
kim.pepperRe #204 I assume adding use DependencySerializationTrait; is enough here?
Did a few fixes for my own feedback in #205 as I'm keen to see this issue move along. :-)
Comment #208
kim.pepperComment #209
kim.pepperThe current patch is pretty much a like-for-like of the existing array madness. I'm going to try to make a nicer API which uses value objects for the pager.
Comment #210
mile23See #181 on why I gave up on the value object idea.
We have to provide BC for all contrib that might use this API, and that means using the globals. Value objects then have to be stateless and always get their value from the globals and set their value to the globals.
Comment #211
kim.pepperThanks @Mile23. Yep understood. I'll keep giving it a plug to see if I can make it work.
Comment #212
kim.pepperHere's where I am going with the value object approach.
Thoughts?
Comment #214
kim.pepperRe-roll
Comment #215
andypostMaybe use arrayAccess and put this new shiny objects inyo globals?
It could help to trigger deprecation error for custom/contrib code to catch bc usage
Comment #217
kim.pepperFixes for #214
Re: #215 @andypost I'm not sure how that would work. In this patch, we're moving from separate global arrays containing pager information, to a single static array containing Pagers. How would array access help?
Comment #219
kim.pepperFixes minipager.
Comment #220
kim.pepperComment #222
kim.pepperWe need to setCurrentPage() after setting the TotalPages in the Pager constructor.
Comment #224
kim.pepperRemove use of globals in views and taxonomy.
Comment #226
Anonymous (not verified) commentedi'll have a go at moving this one forward again. thanks kim.pepper for the pointer.
Comment #227
Anonymous (not verified) commentedjust a reroll, #225 no longer applied. i've only tested that install works. let's see what is broken.
Comment #229
andypostquick fix for most of failures
Comment #232
claudiu.cristeaThis removes the
drupal_static()frompager_get_query_parameters(), making this issue also part of #1577902: [META] Remove all usages of drupal_static() & drupal_static_reset() .Comment #233
kim.pepperNeeds reroll after #3067584: Pager template renders attributes that do not exist
Comment #234
andypostRerolled and fixed message
Comment #235
andypostAdd hunk lost in 227
Comment #237
andypostSome clean-up and fix
Drupal\Tests\views\Kernel\Plugin\CacheTesttest, views pager still needs some loveComment #239
andypostFix one more test (RequestPagerTests renamed to RequestPagerTest)
Also checking how static cache removal affects...
In #204 I already pointed that current request may change in stack
So better to check splObject of current request somehow
Comment #241
kim.pepperI think I found the issue with the views tests fails. We weren't setting the current page from the request parameter.
Comment #242
kim.pepperInjecting dependencies into \Drupal\views\Plugin\views\pager\SqlBase
Also, I'm not super keen on RequestPager as a name. Perhaps PagerParamsHelper instead?
Comment #245
kim.pepperFixes tests fails in #242
Comment #247
andypostYep, #245 is like #3067584-17: Pager template renders attributes that do not exist
Patch removes useless var & fixes remaining test but this fix means that views integration still not ready
Next 2 methods calling each other to many times to affect globals and calculate view page
Comment #248
kim.pepperWoohoo! First green patch in 9 months!
Comment #249
jibranI had a look at views pager. TBH, this code is a bit of a mystery to me but the only questionable thing I could find is:
The request object
\Drupal\views\ViewExecutable::$requestand the current request in request stack might not be the same.Anyway here is reroll.
Comment #250
kim.pepperInstead of RequestPager how about these alternative names?
Comment #251
andypostMaybe just
PagerParametersand #249 is right - probably it needs static methodPagerRequest::fromRequest()Comment #252
kim.pepperRe-roll of #249
Then we need to handle both
RequestStackandRequestinRequestPagerwhich I think is ugly.In
\Drupal\views\ViewExecutableFactory::get()we are doing:$view->setRequest($this->requestStack->getCurrentRequest());Wouldn't that make it the same request?
Comment #253
jibran@kim.pepper can we remove 'Needs issue summary update' and 'Needs followup' now? If not then let's update the IS and add some followups.
Comment #254
kim.pepperWe can remove needs followup as that was added in #182 to convert existing code to use the new API. However, we've already done that here.
Updated the IS.
I renamed
RequestPagertoPagerParametersas per #251.Comment #255
jibranThanks, it's looking good now.
Comment #256
larowlancan we get a follow-up (if there's not one already) to make
\Drupal\Core\Database\Query\Select::extenduse the class resolver so we can use container injection here?Same here, can we get a follow up to inject the class resolver into
\Drupal\Core\Entity\Query\Sql\QueryFactoryand use it with::getI think we should do leak detection and trigger_error here. i.e we should keep a 'last values' and compare that to current values, and if the globals have changed outside of this class, we should trigger an error.
That will help people find usages
i.e before we update the global values, we should check that they're still the same as our previous value and detect that something else has modified them
We should be able to extend the existing test to verify this too.
No we've got greenfield - is this the best name for this? (Sorry for the bikeshed) but I don't think queryAddPage conveys what is happening?
getCurrentRequest can sometimes return NULL (e.g. in cli context). I think we should add some defensive code here around that scenario.
Should we name this $pager_id now instead of $element to convey what it actually is, $element is vague (yes I realise that's what we have now). Also, my bikeshed should be yellow.
two () here
It would be nice if we could do the BC dance here, i.e. removing the typehints, defaulting to NULL and checking the type - just in case someone in contrib has extended this class and implemented ::create and __construct with different arguments
does this change indicate something broken?
why are these changes needed?
Comment #257
kim.pepperupdateParameters()$pager_idI think we need to take another look at the AreaDisplayLinkTest changes. 😕
Comment #258
andypostLinking one more ancient issue #33809: Make pagers not suck
Comment #259
larowlan😂
Comment #260
larowlandiscussed with @kim.pepper - because the globals are all arrays, we can use ArrayAccess here to provide a for-reals deprecation layer - knocked up some sample code in https://3v4l.org/AlHZK
Comment #261
kim.pepperI've put together an implementation of #260 to see what breaks.
Comment #263
kim.pepperFound a usage of
global $pager_totalincore/modules/system/tests/modules/pager_test/pager_test.moduleComment #264
jibranMeans new BC layer worked.
Comment #265
kim.pepperI think this will only work if
$pager_manager->createPager()has already been called.If you were to just try and set a value for example:
it would break, because the current code needs to call
$pager_manager->getPager()before setting a value. This can return NULL if the pager doesn't exist yet. We can only shortcut it and not set a value if it doesn't exist, but we'll lose that data.It also doesn't call
$pager_manager->setPager()either, so it's never stored. Because we don't have the$pager_id. We could add it as a property of Pager.So I don't think we have BC with this solution.
Comment #266
larowlanso I think the solution is that these need to be wrapped in a protected
createOrGetPagermethod that does the creation if it doesn't existComment #267
kim.pepperNah. In order to create a pager, we need
__construct($totalItems, $limit, $currentPage = 0). However, when we are in a global wrapper, we are only dealing with one at a time (e.g.PagerTotalItemsGlobalVariableWrapper). We don't have access to those. We don't have access to$pager_ideither togetPager($pager_id).Comment #268
larowlandamn, back to the drawing board I guess
Comment #269
kim.pepperOK here's #257 with #263 applied.
Still need to work out the views links test issues.
Comment #270
kim.pepperWoo! Fixed the view links test. :-)
Comment #271
andypostAs #256 9-10 addressed
Comment #272
larowlanI still think we should wrap these in an object that implements array access and trigger the error if someone tries to access it, but just drop the injection of pager manager that tries to keep them in sync
ie what we discussed at one point in slack DeprecatedArray or similar that just wrapped the values and took a message as a constructor
thoughts?
Comment #273
kim.pepperIf we did that, we would be triggering it from PageManager, as it needs to update globals for BC. We could add them to the list of skipped deprecations? But I think we're trying to avoid that.
Comment #274
larowlanCould PagerManager just create new instances of the array access object instead?
Comment #275
kim.pepperDiscussed this with @larowlan in slack and agreed that we can use DeprecatedArray and just reset all the globals whenever an pager is updated in PagerManager.
Comment #276
jibranDo we need to create a followup and add the link here?
Comment #277
larowlanI think we need the BC dance here, but the two-pronged version as seen in #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it where we also have to allow for sub-classes that don't call the parent constructor because it was previously empty - sorry 😿
is pager_page_array using getPagerQuery correct? in updateGlobals we use getCurrentPage for that variable?
Comment #278
kim.pepperRe: #276 discussed in slack and decided to remove @todos.
Re: #277
$this->pagerParams->getPagerQuery()is an array of pager_id => current page. In updateGlobals() we are iterating through the pagers and building this up.Comment #279
jibranThanks, for addressing the feedback it looks ready now.
Comment #281
larowlanI'm RTBC+1 on this, but will discuss with other framework managers
Comment #282
larowlanComment #284
larowlancross-post
Comment #285
alexpottSo re #277 and the BC dance. It's more complex than what we do in #278 and the "two-pronged version as seen in #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it" is also not quite right. That's because in HEAD this class extends \Drupal\Core\Cache\Context\RequestStackCacheContextBase() which does implement a constructor.
So the correct BC is not typehinting the first argument and checking if it is an instanceof PagerParametersInterface and updating the @trigger_error message appropriately. Add adding
Comment #286
alexpottSomething like this.
Comment #287
catchApparently three years (!) since I last reviewed this. Only really found some nits although much shorter review than I'd like.
This is a nice solution.
Nit: not really a static cache.
This is a bit confusing, it both says the element is an integer identifying the pager, then also that it's an integer telling you the page number. Should it say the value of the element tells you the page number? But I think it's copied from the existing docs..
I'm sure this is copy/pasted but there's no need for $where to be a variable here. Would be good to update the docs to use selectquery or similar (in a follow-up, appreciate this is probably copy and paste from the old example).
On #285 IMO we don't need to provide bc for cache context implementations at all.
Comment #288
alexpottFor what its worth I agree with @catch that BC constructors on cache contexts is unnecessary. The thing is though our current policy is best effort BC so @larowlan requested the deprecation in #277. #285 was pointing out that the version of the BC dance used in #278 wasn't right.
@Berdir and I have talked about opening a policy issue to clarify when we need to do constructor deprecation and when we don't but as far as I know that's never happened. Our BC policy says that constructors are not API but in real world we have broken plenty of things with changes to constructors of things like plugins (that are not API)...
Comment #289
plachVery cool indeed!
There are no hard-blockers in my review below, however it would be nice to address it if we happen to reroll this patch, as we probably should, per #285.
What about casting the return value of
::getTotalPages()to int instead? Or alternatively initialize all members with 0.The
$this->updateGlobals();invocation is not needed: it is already performed by::setPager().Very nice! However I think we should probably do all the
$GLOBALSmassaging (init/update) only in the master request.s/this function/this service?
There is no
PagerManagerInterface::findPage()method, if I'm not mistaken this code should use thepage.parametersservice instead.The combination of PHP doc and method name is confusing: makes me wonder whether this is a getter or a setter. I'd rename this to
::getUpdatedParameters()."Gets"
Since we don't want to promote service location over dependency injection, I'd rather refer generically to
PagerManagerInterface::createPager()instead :)I'm not sure it's ok to instantiate a service directly this way: I normally assume services are singletons and their state is reliable for the whole request lifecycle. If we later added an internal cache or anything else beyond the request stack, that might get out of sync with the container instance. Since we don't use the request stack elsewhere in this class, why don't we just inject the page parameters service instead?
I'd also check that the actual values match the ones we set through the pager.
Maybe I'm missing something, but I'd expect this to check that the values stored in the global did not override the pager values, i.e. I'd assert that the pager holds the expected values before and after the last
::createPager()invocation.Also, I'd expect scalar values to be assigned to the global.
Can we assert that also pager 0 has the expected value?
Comment #290
kim.pepperThanks everyone for the feedback. I hope there is still time to get this in!
Re: #285 & #286 Applied the patch, and tweaked the deprecation message.
Re: #287
Re: #289
Comment #291
kim.pepperComment #292
andypostFiled follow-up from slack to not block it #3087509: Convert comment reply subrequest to new pager manager
Comment #296
plachCommitted 38741fc and pushed to 9.0.x and friends.
Thanks for keeping this alive!
Comment #297
kim.pepperAwesome! 🎉💃🕺 So happy to see this get in. 🥂🍾
Noticed a tiny clean-up issue so created followup #3087514: Remove unused RequestStack property on pager/SqlBase.
Comment #298
kim.pepperCreated #3087517: Remove BC layer for Pager service for Drupal 9.0.x.
Comment #299
kim.pepperCreated another follow-up #3087518: Remove references to queryAddPage() in comments and deprecation notices
Comment #300
mondrakeThank you all for seeing through this to its final docking :)
Comment #301
mondrakeUnfortunately this is breaking tests in contrib Pagerer module - filed follow-up #3087602: DeprecatedArray implements \ArrayAccess, traversals not possible.