Jump to content

Routes and rewriting URLS


Recommended Posts

I wanted to have a similar short link as mods.pw/XXX and wanted to store them directly in the page in a field.

Created a unique text field "shortlink"

This is my module code for creating a random key (in this case 6 digits) and assign it to the shortlink field

<?php class ShortLink extends WireData implements Module {

    /** getModuleInfo is a module required by all modules to tell ProcessWire about them
     * @return array
    public static function getModuleInfo() {

        return array(
            'title' => 'ShortLink',
            'version' => 100,
            'summary' => 'Pre-populate $field->shortlink with random 6 digit string',
            'singular' => true,
            'autoload' => "template=admin"

    public function init() {
        // add before-hook to the inputfield render method
        $this->addHooKBefore("Inputfield::render", $this, "renderField");

     * If not empty, create unique value for textfield 'shortlink'
    public function renderField(HookEvent $event) {
        $field = $event->object;
        if($field->name == 'shortlink' && $field->value == '' ) {
          // $id is unique, as page id is unique
		  $shortlink = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@-_"), 0, 6);
          $field->set('value', $shortlink);

and then I activated URL Segments on my home template

and added this at the very top of my head.inc

if($input->urlSegment1) {
  // URL has /category-name/post-name/
  $shortLink = $sanitizer->pageName($input->urlSegment1);
  $post = $pages->get("shortlink=$shortLink"); 
  if(!$post->shortlink) {

It´s customized option 2 from Ryan (post #21)

At the moment it seems to work as expected. But as I´m not really a programmer I don´t know if it´s really safe etc..

Maybe the code for the head.inc could come in the module?

What do you guys think about this?

Cheers Can


Added canonical metatag to the header

I like that I´m able to override the generated key.

The module is not really needed but it´s nice to have when I´m to lazy to think about an own shortlink

When using for a customer I maybe set the field to read only, but because it´s unique text field it will throw an error anyway so shouldn´t be possible to mess with the field value

Ah there is one thing I´m not sure about.

The shortlink field is type of TextUnique, but what if the str_shuffle generates a duplicate (don´t think it´s impossible?!)

I guess the field wouldn´t get populated right? Of course I could save the page for a second time but I´m sure there is a way of doing this directly while populating it for the first time in the module but I don´t know how?

It´s currently not working with Markup Sitemap XML module, could extend the if in my header.inc to check for /sitemap.xml

Ah just figured out that Markup Sitemap XML is not working with URL Segments turned on at all.

Edited by Can
  • Like 2
Link to comment
Share on other sites

Hi @Can,

Here is another option for automatic shortlinks:


It uses the ID of the page, so a link can be as simple as: mysite.com/1034

but as long as the page id is in the url somewhere, it will work, so you can structure the link to be whatever you want really.

Not sure if it suits your needs or not, but thought I'd mention it just in case.

Link to comment
Share on other sites

Thank you Adrian should´ve mentioned that I knew this module but thanks anyway..maybe someone else would prefer the id solution ;-)

I wanted to have the possibility to have readable shortlinks like example.com/tpls or /templates which would redirect to example.com/articles/templates for example

Because the core module "Markup RSS Feed" works fine with my redirection stuff I don´t care about Markup Sitemap XML at the moment.

Link to comment
Share on other sites

Yes thanks again  ;)

It even provides a API function so it should be possible to automate it.

But I wanted to have it even simpler with short links within the page

And at the moment I don´t need to have external redirects

  • Like 1
Link to comment
Share on other sites

Needed to change the rewrite code a little to get it work beside the shopping checkout from apeisa

The second line (after <?php) looks now like this

$get = $input->urlSegment1;
if($get && $get !== "confirmation" && $get !== "payment" && $get !== "completed") {
Link to comment
Share on other sites

  • 2 months later...

I think hellomoto is trying to get a url structure like in wordpress (/2014/07/page-title), without creating folders for 2014 and 07.

That would work, if you deligate all posts from the homepage template with three url segments. 

Link to comment
Share on other sites

  • 2 years later...

A question about urlsegments and site structure:
In my professional life I mostly replicate private real estate databases to public web databases and develop websites on top of the latter.

Now I want to incorporate the real estate data (and my set of classes) into Processwire (as a test).  The real estate tables will reside next to the Processwire tables (for now).
I plan to make a template immo.php and use it as a controller for all the real estate output.

Would it be safe to say that using urlsegments - assuming I allow url segments in the immo template - is the way to go to obtain something like:


Link to comment
Share on other sites

It's definitely one possible way to go, sure.

the url names are received from the db or you define them in the template? If the first is the case make sure to sanitize them e.g. $sanitizer->pageName($url)

  • Like 2
Link to comment
Share on other sites

  • 2 months later...
On 13.9.2012 at 3:25 PM, ryan said:

But the reason you don't see things like this outlined in the documentation is because I don't think it's a best practice, whether in ProcessWire or on the web in general. So if someone uses multiple routes, I would suggest it only to solve a specific need after a site is produced… not as something to build around from the start.

Having multiple routes to a given page is certainly not a good idea. And I like the idea of the page tree as a kind of file system.

But I also sometimes like to use pages as kind of “folders” in my page tree, but do not necessarily want them as part of my URL structure. TYPO3 has a nice checkbox giving me the option to omit a page’s URL section from the URLs of its children. This way you can use pages as “folders” to structure your page tree, but they can be invisible in the URLs if they are not required there.

Would this be an idea for ProcessWire?

Link to comment
Share on other sites

7 hours ago, Michael van Laar said:

But I also sometimes like to use pages as kind of “folders” in my page tree, but do not necessarily want them as part of my URL structure. TYPO3 has a nice checkbox giving me the option to omit a page’s URL section from the URLs of its children. This way you can use pages as “folders” to structure your page tree, but they can be invisible in the URLs if they are not required there.

Would this be an idea for ProcessWire?

i think using this sort of structure is going to be inevitable on a lot of projects, especially where you don't need/want the users to even interact with the page tree - for example a shop or product catalog; in those situations, you may use a bucket approach, putting all of the products into a 'folder' and then when the user creates a new product they need to set it up from the editing screen - meaning they may need to specify a product type, family, category, or even a combination of those things. Product categories may be just options that need to render lists of items in those categories. In Ryan's CMS critic case study, he demonstrated a hook to rewrite the path of any page based on it's template.

I use that method, but depending on how complex the setup is, it may have a series of cascading implications that need to be accounted for system-wide, such as page title, section, breadcrumbs, and the actual page that is supposed to be rendered.

I setup a WireData container ($config->segmentData) where i can set those items so that the system can work with whatever virtual page needs to get rendered based on the request.

So in the end i was able to get a complete URL routing system by

1) adding the page paths in a hook in ready.php for the relevant templates
2) adding a custom WireData() ($this->wire("config")->segmentData = new WireData();) in ready.php
3) setting up some blank vars in that $config->segmentData in the template init, like $config->segmentData->page = '';
4) setting up a router template which handles the segment requests, and setting a page in the tree to use that template.
5) i also use a getVirtualPage function that simplifies the code in the router

there are a couple of other concerns depending on how you render the content, but nothing that difficult...

  • Like 1
Link to comment
Share on other sites

I know this is possible. And I also considered using a similar solution in such cases. But …

17 hours ago, Macrura said:

… So in the end i was able to get a complete URL routing system …

That’s exactly what I don’t want. I don’t want to sort of reinvent the whole URL handling in ProcessWire – which is basically what any of the mentioned solutions is, even if it is only for a certain part of the page tree. As a front end developer I simply don’t want to have to take care of URL handling. That’s the CMS’ job. And ProcessWire does this job very well. I don’t want to completely separate the page tree structure from the URL creation. I’d just like to have an intuitive and easy to use way to skip one page tree level in the URLs if required.

But probably this is no good idea at all. In most cases I can ether live with the extra level in the URL path, or I can structure my page tree using some “folder” pages and the multi-sort module, so that the most of the URLs are as short as I want them to be, while the page tree is still quite neatly arranged.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

Hello guys,

Although I have already built a web-site with ProcessWire, I'm still feeling new at ProcessWire developing. So, I have one question regarding this topic. I want to shorten my urls. I did what WillyC told in the first post. And I get my link shortened successfully. However, the page content does not display, shows only header content. What I'm doing wrong? Should I do something additionally?

My code in the top of _head.php file:

$pages->addHookAfter('Page::path', null, 'hookPagePath');
function hookPagePath(HookEvent $e) {
    $page = $e->object;
    if ($page->template == 'services') $e->return = "/$page->name/";


Link to comment
Share on other sites

you still have to tell processwire what content to show, either by redirecting or rendering for redirection check out my post at the top of this page https://processwire.com/talk/topic/1799-routes-and-rewriting-urls/?do=findComment&comment=60856

for rendering you go a similar way by checking if there's a urlSegment present, if so render the requested page by urlSegment (shortlink) instead of the home page or blog page..depending on how you structure your shortlinks
so if you shortlinks will look like domain.com/shorty then you're rendering instead of home page, if you url looks like domain.com/blog/shorty then rendering will replace blog template..

and where / how to place the code depends on how you set up your template files, are you using the delayed output using _init.php -> template file -> _main.php or are you including the head and foot in each template file? (could even be a totally different set up..)  



  • Like 1
Link to comment
Share on other sites

  • 5 months later...
  • 4 years later...
On 9/13/2012 at 12:20 AM, WillyC said:

i.am first

you can puut this in your head.inc tamplate or some includeded file before.u are doing $page->url

$pages->addHookAfter('Page::path', null, 'hookPagePath');
function hookPagePath(HookEvent $e) {
 $page = $e->object;
 if($page->template == 'article') $e->return = "/blog/$page->name/";

I just implemented this inside a autoload module and discovered that this hook only works in application ready state. So if you are utilizing this inside a module, you need to call the hook inside ready() method like this

	public function init()
		// handle render of correct page from urlSegements
		$this->addHookAfter('ProcessPageView::execute', $this, 'hookPageView');
	public function ready()
		// need to call this in ready. Not working in init()
		$this->pages->addHookAfter('Page::path', $this, 'hookPagePath');

	public function hookPagePath(HookEvent $event)

		$page = $event->object;
		// page ROW and all children recursively
		if ($page->id == 1043 || $page->rootParent->id != 1043) return; 
		$orgPath = $event->return;
		$pathSegments = explode('/', trim($orgPath, '/'));
		// $pathSegments[0] is language segment
		// get rid of $pathSegments[1] 'row' 
		$newPath = '/' . implode('/', $pathSegments) . '/';
		$event->return = str_replace('//', '/', $newPath);

	public function hookPageView(HookEvent $event)

		$page = $event->page;
		// only act on homepage
		if ($page->id != 1) return;

		// get last urlSegment to retieve page with that name
		if (count(input()->urlSegments())) {
			$wantedName = sanitizer()->pageName(input()->urlSegmentLast);
			$wantedPage = pages()->get("name={$wantedName}");
			if ($wantedPage && $wantedPage->id) {
				$event->return = $wantedPage->render();
			} else {
				throw new Wire404Exception();

If knew this earlier it would have saved me some time and frustration...


  • Like 1
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
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...