Jump to content
stanoliver

pagination with categories

Recommended Posts

Good morning everyone!

I have a growing number of posts about cars, bikes, airplanes, etc. 

The following code (below) just works fine and returns only the posts of the category=cars as I desired together with pagination.

In my url I have for example /categories/cars/car1 or /categories/bikes/bike1

I do want to filter my posts not only with ... category=cars ... but also with category=bikes or category=airplanes and at best:

If my url is /categories/bikes/ then ... category=cars ... should be overwritten or replaced by ... category=bikes ... 

If my url is /categories/airplanes/ ... then the filter should be ... category=airplanes ... (I know a work around by creating almost identical templates where I could just change the "category=cars" part of my code but that's comes of a prize by repeating a lot of identical code and is not a good habit). In the documentation I read something about the "has_parent" selector but I could not get to work it related to the urls mentioned above.

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=cars, limit=5, sort=-postdate') as $post 😞?>
<!-- Blog entry -->
<div class="g8-card-4 g8-margin g8-white">
<!--<img src="/g8images/bridge.jpg" alt="Norway" style="width:100%">-->
<div class="g8-container">
<h3><b><?= $post->title; ?></b></h3>
<h5>Datum: <span class="g8-opacity"><?= $post->postdate; ?></span></h5>
</div>
 
<div class="g8-container">
<p><?= $post->posteditor; ?></p>
<div class="g8-row">
<div class="g8-col m8 s12">
<p>
<a href="<?= $post->url; ?>"><button class="g8-button g8-padding-large g8-white g8-border"><b>Details lesen &raquo;</b></button></a>
</p>
</div>
<div class="g8-col m4 g8-hide-small">
<!--<p><span class="g8-padding-large g8-right"><b>Comments &nbsp;</b> <span class="g8-badge">2</span></span></p>-->
</div>
</div>
</div>
</div>
<!-- END BLOG ENTRIES -->
<?php endforeach; ?>
 
<? echo $results->renderPager(array(
'nextItemLabel' => "rückwärts",
'previousItemLabel' => "vorwärts")); ?>

Share this post


Link to post
Share on other sites
7 minutes ago, dragan said:

Just use URL segments.

Hi @dragan, I have read about url segments before but I have no idea how to implement it in my code above. Also I am not sure if url segments still work if new categories will be added by an end user of the cms (at the moment I just have the categories cars, bikes, planes, ...).

Is there no easy way with a switch or if statement just to check what is the parent url segment (/categories/car/) -> return ... category=cars

check case: /categories/bikes/ -> return ... category=bikes

If I just wanted a working solution and would have 5 categories for example than I would just my code from above 4 more times to 4 new templates and all is fine (but bad practise because all code would be identical and only differ by one word (the category itself)

 

Share this post


Link to post
Share on other sites

 

// we are only using 1 URL segment, so send a 404 if there's more than 1
if(strlen($input->urlSegment2)) throw new Wire404Exception();

$segment = $sanitizer->pageName($input->urlSegment1);
$categories = $pages->get(1192)->children; // 1192 = id of parent holding my category pages
$allowed = array();
foreach($categories as $cat) {
    $allowed[] = $cat->name;
}
// does the URL segment match an existing category name?
if(!in_array($segment, $allowed)) {
    throw new Wire404Exception();
} else {
  $results = $pages->find("id>1, template=templateblogpost, category=$segment, limit=5, sort=-postdate");
   foreach ($results as $post) {
    // output each $post
  }
}
5 minutes ago, stanoliver said:

Also I am not sure if url segments still work if new categories will be added by an end user of the cms

If you do it like my above example (or similar), yes.

Share this post


Link to post
Share on other sites
8 minutes ago, stanoliver said:

The provided code gives an 404 not found 

What could be wrong?

You probably didn't adjust this line:

1 hour ago, dragan said:

$categories = $pages->get(1192)->children; // 1192 = id of parent holding my category pages

I assume each of your categories is a page, i.e. you are using page references.

Share this post


Link to post
Share on other sites

Yes, my categories are pages and the url-structure example.com/categories/cars/ but I am not sure what you mean with page references. I have adjusted the your id=1192 to the id of page "categories"

Share this post


Link to post
Share on other sites

@Gideon So Hi Gideon, if I use the code you provided and replace "Do your thing here."

with echo 'Hi!';

everything works but I need that code work togehter with my code from above. My own code from above works also perfectly but I do not want to repeat myself in way like:

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=cars, limit=5, sort=-postdate') as $post ?>
<!-- Blog entry -->
<div class="g8-card-4 g8-margin g8-white">
<!--<img src="/g8images/bridge.jpg" alt="Norway" style="width:100%">-->
<div class="g8-container">
<h3><b><?= $post->title; ?></b></h3>
<h5>Datum: <span class="g8-opacity"><?= $post->postdate; ?></span></h5>
</div>

 

<div class="g8-container">
<p><?= $post->posteditor; ?></p>
<div class="g8-row">
<div class="g8-col m8 s12">
<p>
<a href="<?= $post->url; ?>"><button class="g8-button g8-padding-large g8-white g8-border"><b>Details lesen &raquo;</b></button></a>
</p>
</div>
<div class="g8-col m4 g8-hide-small">
<!--<p><span class="g8-padding-large g8-right"><b>Comments &nbsp;</b> <span class="g8-badge">2</span></span></p>-->
</div>
</div>
</div>
</div>
<!-- END BLOG ENTRIES -->
<?php endforeach; ?>

 

<? echo $results->renderPager(array(
'nextItemLabel' => "rückwärts",
'previousItemLabel' => "vorwärts")); ?>
 
//and now the same code again with "category=cars" replaced with "category=bikes"
 
<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=bikes, limit=5, sort=-postdate') as $post ?>
<!-- Blog entry -->
<div class="g8-card-4 g8-margin g8-white">
<!--<img src="/g8images/bridge.jpg" alt="Norway" style="width:100%">-->
<div class="g8-container">
<h3><b><?= $post->title; ?></b></h3>
<h5>Datum: <span class="g8-opacity"><?= $post->postdate; ?></span></h5>
</div>
 
<div class="g8-container">
<p><?= $post->posteditor; ?></p>
<div class="g8-row">
<div class="g8-col m8 s12">
<p>
<a href="<?= $post->url; ?>"><button class="g8-button g8-padding-large g8-white g8-border"><b>Details lesen &raquo;</b></button></a>
</p>
</div>
<div class="g8-col m4 g8-hide-small">
<!--<p><span class="g8-padding-large g8-right"><b>Comments &nbsp;</b> <span class="g8-badge">2</span></span></p>-->
</div>
</div>
</div>
</div>
<!-- END BLOG ENTRIES -->
<?php endforeach; ?>
 
<? echo $results->renderPager(array(
'nextItemLabel' => "rückwärts",
'previousItemLabel' => "vorwärts")); ?>

Share this post


Link to post
Share on other sites

@stanoliver, please use the Code button for blocks of code you insert in the forum - your post above is almost unreadable.

2019-06-24_133640.png.8017095c9d78e077fd6fff60cce757c3.png

On to your problem...

As I understand it you are rendering a category page (cars, bikes, etc), and you have some posts which are tagged with these same category pages in a Page Reference field (a post is tagged with cars, bikes, etc). Therefore your selector would be:

$results = $pages->find("template=templateblogpost, category=$page, limit=5, sort=-postdate");

Note the double-quote marks around the selector string, so that the $page variable will be interpolated.

Then foreach() $results to output your markup.

Share this post


Link to post
Share on other sites

@Robin S What already work is (but only with repeating 3times almost identical code):

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=cars, limit=5, sort=-postdate') as $post ?>

do something

<?php endforeach?>

//////////////////////////////////////

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=airplanes, limit=5, sort=-postdate') as $post ?>

do something

<?php endforeach?>

////////////////////////////////////////

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=motorbikes, limit=5, sort=-postdate') as $post ?>

do something

<?php endforeach?>

What I would like to have:

Only one time the codeblock

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=$segment, limit=5, sort=-postdate') as $post ?>

do something

<?php endforeach?>

The problem is that I do not know what's the code related to $segment before the opening of <?php foreach ... ?>

I could imagine something like 

(*****)

if($segment->path == "/categories/segment/") {

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=$segment, limit=5, sort=-postdate') as $post ?>

do something

<?php endforeach?>

}

In short: 

1. The code should check in the url wich category is used and assign the variable $segment to it 

2. The code above (*****) should be executed with the right $segment

Share this post


Link to post
Share on other sites

@stanoliver, yes, I understand the objective. What's not clear is how your blog posts are identified as belonging to a particular category. If there is a blog post titled "My bike" and it is in the category of "bike", how do you connect category "bike" with post "My bike"? Probably it would help if you show a screenshot of your page tree, with both the post pages visible and the category pages visible.

Most commonly, developers structure their website so that they have a structure of category pages (using template "category" let's say), and then they have a Page Reference field (named "categories" let's say, with selectable pages "template=category") and they add this Page Reference field to the post template. Then for each post they select one or more category pages. That's what my previous post assumes. So that when you are viewing a category page on the front-end, that category page exists as the $page variable. And so if you want to find posts that have that category page assigned to them you search for them with this:

$results = $pages->find("template=templateblogpost, categories=$page, limit=5, sort=-postdate");

 

Another way that you might be connecting categories to posts is by a parent-child relationship. So the post pages are children of a category page that they belong to. This is okay but less flexible because it means that any post can only belong to one category. So if you found yourself needing to write a blog post "How to fix a bike rack to your car" you would have a problem because the post could only be in the bike category or the car category but not both.

But if you are in fact using a parent-child relationship you would use this selector on the category page (note change from $pages->find to $page->children):

$results = $page->children("template=templateblogpost, limit=5, sort=-postdate");

 

Share this post


Link to post
Share on other sites

Important: Using following selector works just perfect in my codeblock (xxx)

$results = $pages->find('id>1, template=templateblogpost, category=cars, limit=5, sort=-postdate'

and when I  go on repeating another codeblock with

$results = $pages->find('id>1, template=templateblogpost, category=airplanes, limit=5, sort=-postdate'

and the last codeblock with

$results = $pages->find('id>1, template=templateblogpost, category=motorcyles, limit=5, sort=-postdate'

all just works fine.

 

So one entire codeblock (xxx) wich just works perfect looks like:

<?php foreach ( $results = $pages->find('id>1, template=templateblogpost, category=cars, limit=5, sort=-postdate') as $post 😞?>
<!-- Blog entry -->
<div class="g8-card-4 g8-margin g8-white">
<!--<img src="/g8images/bridge.jpg" alt="Norway" style="width:100%">-->
<div class="g8-container">
<h3><b><?= $post->title; ?></b></h3>
<h5>Datum: <span class="g8-opacity"><?= $post->postdate; ?></span></h5>
</div>
 
<div class="g8-container">
<p><?= $post->posteditor; ?></p>
<div class="g8-row">
<div class="g8-col m8 s12">
<p>
<a href="<?= $post->url; ?>"><button class="g8-button g8-padding-large g8-white g8-border"><b>Details lesen »</b></button></a>
</p>
</div>
<div class="g8-col m4 g8-hide-small">
<!--<p><span class="g8-padding-large g8-right"><b>Comments  </b> <span class="g8-badge">2</span></span></p>-->
</div>
</div>
</div>
</div>
<!-- END BLOG ENTRIES -->
<?php endforeach; ?>
 
<? echo $results->renderPager(array(
'nextItemLabel' => "back",
'previousItemLabel' => "forward")); ?>

I also could use  the last codeblock (xxx) two more times and just replace in the "first line" "category=cars" by "category=airplanes". Another time (the third codeblock) would be identical and I only would replace "category=cars" by "category=motorcyles". Then I would have 3times an almost identical codeblock which only differs in one place: "category= " Repeating almost identical code is a bad habbit and therefore I need a variable like $segment so that I just can make everything work with one codeblock.

Maybe short in other words:

Before I can use something like $segment in my codeblock (xxx) I have to declare it before my block starts but I have no idea how to declare $segment. What I know is that my codeblock (xxx) works perfectly for a specific category.

Share this post


Link to post
Share on other sites
15 hours ago, Robin S said:

Another way that you might be connecting categories to posts is by a parent-child relationship. So the post pages are children of a category page that they belong to. This is okay but less flexible because it means that any post can only belong to one category. So if you found yourself needing to write a blog post "How to fix a bike rack to your car" you would have a problem because the post could only be in the bike category or the car category but not both.

But if you are in fact using a parent-child relationship you would use this selector on the category page (note change from $pages->find to $page->children):


$results = $page->children("template=templateblogpost, limit=5, sort=-postdate");

 

@Robin S 1. Yes, I am using a parent-child relationship and for the moment it would be enough even when a post can only be in one category.

2. The little snippet of Gideon

if($page->path == "/categories/cars/") {

Do your thing here.

}

is already totally fine and I do not need much more functionality. I only would need something like in (my own pseudocode comes now):

if($page->path == "/categories/$variable/") {

echo $variable->title;

}

If the url is

/categories/cars

the output should be all posts in category cars.

If the url is

/categories/airplanes

the output should be all posts in category airplanes

3. There would be no problem at all with all my mentioned code above if I only knew which are categories I will need in the future but I don't know that becaus the end user will be allowed to drop new childrend under the parent "categories"

 

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 jds43
      Hello, I'm trying to list the categories, on the front through select options, that have been selected by page reference field (multiple pages PageArray) on the child pages.
      Things to Do (would only display three, six, seven, nine in select)
      -thing one (-category three, -category nine)
      -thing two (-category six, -category seven)

      Lodging (would only display one, two, three, four in select)
      -lodging one (-category one, -category two)
      -lodging two (-category three, -category four)

      Dining (would only display five, six, seven, eight in select)
      -dining one (-category five, -category six)
      -dining two (-category seven, -category eight)
      Categories(hidden page)
      -category one
      -category two
      -category there
      -category four
      -category five
      -category six
      -category seven
      -category eight
      -category nine
      -category ten
      $categories = $pages->find(1129)->children('include=hidden'); foreach($categories->references('category') as $ref) { echo $ref->title; } This selector isn't working, but it seems 'references' would be helpful. I've never used it before, so I'm not sure how to employ for this.
      https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#page-gt-references
×
×
  • Create New...