Jump to content
FireWire

[SOLVED] Pagination links render but do not work.

Recommended Posts

Hey everyone,

I'm pretty experienced with pagination and haven't seen this before. I have my pages pulled using $pages->find, which is working fine however using renderPager() generates pagination where the links do not work. Here are some details.

  • All templates needing pagination have pagination enabled in the admin.
  • URL Segments are not enabled.
  • Clicking on the "Next" or numbered links merely reloads the current page.
  • The link href values are properly being output with the urls being /page2, /page3, /page4, etc.
  • Manually entering the paginated urls has the same effect of reloading the current page with no new content.
  • Pages are being returned from the ->find function properly and with the proper limit.

A few other details:

  • ProcessWire v3.0.98
  • Multi-language is enabled, 2 languages implemented.
  • Pagination does not work on either language
  • Have very few modules installed (few enough to list), none of which I could see interfering:
    • ProFields, ProDrafts, ListerPro, DB Backups, Cronjob DB Backup, Upgrades, Upgrade Checker, Force Password Change, Markup Sitemap XML

I'm stumped. For the sake of being overly-thorough, here's the code:

<?php
  $articleTemplates = [
    'template_news_article',
    'template_news_video',
    'template_news_press_release'
  ];

  $searchParams = [
    'template=' . implode('|', $articleTemplates),
    'sort=-published',
    'limit=' . $pages->get('template=template_news')->list_count
  ];

  $articles = $pages->find(implode(',', $searchParams));
  
  echo $articles->renderPager();
?>

 

Share this post


Link to post
Share on other sites

So I figured out the source of the issue. I wrote some code that detects a user's preferred language either from their browser language setting, or from a cookie that is created when they have visited and chose a language last time.

If a preferred language detected and the current page URL does not match it, it will redirect to the same page but in the proper language.

My problem is finding a way to get the URL that includes the pagination to compare. Right now my idea is to use $input->pageNum to build the URL myself... is there a way to get the URL including the page number?

Lesson learned...

Share this post


Link to post
Share on other sites
1 hour ago, usualCommission said:

is there a way to get the URL including the page number?

I'm not 100% I understand your question. Maybe you can try combination of $input->pageNum and $pager->getURL()? E.g.:

$articles->getURL($input->pageNum());

Note the caveat in the $pager->getURL() method at the above link.

Could this topic be of interest?

 

 

Edited by kongondo

Share this post


Link to post
Share on other sites

Yeah, I'm not explaining my situation well enough and it's becoming a little more complex.

I'm working on abstracting localization to a class where one of the methods is used to redirect the user to their preferred language. That preferred language is either the language preference they have set in their browser, or a language cookie that was set last time they visited and selected a language via a select dropdown in the nav. That said, since the method is run on page load it will not have any access to specific pagination instances because they don't exist yet.

I read the link above and didn't see a solution that covers all of the bases and the module reference in a link to another post is no longer maintained, so I am going to write a custom implementation.

Here's my solution:

  • Language detection will be tied to a unique session variable.
  • On page load, the script checks for that session variable, if it exists- it exits because the language has been checked/redirected if necessary on ingress already.
  • If no language session variable exists, then compare the destination URL against the URL of that page for their preferred language.
  • If the URLs do not match then build a new URL
  • The destination URL is checked for page numbering using $input->pageNum(), if  > 1 then create a URL string for that. Any GET variables are extracted and rebuilt into a string- this preserves any possible incoming variables like UTM trackers.
  • Get localized URL for user's preferred language, re-add page number, re-add any GET variables that existed.
  • Set unique language session variable, then properly redirect to the localized page using the build URL.

I kind of wish that there was a built in method that could wrap most of this up and preserve URL details while redirecting to a given translated page, but this should work and also only redirect once.

I may be over-complicating this but it's what I've got. I really appreciate your help!

Share this post


Link to post
Share on other sites

Something like this would be the easiest way, I can think of, to do what you are doing. Redirect to a another language, including urlSegments, Pagenumber and optional QueryStrings;

if($user->language->isDefault) {
	$user->language = $languages->get("de");
	$session->redirect($input->url($withQueryString = true));
}

 

  • Like 2

Share this post


Link to post
Share on other sites

That sets the user language properly but getting the $input->url(true) still gets the non-localized version of the URL so it doesn't redirect to the translated page. It loops and fails the redirect.

Also the language move has been a little rough because using the code below seems to lead to a possible solution but is where I was hitting the paging/GET issues.

$languages->setLanguage(-lang str here-); // Also sets the language as a $user->language assignment does
$page->localHttpUrl($user->language); // Returns the proper localized URL, but does not include pagination or GET vars

I've seen many posts in the forums about redirection to a language and all of them use $page->localUrl($user->langauge) and none consider GET vars. Multi-language documentation says that the $page->localUrl() will automatically adjust to pagination but I haven't seen it. Would still have to manually build GET vars.

 

Share this post


Link to post
Share on other sites

How about getting the url-segments/page-num/get-vars suffix and then appending that to $page->localUrl?

$url_suffix = str_replace($page->url, '', $input->url(true));
$redirect_url = $page->localUrl() . $url_suffix;

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Added that to a comparison and that did the trick! Here's the method after adding your trick:

private function doInitLocalization() {
  $page = wire('page');
  $user = wire('user');
  $input = wire('input');

  // Get/set user language from either browser or cookie
  $user->language = $this->getLangFromCookie() ?: $this->getValidBrowserPrefLang();

  $urlSuffix = str_replace($page->url, '', $input->url(true));
  $userUrl = $page->localUrl($user->language) . $urlSuffix;

  if ($_SERVER['REQUEST_URI'] !== $userUrl) {
    header("Location: {$userUrl}");
  }
}

getLangFromCookie() checks if the user has visited previously and selected a language from a dropdown.
getValidBrowserPrefLang() gets the language pref sent by the browser, if it exists as a language in the CMS, returns the value.

By setting the language for the user this checks against what the browser request asked for/server is delivering vs. what the CMS knows it should be delivering. If they differ, then redirect.

This is executed in _init.php so any point of ingress is checked for url vs. preference.

Thanks @kongondo for introducing me to $input->url and the good idea for direction.
Thanks @Soma for concluding my head->desk marathon. Your solution is the simple one I couldn't see.

 

Share this post


Link to post
Share on other sites

When you change the $user->language, all following API returns stuff in that language also, the $page->url or $input->url. No need to do str_eplace on something already the same.

So if $this.>getLangFromCookie does return the language object then you can change it to this.

private function doInitLocalization() {
  $user = wire('user');
  $input = wire('input');

  // Get/set user language from either browser or cookie
  $language = $this->getLangFromCookie() ?: $this->getValidBrowserPrefLang();
  if($language !== $user->language) {
    $user->language = $language;
    $this->wire("session")->redirect($input->url(true));
  }
}

 

  • Like 1

Share this post


Link to post
Share on other sites

Good to know.

$this->getLangFromCookie() returns a string and is shared by other methods in the same/other classes. I could probably refactor it to work directly with the language object, but I'll have to come back to that sometime. Appreciate that info.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By fruid
      I'm having and always have a hard time building PaginatedArrays, I never know where to put the "limit=24" selector so please enlighten me.
      Here's what I'm doing…
      $categories = $page->protable('start=0, limit=999999'); // this I need in order to retrieve the pages's "categories" and I don't know how to make use of $rows instead for that purpose because of this confusing limit-API $rows = $page->protable; // put ("limit=20") here? $custom = buildSelector($input, $rows); // this function returns an array of selectors depending on the user input $items = new PaginatedArray; // or here? // or here? $items->find("limit=20") // or like this? $items("limit=20") // or like this? $items = $items("limit=20") // or like this? $items = $items->find("limit=20") foreach ($rows as $r) : if ($custom->get('selector')->matches($r)) : $items->add($r); endif; endforeach; // or at this point? and then
      if ($items) { // or maybe somewhere here?     echo '<span class="grey">'.$items->getPaginationString(array(     'label' => 'entries',     'zeroLabel' => '0 entries', // 3.0.127+ only     'usePageNum' => false,     'count' => count($items),     'start' => $items->getStart(),     'limit' => count($items),     'total' => $items->getTotal()     )); and then of course…
      $pager = $modules->get("MarkupPagerNav"); echo '<div class="uk-flex uk-flex-center">'.$pager->render($items, $options).'</div>'; I'm out of ideas and confused cause it doesn't make sense to me either way.
      ____
      The buildSelector function above returns and array…
      $selected = new Selectors("$letter, $searchterm, $category"); $custom = new Wirearray; $custom->set('selector', $selected); $built->set('sort', $sort); return $custom; and each of the new selectors are basically strings ("category=whatever")
      Also, you cannot put the "sort=title" or whatever as a selector for the Selectors function (see above). Why, I know not.
      I don't know if that is the proper way but selecting kind of works now as opposed to many other ways I tried. Selectors always require a lot of trial and error, it seems to have a very sensitive API, always depends on double quotes, single quotes and how you concatenate.
      Thanks for help!
    • By picarica
      Hello si have this gallery, pretty good website, but yesterday client uploaded over 3000 images oto the site, and it cannot handle filtering them out and showing them at the same time , i wanted some simple pagination maybe infinite scrool or onclikc load whatever, but i cannot seem to implement infinite ajax scrool and any other JS methods, nut sure why i still got stuck at the next method, like there is not next page.
      so i wanted to implement PW method of paginating i wanted to use MarkupPagerNav
      so far i have  this code for outputting images
      $pa = $pages->find("template=basic-page|art_gallery, images.tags!=''"); /* $pa = $pages->find("has_parent!=2,id!=2|7,status<".Page::statusTrash.",include=all"); */ echo "<div class='js-filter' id='gal' >"; /* row gtr-50 gtr-uniform */ $frame = $pages->get('/settings/')->watermark; foreach ($pa as $p) { foreach($p->images as $image) { if(!$image->hasTag("act")) { $options = array('quality' => 80, 'upscaling' => true, 'cropping' => 'north', 'sharpening'=>'medium'); $large = $image->size(1200, 0, $options); $wmImage = $large->pim2Load('wm1', ['quality'=>80, 'upscaling' => true, 'sharpening'=>'medium', 'defaultGamma'=>-1])->watermarkLogo($frame, $position='se',$padding=1.5)->pimSave(); /* zmazanie variacii, treba odpoznakovat ked sa menia nastavenie vyssie */ /* $image->pim2Load('wm1')->removePimVariations(); */ $thumb = $image->size(400, 300, $options); echo "<div class='$image->tags grid-item' style=''>"; echo "<span style='overflow:hidden;'class='image fit'>"; echo "<a class='hvr-reveal' href='$wmImage->url'>"; echo "<img uk-scrollspy='cls: uk-animation-fade; repeat: false' src='$thumb->url' alt='$image->tags'>"; echo "</a>"; echo "</span>"; echo "</div>"; } else { $options = array('quality' => 80, 'upscaling' => true, 'cropping' => 'north', 'sharpening'=>'medium'); $large = $image->size(1200, 0, $options); $wmImage = $large->pim2Load('wm2', ['quality'=>80, 'upscaling' => true, 'sharpening'=>'medium', 'defaultGamma'=>-1])->pixelate(25)->smooth(255)->watermarkLogo($frame, $position='se',$padding=1.5)->pimSave(); /* zmazanie variacii, treba odpoznakovat ked sa menia nastavenie vyssie */ /* $image->pim2Load('wm2')->removePimVariations(); */ $thumb = $image->size(400, 300, $options); echo "<div class='$image->tags blur grid-item' style=''>"; echo "<span style='overflow:hidden;'class='image fit'>"; echo "<a class='hvr-reveal' href='$wmImage->url'>"; echo "<img uk-scrollspy='cls: uk-animation-fade; repeat: false' src='$thumb->url' alt='$image->tags'><span>18+</span>"; /* <span>BY OPENING THIS IMAGE YOU CONSET THAT YOU'RE 18 YEARS OR OLDER</span> text copyraightova,y dat vedla obrazky potom */ echo "</a>"; echo "</span>"; echo "</div>"; } } }; to put it  simply, it first searches for sites, and then outputs all images from those sites, pretty simple, it also uses watermarking and uikit filtering
      i have filtering done like this
      $num = 1; echo "<ul class='uk-subnav uk-subnav-pill'>"; foreach(array_unique($alltags) as $key => $tag) { echo"<li uk-filter-control='filter: .$tag;group: $num' class='butt$num' uk-toggle='target: .butt$num ; animation: uk-animation-fade; queued: true'><a href='#'>$tag</a></li>"; echo"<li uk-filter-control='group: $num' class='butt$num active' aria-hidden='true' hidden='' uk-toggle='target: .butt$num; animation: uk-animation-fade'><a href='#'>remove - $tag</a></li>"; $num++; } echo "</ul>"; simple, but i have NO idea how to implement pagination, just because it seems to work that it like find all images, and stores with limit, and then just paginates them, but i cannot apply this method in my code.
      any idea how to make any JS inifite scroll work ? or just how to make this work ? with my setup, or with some modifications, it just have to work as is now
    • By creativejay
      I will preface this by saying I have checked (multiple times) per template (both parent and child, to be totally sure) that they are set to allow pagination.
      What's happening is that my first page of results keeps displaying, despite /page2/ being in the URL. The pagination markup also indicates I am still on page one. This is happening across multiple types of paginated pages.
      $pagination = $pager->render($results, $poptions); The options are just markup...
      $poptions = array( 'numPageLinks' => 5, 'listClass' => 'uk-pagination', 'linkMarkup' => "<a href='{url}'>{out}</a>", 'currentItemClass' => 'uk-active', 'separatorItemLabel' => '<span>&hellip;</span>', 'separatorItemClass' => 'uk-disabled', 'currentLinkMarkup' => "<span>{out}</span>", 'nextItemLabel' => '<i class="uk-icon-angle-double-right"></i>', 'previousItemLabel' => '<i class="uk-icon-angle-double-left"></i>', 'nextItemClass' => '', // blank out classes irrelevant to Uikit 'previousItemClass' => '', 'lastItemClass' => '', ); In the header, I call for the module and the options include:
      $pager = $modules->get('MarkupPagerNav'); include_once("pagination.inc"); Aside from the usual "check that you allowed pagination" advice, what issue might these symptoms indicate?
    • By ottogal
      Hello all,
      using PW 3.0.148 with the regular site profile for a blog, I got an an empty pagination output when I had a Toggle field in the selector.
      The Toggle Fieldtype was introduced with https://processwire.com/blog/posts/pw-3.0.139/ .
      The selector resulting in empty pagination:
      $posts = $pages->find("parent=blog, sort=-date, limit=10, toggle_field=0"); It worked well, when I replaced the Toggle field with a Checkbox field:
      $posts = $pages->find("parent=blog, sort=-date, limit=10, checkbox_field=0"); So the prerequisites for the pagination to work are given.
      The settings for the Toggle field were:
      Formatted value: Integer Label Type: Yes/No Input Type: Toggle buttons Default selected option: No Thanks for any hints!
    • By jacmaes
      I have a series of videos, and the following search form (translated into English here) that allows to filter these videos on the frontend:

      I've built a few of these search forms, but only with text fields, selects and radio buttons. Here I'm using an array with checkboxes ("Level" field above), and it's causing me grief when I try to paginate these results. I've done a lot of searching in the forum and spent too many hours to try to get it to work. Here's how I'm building the selector:
      <?php if(count($input->get)): // Level is an array. Code adapted from Ryan's snippet here: // https://processwire.com/talk/topic/3472-enable-pagination-for-search-results/?tab=comments#comment-38042 if($input->get->level) { $level = array(); foreach($input->get->level as $id) $level[] = (int) $id; // sanitize to INTs $level = implode('|', $level); // convert to 123|456|789 string, ready for selector } else { $level = ''; } $data = array( 'training_type' => array('=', (int) $input->get->training_type), 'duration' => array('=', (int) $input->get->duration), 'level' => array('=', $level), 'limit' => array('=', (int) $input->get->limit) ); $selector = ''; // iterate through the $data we made above to create a selector string foreach($data as $field => $a) { list($operator, $value) = $a; if(empty($value)) continue; // send value to the whitelist so that it can be used in pagination $input->whitelist($field, $value); // append to our selector string $selector .= "$field$operator$value, "; } $videos = $page->children("$selector"); When I hit search, I get the expected results. So far so good. The GET parameters are the following with the options selected in the screenshot above:
      videos/?level[]=1476&level[]=1477&training_type=1473&duration=1485&limit=10 $selector echoes the following as the "level" field is an array with a pipe character:
      level=1476|1477, training_type=1473, duration=1485, limit=10 Now, when paginating these results, the following page (page 2) shows these GET parameters:
      videos/page2/?level=1476|1477&training_type=1473&duration=1486&limit=10 And I think that's where the problem lies. The "level" field is "lost" and I'm getting more results than expected on subsequent pages. If I manually add "page2" to the initial results in the URL, just to test, everything works fine:
      videos/page2/?level[]=1476&level[]=1477&training_type=1473&duration=1486&limit=10 But how can I achieve this in code? Do I need to revert to "level[]=1475&level[]=1477" instead of "level=1476|1477" for the pagination to work correctly, and can you PHP gods illuminate me?
      Any help would be really appreciated, really.
×
×
  • Create New...