Jump to content

Dual url structure for categorized content

Recommended Posts

Websites often provide content not only as on list of stuff, but with some sort of category. The most flexible way in ProcessWire to manage such a categorization are with PageFields. Form example with a structure like this.

- Magazine (magazine)
	- Articles
		- Article 1 (article)
		- Article 2
		- Article 3
		- …
	- Categories
		- Category 1 (category)
		- Category 2
		- …

Templatenames in parentheses
Now all articles have a url structure like: "…/magazine/articles/articlename/"
The categories are looking like: "…/magazine/categories/categoryname/"
But it can be useful to also provide the articles as part of the categories like this: "…/magazine/categories/categoryname/articlename/"
Because ProcessWire doesn't provide such functionality by default, we'll use urlSegments. These have to be enabled in the template-settings for the category template. This template therefore fulfills two different jobs. Displaying a list of containing articles, as well as rendering the articles which are linked by the list. 
A simple example of a existing category.php could be:

<?php // category.php

$articles = $pages->find("template=article, category=$page");

// This example uses a deligated template approach.
// Feel free to use your own way of templating,
// but this is also a simple way to explain this.
$content = renderArticleList($articles);


Now we need to include the logic to seperate the default rendered article-list to the now added rendering of the called article.

<?php // category.php

// Throw a 404 Error if more than one segment is provided
if($input->urlSegment2) throw new Wire404Exception();

if($input->urlSegment1){ // Show the called article 

	// Sanitize the input for pageNames
	$name = $sanitizer->pageName($input->urlSegment1);

	// Search for the article with this name
	$article = $pages->get("template=article, name=$name");

	// Throw an 404 error if no article is found
	if(!$article->id) throw new Wire404Exception();

	// Explicitly set the original url of the article for the <link type="canonical" href=""> tag
	$article->canonical = $article->url;

	// Render the page, like if it was normally called. 
	// $page->url will not updated to the "categorized" url for the rendering-part
	// so you need to have that in mind if you provide some sort of breadcrumb
	echo $article->render();

}else{ // Show the list of articles of the current category

	$articles = $pages->find("template=article, category=$page");

	// The generateCategoryUrls() function is new, because
	// $page->url would provide the wrong urls.
	// Details are provided later
	$content = renderArticleList( generateCategoryUrls($articles, $page) );



Now if we call this "…/magazine/categories/categoryname/articlename/" we'll get the right article rendered out instead of the article-list. Now we need to talk about the article-list, which would - without changes - still render the "wrong" urls to all those articles.

Therefore I added the generateCategoryUrls() function. This function iterates over the pageArray and adds a second url to all those articles.

<?php // part of _func.php

function generateCategoryUrls($list, $category){
	foreach($list as $item){
		$item->categoryUrl = $category->url.$item->name."/";
	return $list;

The last thing missing is the actual template which gets rendered by renderArticleList(). This would normally call for $article->url to get the url to the article. We want this to render our second url if we are on a category site. To let the template still be useable by non category sites, we just change the parts, where to url is used form the current $article->url to $article->get("categoryUrl|url"). Only if the additional urls are provided they get rendered or it falls back to the normal urls of the articles.

There we go, with such a setup all categorized articles are reachable via both urls. 

A small addition to explain the $article->canonical I used in the category.php. For SEO it's not good to provide the same content on multiple pages without explicitly declaring which of the duplicated ones should be the original / indexed one. By providing the following link tag we provide this declaration. The extra field I use isn't really necessary, because $page->url still is the standart ProcessWire url of the shown article. But I like this to be visibile in the code, that this is a dublicate. 

<link rel="canonical" href="<?php echo $page->get("canonical|url") ?>"/>

Hope you like the explanation. Feel free to give feedback on this.

Based on the example shown in the wiki: http://wiki.processwire.com/index.php/URL_Segments_in_category_tree_example

  • Like 13

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.

  • Create New...