Jump to content

How to build a news page


Recommended Posts

What would be the syntax/template example for say a simple news structure with pagination like this example:


Latest news first with a headline being a link to the full story perhaps. It would need a backlink to the last page of pagination and a prev/next to the other articles. Intelligent enough so that the prev/next finish at the end/start of all news pages.

Link to comment
Share on other sites

It looks like your news page example displays 4 stories per page, and it displays the full bodycopy for each of them. Assuming the same structure and fields of the pages you posted, you'll need to create two templates: 

1. news_index.php

2. news_story.php

You can call those templates above whatever you want, but just note that we'll use a separate template for news stories and news items. Your news_story template will contain the following fields:

1. title

2. date (date)

3. body (textarea)

And it's markup might look like this (excluding headers, footers, etc.):


<div id='bodycopy'>

Not much action there. Your news_index is where most of the action will happen. In Admin > Setup > Templates > news_index > Advanced, check the box for "Page Numbers" to turn them on. Save.

Here is what the code in your news_index.php might look like:



// start the news stories list
echo "<ul>";

// get the stories for this page
$stories = $page->children("limit=4, sort=-date"); 

// note if you set the stories to sort by date descending on the /news/ page
// in the admin, then you can omit the "sort=-date" above. 

// cycle through each story and print it in a <li>
foreach($stories as $story) {
   echo "
       <li><a href='{$story->url}'>{$story->title}</a> 
       <p><strong>Date:</strong> {$story->date}</p> 

echo "</ul>";

// get values for our placemarker headline
$start = $stories->getStart(); 
$end = $start + count($stories);  
$total = $stories->getTotal(); 
$num = $input->pageNum; 
$lastNum =  ceil($total / $stories->getLimit());

// output the placemarker headline
echo "<h4>Showing $start - $end of $total Article/s | Page $num of $lastNum</h4>"; 
// output pagination links
echo $stories->renderPager();

Lets say that you don't want the pagination links that renderPager produces, you can always modify it's output by passing params to it. See the page about pagination here:


But if you want your literal "previous" and "next" buttons like on your site, then you'll want to insert your own logic to do that. Something like the next example.

Note I'm reusing the vars I set in the previous example here for brevity. This snippet would replace the renderPager() method in the previous example.


// make the previous link
if($num > 2) $prevLink = "./page" . ($num-1);
   else if($num == 2) $hrefLink = "./"; // page 1  
   else $prevLink = "./page" . $lastNum; // last page

// make the next link
if($num >= $lastNum) $nextLink = "./"; // page 1
   else $nextLink = "./page" . ($num+1); 

// output the prev/next links: 
echo "<p><a href='$prevLink'>Previous</a> <a href='{$nextLink}'>Next</a></p>";

Disclaimer: the examples on this page are just written off the top of my head and are not actually tested examples. You'll likely have to tweak them. In particular, you may have to add or subtract 1 in a few places to get the right numbers. If you end up adapting this, please let me know of any errors I have here so that I can correct them.

  • Like 2
Link to comment
Share on other sites

Just wanted to add a couple more notes to this:

Using a 'summary' field

I think it's more common on this type of page that you would display a summary of the news story rather than the whole news story... and then link to the full news story. To display a summary, you could have a separate 'summary' field in your template, which would be just a regular textarea field where you would have a 1-2 sentence summary of the article. And you would display this 'summary' field rather than 'body' field in the news_index template.

If you wanted to autogenerate a summary from the 'body' field (rather than creating a new 'summary' field), you could just grab the first sentence or paragraph of the body and use that as your summary. This is how I usually do something like that:

// make our own summary from the beginning of the body copy
// grab the first 255 characters
$summary = substr($story->body, 0, 255); 

// truncate it to the last period if possible
if(($pos = strrpos($summary, ".")) !== false) {
    $summary = substr($summary, 0, $pos); 

That's a really simple example, and you may want to go further to make sure you are really at the end of a sentence and not at an abbreviation like "Mr."

In the example that Moondawgy posted, it makes sense to autogenerate a summary (if he needed it). But in other cases, the 'body' can be quite long (and take up a lot of memory), and it makes more sense to maintain a separate summary field if you have to keep a lot of pages loaded at once. This is really only an issue once you get into hundreds of pages loaded at a time. It's not an issue in these examples, but I just wanted to point it out.


Using the 'autojoin' optimization can increase performance on fields that get used a lot. Not using it can reduce the page's memory footprint. What is more desirable in each instance depends on your situation. In this news section example, the date and body fields would benefit from having 'autojoin' turned ON. See this page for an explanation:


  • Like 3
Link to comment
Share on other sites

  • 7 months later...

Hi there!

I am trying to build a news page like Ryan described in his first example above, but i cannot get the pagination to work - it is not shown (see attached file)

I have the both required modules installed (they were already installed in PW 2.1).

Any idea what could be wrong? I have read about pagination in the doku and the forum, tried different things out - but the pagination does not appear...

Kind regards,



Link to comment
Share on other sites

Pagination won't appear if there aren't enough pages to paginate. So the first thing to check would be that you've retrieved a group of pages (with "limit=10" or some other limit, in the selector), and that the total number of possible pages is larger than your limit. PW won't bother with pagination otherwise, since there would be no reason to have it.

If this doesn't resolve it, please post a code example or attach your entire template file if possible.

Link to comment
Share on other sites

I had only 2 childpages and have set the limit to 1 - so the total number of pages was bigger than the limit. But it seems that it works only if you have minimum 3 pages and set the limit to 2. So at least it works now, thanks!

Another thing that appeared is that the date shows always " 31/12/1969 19:00" if i doesn't input anything. I am using the datepicker and have set "use todays date" to active. Any idea what could bw wrong?

Link to comment
Share on other sites

ProcessWire doesn't attempt to do any pagination with a "limit=1", so at present, you need at least a "limit=2" in order to paginate.

For the 1969 date, that's the first value a date can have in a unix timestamp, so it basically means there is no date set. I'm guessing that the "default to today's date" was added sometime after the pages with 1969 were created. Either that, or that the field was added to a template but a date wasn't set in all the pages. The default is only set when you edit a page, so you would have to edit and save a page for it to pull in that default if it didn't have it before. In your case, what you may want to do is skip over the fields that don't have a date set:

if(!strpos($page->date, '1969')) {
   // output your date

I'm trying to think of ways to avoid this problem from the core. Perhaps I should have PW return blank, rather than a formatted date, when the timestamp value is 0.

  • Like 1
Link to comment
Share on other sites

I had just to save those pages again - no problem. And thanks again for the help - it's great to have such a fantastic support here!

BTW: After a few days testing around with PW i have to say that i am so happy that i have found this great system - i have tried out so many CMSs in the past few years and after i have spent few days now again with searching for a flexible system which supports custom fields i cannot think of any other system which fits my needs better!

Thank you very much for that great peace of software!


And although i knew already about the flexibility, again and again i am impressed how flexible and well-though-out this CMS is: i just discovered the template setting "List of fields to display in the admin Page List" - that will help my clients to work with the news-section much more comfortable...

Link to comment
Share on other sites

I will do my best to make sure it keeps getting better and better.

I have no doubt that you will!

I have another little question...

In the news-template i have set the news to be sorted by date:

$stories = $page->children("limit=3, sort=-date");

If two articles are created the same day the last created is placed below the first created. I only found the solution to set the sort field on the page settings to created, but then this setting will be overwritten by the template.

Is there a possibility to set two parameters in the template (date and created)?

Another thing is that i wondered why "date" is not shown in the sort field - is this changed in PW 2.1? I just ask because you commented the code above:

// note if you set the stories to sort by date descending on the /news/ page

// in the admin, then you can omit the "sort=-date" above.


Ok, got it - i just made a mistake when i tried to add "-created" to the template, i did it like this:

$stories = $page->children("limit=3, sort=-date,-created");

but i had to do it like that:

$stories = $page->children("limit=3, sort=-date, sort=-created");

So now its working fine. Would maybe be a nice thing, if the sort field in the settings would allow multiple sort parameters too, or not? But as we can sort pages in the template it seems to be just an addition that you can set the sort field in the page settings - or are there any scenarios where it's better to use the page setting to sort them?

Ah, and i still couldn't find out why "date" is not shown there.

Link to comment
Share on other sites

If you want your custom 'date' field to be one that can be sorted on, check the box for 'autojoin' in the field's advanced settings. That's the only technical requirement to make a page sortable by that field. Following that, you should be able to select it in your page's children tab as the default sort.

Multiple sort parameters in your selector are just fine, and ProcessWire supports this as long as those fields are 'autojoin'.

It also sounds like you may need to add a time component to your 'date' custom field, so that you can sort by two entries in the same day. PW supports this, but you have to include time codes in your date input format, i.e. "dd-mm-yy H:i" where "H:i" is the time component (hours and minutes). (If you need them, here are the full set of date and time codes used by PW: http://www.php.net/manual/en/function.date.php).

Link to comment
Share on other sites

  • 3 years later...

I'm not sure where to put this, but because I got the basic code from Ryan's post in this thread, I'll put it here.

I was working on a portfolio site where the Work page spits out a list of thumbnail links. I scoured the forums looking for a simple prev/next link setup to move from page to page of the work thumbnails. I'm still learning PHP, so I can't solve these problems from scratch. Thankfully, I came across this thread and the basic solution to my problem. After tweaking Ryan's code and adding a couple of things, I came up with this:

// What we are trying to grab
$entries = $pages->get("/work/")->find("template=work-entry, sort=sort, limit=2");

// values we need to make this work
$start = $entries->getStart();
$end = $entries + count($entries);
$total = $entries->getTotal();
$num = $input->pageNum;
$lastNum = ceil($total / $entries->getLimit());

// prev link
if($num > 2) {
  $prevLink = "./page" . ($num - 1);
  $prevLinkClass = "class='NavPages-item  NavPages-item--prev'";
  $navPagesPrev = "<li {$prevLinkClass}><a href='{$prevLink}'>« Newer</a></li>";
} else if($num == 2) {
  $prevLink = "./";
  $prevLinkClass = "class='NavPages-item  NavPages-item--prev'";
  $navPagesPrev = "<li {$prevLinkClass}><a href='{$prevLink}'>« Newer</a></li>";
} else {
  $navPagesPrev = "";

// next link
if($num < $lastNum) {
  $nextLink = "./page" . ($num + 1);
  $nextLinkClass = "class='NavPages-item  NavPages-item--next'";
  $navPagesNext = "<li {$nextLinkClass}><a href='{$nextLink}'>Older »</a></li>";
} elseif($num == $lastNum) {
  $navPagesNext = "";

Then you can output the links with

<ul class="Nav  NavPages">
echo $navPagesPrev;
echo $navPagesNext;

When you're on the first page, the prev link won't show up, and when you reach the last page, the next link will disappear. I used a small limit to make sure it worked for over 2 pages of links.

I hope somebody will find this useful.

  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...
  • 5 years later...

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.
  • Create New...