More link disappears when time-based views cache is enabled

Created on 22 October 2012, about 12 years ago
Updated 6 December 2023, 12 months ago

Problem/Motivation

On a View that has caching enabled and uses a more link, the more link disappears as soon the View is delivered from the cache.

Steps to reproduce

  1. Install Drupal using the minimal profile.
  2. Enable Views, Views UI.
  3. Edit the view 'recent_content':
    1. Set "Use pager" to "Display a specified number of items | 3 items"
    2. On "More link", check "Create more link"
    3. Add a Page display, override "Use pager" to "Mini pager | 10 items" and disable "More link".
    4. On the Block display, set "Link display" to "Page".
  4. Create 4 nodes.
  5. Place the block "Recent content".
  6. Go to the page where the block is displayed. At first, the more link is there.
  7. Refresh the page. Now the more link is gone.

Proposed resolution

TBD

Remaining tasks

A fail patch to show the test is working
Fix the test
Review
Commit

User interface changes

API changes

Data model changes

Release notes snippet

Original report by valderama

When I enable views built-in timebased cached, the "more" which links to the full page disappears.

Any ideas? I am also happy to provide more infos, but please tell what you need.

🐛 Bug report
Status

Needs work

Version

10.1

Component
Views 

Last updated about 4 hours ago

Created by

🇩🇪Germany valderama

Live updates comments and jobs are added and updated live.
  • VDC

    Related to the Views in Drupal Core initiative.

  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇳🇿New Zealand quietone

    I tried to test this on Drupal 10.1.x, using the steps in the Issue Summary. I wasn't able to follow the steps in the Issue Summary where it states, "On the Block display, set "Link display" to "Page"." There was no step previous step to create a block display. There are screenshots in #59 (which should be linked from the Issue Summary) which are using standard profile yet the instructions are for minimal. Do we know if the suggested fix works for both profiles?

    There is a comment in #54 that this was not reproducible. We need to confirm the problem exists before working on the patch.

    I am adding tags for an Issue summary update and steps to reproduce.

  • 🇳🇿New Zealand quietone

    I retested and can reproduce the problem. It was user error on my part, I just didn't read the steps correctly.

    The patch still applies, so I tested it and it does work and locally the same test fails as in #72. I have updated the remaining steps in the IS.

  • Status changed to Needs review over 1 year ago
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    30,337 pass
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update over 1 year ago
    30,337 pass
  • 🇳🇱Netherlands megachriz

    I did some fiddling with the tests, but I finally found a way to reproduce it with a test!

    I needed to add a call to drupal_flush_all_caches() somewhere and let the test also visit other pages where the same view is displayed on.

  • Status changed to Needs work over 1 year ago
  • 🇳🇱Netherlands megachriz

    Hm. The tests only patches still passes on the testbot. Maybe still needs work then.

  • last update about 1 year ago
    30,343 pass
  • 🇺🇸United States joycehutch Kansas City, MO

    I was using the #46 patch and it worked fine until upgrading to Drupal 10. It doesn't work with Drupal 10.

  • 🇺🇸United States joycehutch Kansas City, MO

    Drupal 10.1.x version

  • last update about 1 year ago
    29,678 pass
  • 🇮🇳India Akhil Babu Chengannur

    I tested this issue in 10.1.6 with standard installation profile and these are the findings

    • Updated Recent content view as per the issue description, placed the block in sidebar and created 4 pages.
    • More link was not appearing in the block even after clearing cache.
    • More link appears only if "Always display the more link" is enabled.
    • Ideally More link should appear even if "Always display the more link" is not enabled. There is already an issue https://www.drupal.org/project/drupal/issues/3381979 🐛 More link is missing in pager when using the "Some" pager and there are more records than shown Needs work related to this. Applied the patch from comment #8 on that issue and More link appeared again.

    What was the issue and how the patch fixed it?

    . Drupal\views\Plugin\views\display\DisplayPluginBase calls renderMoreLink() to check if More link should be rendered or not.

      /**
       * {@inheritdoc}
       */
      public function renderMoreLink() {
        $hasMoreRecords = !empty($this->view->pager) && $this->view->pager->hasMoreRecords();
        if ($this->isMoreEnabled() && ($this->useMoreAlways() || $hasMoreRecords)) {
          $url = $this->getMoreUrl();
    
          return [
            '#type' => 'more_link',
            '#url' => $url,
            '#title' => $this->useMoreText(),
            '#view' => $this->view,
          ];
        }
      }
    

    renderMoreLink() calls Drupal\views\Plugin\views\pager\PagerPluginBase::hasMoreRecords()

      public function hasMoreRecords() {
        return $this->getItemsPerPage()
          && $this->total_items > (intval($this->current_page) + 1) * $this->getItemsPerPage();
      }
    

    Function checks if total_items is greater than the number of currently displayed items.
    Logic to set total_items was updated in another issue https://www.drupal.org/project/drupal/issues/3265798 🐛 [view:total-rows] problem in Display a 'Specified number of items' pager Fixed and was committed to core. The fix in that issue was to add postExecute function in Drupal\views\Plugin\views\pager\Some to set the value of total_items as

      public function postExecute(&$result): void {
        $this->total_items = count($result);
      }
    

    So total_item only stores the number of results returned after applying pagination instead of storing the total number of results. If the view has 4 results and pagination is set to show 3 items, then value stored in total_item will be 3 instead of 4.

    Now, the patch in comment #8 of https://www.drupal.org/project/drupal/issues/3381979 🐛 More link is missing in pager when using the "Some" pager and there are more records than shown Needs work introduced a new property numberOfItemsBeforeLimit to store the actual number of results , and then overrided hasMoreRecords() which fixed the issue.

      public function hasMoreRecords() {
        // Use the original number of items if it has been set to determine if there
        // are more records.
        return $this->getItemsPerPage()
          && ($this->numberOfItemsBeforeLimit ?? $this->total_items) > $this->getItemsPerPage();
      }
    

    Caching issue with More link

    I was able to reproduce the caching issue (Recent content block placed in sidebar->Created 4 pages->viewed the block from page 1-> More link is present->viewed the block from page 2 -> More link is not present) once the More link got rendered. But none of the patches in this issue fixes the problem.

    Reason

    As mentioned earlier, renderMoreLink() function calls hasMoreRecords() which should return 'true' to display the More link. hasMoreRecords() compares the value of total_items with number of items currently being displayed. total_items should be higher than number of items currently being displayed. But this never happen when view gets cached.

    Check the following lines from ViewsExecutable::execute()

    if ($cache->cacheGet('results')) {
    if ($this->usePager() || !empty($this->get_total_rows)) {
    $this->pager->total_items = $this->total_rows;
    $this->pager->updatePageInfo();
    }
    }
    else {
    $this->query->execute($this);
    // Enforce the array key rule as documented in
    // views_plugin_query::execute().
    $this->result = array_values($this->result);
    $this->_postExecute();
    $cache->cacheSet('results');
    }

    When view is served from cache, then total_rows value of the view is set as total_items value in pager. So total_rows of view should give the actual numer of results rather than pagination limited result to the pager. Does that happen?

    Assume view is rendering for the first time. Then there is no data in cache and $this->query->execute($this); gets called. This invokes Drupal\views\Plugin\views\query\Sql::execute. Take a look at the following lines from that function

    $view->pager->postExecute($view->result);
    $view->pager->updatePageInfo();
    $view->total_rows = $view->pager->getTotalItems();

    It calles postExecute method on pager class and also uses the result of $view->pager->getTotalItems() to set total_count of view.

    What does $view->pager->postExecute($view->result) do? It sets the value of total_count to the number of results.
    Drupal\views\Plugin\views\pager\Some::postExecute

      public function postExecute(&$result): void {
        $this->total_items = count($result);
      }
    

    If there are 4 nodes and we set pagination limit to to display 3 items, then total_items will be set to 3.

    What does $view->pager->getTotalItems() return?

      public function getTotalItems() {
        return $this->total_items;
      }
    

    It returns the value of total_items which will be 3 in this case instead of 4.

    So the view gets stored with total_rows 3 in cache->When view is served from cache, $this->pager->total_items = $this->total_rows; gets executed ->renderMoreLink() function calls hasMoreRecords() to check if More link should be rendered or not-> hasMoreRecords() always returns false as total_items property of pager do not have the actual result count.

    In summary, there is no property in pager class or view that gives any infornation about the actual number of results.

Production build 0.71.5 2024