Jump to content

prev / next based on previous page's pageArray


formulate
 Share

Recommended Posts

I've poked around the forums and can't figure out the following:

I have a page that uses a $pages->find to generate a pageArray called $test. I am then using $test->render(); to create a list of page links for returned results. When I click on one of the page links it takes me to the correct page (let's call it Page1). So far so good.

Now, on Page1 I have some $page->prev and $page->next stuff going on. My problem is that it is creating links for direct siblings to Page1 based on PW's Page Tree and not my previous page's $test pageArray of links. I tried the following: $page->next($test); but this didn't work because I presume $test doesn't actually exist as part of Page1 (the pageArray was generated on the previous list page). I don't know how to make my initial pageArray pass through to subsequent pages and then have a prev / next nav that cycles through my pageArray results. Or am I thinking and going about this all wrong?

Thanks.

Link to comment
Share on other sites

If you use a deligated templating approach you could provide $test to all templates by including it into the _init.php. If not, what's preventing you pasting the variable and the pagefind in the templates which need to know the pagearray, to use it? If you really need to pass it dynamically around, save it to a session variable and read it from there. 

Link to comment
Share on other sites

Ok, getting closer but still not there. I have done the following:

On Listing Page:

$test = $pages->find("template=test, limit=2");

$session->testResults = $test;

$content .= $test->render();

On Individual results pages:

$test = $session->get(testResults);

$prev = $page->prev($test)->url;
$next = $page->next($test)->url;

This doesn't seem to work. However, if I make $test a string instead of the pageArray, the individual results pages then work for pulling the session variable.

Lost at this point.

Link to comment
Share on other sites

Try different method of storing the PageArray:

$session->testResults = (string) $test;

And on individual page:

$test = $pages->find("id=" . $session->testResults);

Trying to save PageArray in Session might not work here -- and even if it did, it could be quite a resource hog if this site gets a lot of use. Though I guess there are drawbacks to each method..  :)

  • Like 1
Link to comment
Share on other sites

No need to store something here ... 

$pa = $pages->find("template=basic-page");
$content .= $pa->render();

$content .= "<a href='{$page->prev($pa)->url}'>prev</a> ";
$content .= "<a href='{$page->next($pa)->url}'>next</a> ";

This is the same on all those pages, as they use the same template. Works fine.

  • Like 5
Link to comment
Share on other sites

No need to store something here ... 

$pa = $pages->find("template=basic-page");
$content .= $pa->render();

$content .= "<a href='{$page->prev($pa)->url}'>prev</a> ";
$content .= "<a href='{$page->next($pa)->url}'>next</a> ";

This is the same on all those pages, as they use the same template. Works fine.

Ok, tried this and it's not working. I think I'm missing something? My Listing uses a different template than my detail pages. Do they both need to be the same template? I don't think I'll be able to do this as the presentation I require is quite a bit different for both of them.

I don't understand how $page->next($test)->url would know to use the previous listing page's variable as it's not present on the detail page.

Maybe I'm not explaining myself well and saying "previous" page is misleading. I have a listings page that has multiple sub-pages (detail pages). Kind of like a blog. I do a dynamic $page->find on the listing page and then when someone clicks one one of the listings and goes to the sub-page, I need the next/prev to navigate based on the listing page's dynamic $page-find results rather than admin's page order. I can't duplicate the $page->find call on the sub-pages as it's never static. I'm thinking I either need to session variable the results or session variable the dynamic criteria for the $page->find call.

Link to comment
Share on other sites

I don't know what "dynamic behavior" you have there, but then you need some way to execute the same find query as on the listing page. 

Essentially I have multiple categories that a sub-page can be assigned to. I'm allowing visitors to specify which categories they want to view via a form with checkboxes. Those checkboxes directly translate to selectors for the $pages->find query. Hence every visitor's results are going to be different depending on what selectors they have used. Therefore, I need a way on the sub-pages to navigate next/prev based on the unique user's selectors they specified on the listings page.

I'm thinking I either have to session the results or I session the selectors and re-run the query on every sub-page. I'm reluctant to re-run the query as I have 10,000+ pages to query and I'm worried about overhead. When I tried to session variable the pageArray results, it didn't seem to work (see posts above).

Link to comment
Share on other sites

If there isn't any random behaviour involved, you can simply pass the used selector string to $session, and reuse in in another find.

Don't worry about reruning the query, but make sure you use limit.

Link to comment
Share on other sites

If there isn't any random behaviour involved, you can simply pass the used selector string to $session, and reuse in in another find.

Yeah, this is what I'm thinking. However, I'm really worried about the overhead with re-running the query on every sub-page for every visitor. I'm not joking when I say every query is going to be hitting 10k+ pages.

Link to comment
Share on other sites

Good point Diogo. However, I think I actually just came up with a crazy solution that may work:

Since passing the pageArray as a session variable doesn't work, how about creating a loop that extracts all the page ID's from the pageArray, keeps them in the same order as the pageArray and assigns them as a comma delimited string to a session variable. Then, on the sub-pages, turn the session variable in to an array, find the current sub-page's ID in the array and then go back/forward one place in the array for the ID's of the next and previous pages. Use those page ID's to grab the URL's of the next/prev nav and boom! That way I'm only looking up 2 additional pages on any given sub-page.

Sound feasible? This would eliminate a ton of overhead. I'll give it a whirl and report back.

Link to comment
Share on other sites

That's exactly what teppo suggested earlier.

I thought I had tried that without success. I'll give it another go.

For the record, I just finished my crazy idea and it worked! Teppo's suggestion is of course far superior, but I'm just glad my old brain can still come up with creative solutions once in a while :)

Edit: ugh, just realized my solution is identical to Teppo's, however his is far more elegant and he thought of it first!

Will report back once I play with Teppo's suggestion some more.

Link to comment
Share on other sites

Nearly there! Teppo's solution has worked perfect (not sure how I messed it up the first time) - thanks Teppo!

However, my problem now is how do I paginate the results without limiting the original query? On my listings page I only want to show 10 results at a time. If I use the proper approach for paginating and set a limit (ie: limit=10), paginating works but then next/prev (ie: Teppo's solution) will only work for those 10 results, even if there's 1000 pages that match the initial query. It's almost like I need to do 2 queries, one that has no limit to provide proper next/prev links and one that generates the paginated list. Obviously a 10k+ unlimited pages query would suck. I hope I'm making sense.

Link to comment
Share on other sites

I haven't been following this thoroughly, but does this help? Soma suggested this to me for something a while back and it works well enough.

// generate pager navigation using dummy array
$dummies = $pages->find("template=basic-page, limit=10");
echo $dummies->renderPager();
Link to comment
Share on other sites

I haven't been following this thoroughly, but does this help? Soma suggested this to me for something a while back and it works well enough.

// generate pager navigation using dummy array
$dummies = $pages->find("template=basic-page, limit=10");
echo $dummies->renderPager();

Unfortunately this won't work because on sub-pages the results are no longer an array (they were passed as a string). Also, it would have the same issue of only having results that were generated by the limit=10.

This is the key to the solution: Somehow renderPager() is smart and knows to re-query once you go past the limit. I basically need that same functionality on sub-pages so that my prev/next will re-query based on the custom selectors once the limit has been reached.

Unfortunately I don't know how to make myself make more sense and the only solution I can see at this point is re-running the query on every single sub-page. It seems crazy there isn't a simple solution to this. I know there has to be... just out of my grasp.

What do people do with blogs and need to display multiple categories at once? Just not use prev / next on articles?

Link to comment
Share on other sites

What do people do with blogs and need to display multiple categories at once? Just not use prev / next on articles?

I think so. Usually prev/next work in time sequence and ignore the page where you came from.

Link to comment
Share on other sites

I think so. Usually prev/next work in time sequence and ignore the page where you came from.

Yeah :( Unfortunately the only way I've found to get around this is do two queries on the listings page. One query has no limit and passes the results as a session string variable. The second query has a limit and is used for the list / paginating of the list.

I still think there's a better solution here using start= somehow, but I'm not PW savvy enough to figure it out. Maybe down the road once I'm well versed with PW, I'll revisit this issue. For now I'll just have to live with the overhead. It seems my development method lately has been "throw money at computing power" to solve any performance problems rather than have well done code :(

Anyway, thanks everyone for your input. On to the next problem.

Link to comment
Share on other sites

I manage to find a way, but it's highly untested, and would have to be worked upon. Hope it's understandable:

if($session->selector) {  // selector string resulting of the user selection, has to be in session

    // in the first time we need all the pages that were linked on the base page
    // after that we need only 3, the current page the previous and the next
    // we know if it's the first one if $session->start is not defined
    $limit = $session->start ? "3" : "10";

    // if $session->start is not defined we want to start with the first item
    $start = $session->start ? $session->start : "1";

    // for the pageArray
    $array = $pages->find("{$session->selector}, start={$start}, limit={$limit}");

    $prev = $page->prev($array);
    $next = $page->next($array);

    // if there isn't session->start, give it the index of the current page in the array
    if(!$session->start) $session->start = $array->getItemKey($page);

    // if former page was the previous, add one to $session->start
    if($session->page === $prev->id) $session->start = $start + 1;

    // if former page was the next, subtract one from $session->start
    if($session->page === $next->id) $session->start = $start - 1;

    // store this page ID in $session->page to compare next
    $session->page = $page->id;

    // echo the prev and next links
    echo "<br><a href='" . $prev->url."'>Prev</a>/<a href='" . $next->url . "'>Next</a>";

}

// at some point in the other templates would be good to unset $session->selector with $session->remove("selector")

Edit: without the comments it's less scary :)

Edit2: forgot the $session->selector, already added it

if($session->selector) {

    $limit = $session->start ? "3" : "10";
    $start = $session->start ? $session->start : "1";

    $array = $pages->find("{$session->selector}, start={$start}, limit={$limit}");

    $prev = $page->prev($array);
    $next = $page->next($array);

    if(!$session->start) $session->start = $array->getItemKey($page);

    if($session->page === $prev->id) $session->start = $start + 1;
    if($session->page === $next->id) $session->start = $start - 1;

    $session->page = $page->id;

    echo "<br><a href='" . $prev->url."'>Prev</a>/<a href='" . $next->url . "'>Next</a>";

}
Edited by diogo
correction on code
  • Like 3
Link to comment
Share on other sites

I was trying with my 160k testinstall :) Diogo's code doesn't work really as it has no correct start value and that would be something like the page's position in the result - 2 or something.

So I tried with diogos approach and came up with adding a url segment to each article link in the list like /articleurl/1 to be able to get the position on the article page.

//That would generate the list

$session->list_selector = "template=news-entry, summary*=europa, sort=-date";
$limit = 10;
$list = $pages->find( $session->list_selector . ", limit=$limit");
$start = $input->pageNum ? $limit * ($input->pageNum - 1) : 1; // $input->pageNum is the page from pagination

foreach ($list as $key => $p) {
    $pos = $start + $key + 1;
    echo "<li><a href='$p->url{$pos}'>$p->title</a></li>"; // add position to the url
}

echo $list->renderPager();
And on the article page
if($session->list_selector && $input->urlSegment1) {
  $pos = (int) $input->urlSegment1;
  if(!$pos) return;
  
  $limit = 3;
  $start = $pos == 1 ? 0 : $pos - 2;
  $selector = "{$session->list_selector}, start={$start}, limit={$limit}";
  $array = $pages->find( $selector );

  $prev = $page->prev($array);
  $next = $page->next($array);

  if($prev->id) echo "<a href='" . $prev->url . ($pos-1) . "'>Prev</a> ";
  if($next->id) echo "<a href='" . $next->url . ($pos+1) . "'>Next</a> ";
}

Works fine with a search with about (7) thousands of results in about 0.1 - 0.2 sec. 

This would be cacheable if you even construct the url using the category (if that's what you filter for only) and have those even cached results (hence own url).

Another approach would be to cache a text file with the result (to not store big amount of data in session) and save and load that depending on the filter. A delimited list of ID's and use it to search to get the prev and next item in the list. Not sure this would complicate things but then the DB don't need to query each time (if that's at all a problem.)

  • Like 3
Link to comment
Share on other sites

I was trying with my 160k testinstall :) Diogo's code doesn't work really as it has no correct start value and that would be something like the page's position in the result - 2 or something.

So I tried with diogos approach and came up with adding a url segment to each article link in the list like /articleurl/1 to be able to get the position on the article page.

//That would generate the list

$session->list_selector = "template=news-entry, summary*=europa, sort=-date";
$limit = 10;
$list = $pages->find( $session->list_selector . ", limit=$limit");
$start = $input->pageNum ? $limit * ($input->pageNum - 1) : 1; // $input->pageNum is the page from pagination

foreach ($list as $key => $p) {
    $pos = $start + $key + 1;
    echo "<li><a href='$p->url{$pos}'>$p->title</a></li>"; // add position to the url
}

echo $list->renderPager();
And on the article page
if($session->list_selector && $input->urlSegment1) {
  $pos = (int) $input->urlSegment1;
  if(!$pos) return;
  
  $limit = 3;
  $start = $pos == 1 ? 0 : $pos - 2;
  $selector = "{$session->list_selector}, start={$start}, limit={$limit}";
  $array = $pages->find( $selector );

  $prev = $page->prev($array);
  $next = $page->next($array);

  if($prev->id) echo "<a href='" . $prev->url . ($pos-1) . "'>Prev</a> ";
  if($next->id) echo "<a href='" . $next->url . ($pos+1) . "'>Next</a> ";
}

Works fine with a search with about (7) thousands of results in about 0.1 sec. 

This would be cacheable if you even construct the url using the category (if that's what you filter for only) and have those even cached results (hence own url).

Another approach would be to cache a text file with the result (to not store big amount of data in session) and save and load that depending on the filter. A delimited list of ID's and use it to search to get the prev and next item in the list. Not sure this would complicate things but then the DB don't need to query each time (if that's at all a problem.)

Wow! You guys are amazing! PW is awesome, but the community really is the killer "feature". Thanks guys :)

  • Like 2
Link to comment
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
 Share

×
×
  • Create New...