Jump to content

Append a segment1 value to all URL output


Recommended Posts

I have a situation where I'm trying to persist some data across page visits, but want to allow people to bookmark those pages to retain that data, or share the url and have that data persist. That means I can't use only $session or localStorage. I want to use Segment1 for this data so that the site's pages can still be cached.

Can I somehow append a value as Segment1 to all urls (like $page->url)  using a module or _init.php? What about outputting url fields? My guess is that I will need to append the value throughout my templates, but wondered if anyone has had to do something like this? Any suggetions?



Link to comment
Share on other sites

do you mean something like permalinks?

maybe you could clone your page and save it as its own child on every pagesave? you would then have something like this:

/your-article (= current version)



just an idea... of course you would have some unnecessary extra data, but maybe that would be a good solution?

Link to comment
Share on other sites

No problem. Here's the module I put together:


class SiteFilter 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(

            // The module'ss title, typically a little more descriptive than the class name
            'title' => 'Site Filter', 

            // version number 
            'version' => 1, 

            // summary is brief description of what this module is
            'summary' => 'Add the current filter value as urlSegment1 for all internal links.',

            // singular=true: indicates that only one instance of the module is allowed.
            // This is usually what you want for modules that attach hooks. 
            'singular' => true, 

            // autoload=true: indicates the module should be started with ProcessWire.
            // This is necessary for any modules that attach runtime hooks, otherwise those
            // hooks won't get attached unless some other code calls the module on it's own.
            // Note that autoload modules are almost always also 'singular' (seen above).
            'autoload' => true

     * Initialization function. This is where we'll attach our hooks.
    public function init() {

        // minify page markup automatically
        wire()->addHookAfter("Page::render", $this, "addSegment1");


    public function isExternal($url) {
      $components = parse_url($url);
      if ( empty($components['host']) ) return false;  // we will treat url like '/relative.php' as relative
      if ( strcasecmp($components['host'], wire('config')) === 0 ) return false; // url host looks exactly like the local host
      return strrpos(strtolower($components['host']), '.' . wire('config')->httpHost) !== strlen($components['host']) - strlen('.' . wire('config')); // check if the url host is a subdomain

     * addSegment1
     * We have a session variable we'd like to append to all internal urls to persist site filters when 
     * bookmarking, and sharing urls. This also allows filtered pages to be cached. Each template needs 
     * to have segments enabled, and we use a regex value for allowed segments to prevent a runaway 
     * cache.
     * @param HookEvent $event
    public function addSegment1(HookEvent $event) {

        // let's not mess with the back-end urls
        $cur_page = $event->object;
        if($cur_page->template == "admin") return;

        // Let's not pollute the page with domDocument warnings 

        // event return value contains rendered markup
        $markup = $event->return;

        $currentFilter = wire('session')->settings_filter;

        if ($currentFilter == "") return;

        // we don't want to attempt minifying markup unless it's actually HTML
        if (strpos($markup, "<html") === false) return;

        $dom = new domDocument; 
        $dom->preserveWhiteSpace = false;

        $links = $dom->getElementsByTagName('a');

        foreach($links as $link){

            $url = $link->getAttribute('href');
            $external = $this->isExternal($url);

            if($external === false){
                if(strpos($url, $currentFilter) === false){   
                    $url .= $currentFilter . "/";
                    $link->setAttribute("href", $url);

        $newMarkup = $dom->saveHTML();

        $event->return = $newMarkup;


Link to comment
Share on other sites

ah, now i understand your intention! :) thanks for sharing the code.

I'm wondering why you are hooking Page::render? Wouldn't it be possible to hook somewhere where all the urls are created, so that $page->url automatically returns the url with the filter-segment if it is an internal url? note i'm not an expert, so i'm sure there is a good reason for this.

Link to comment
Share on other sites

I decided on Page::render because this site has a lot of internal urls stored in fields and output via templates that also need to carry the segment. There's also a large, sitemap-style menu that uses the $cache feature, and I didn't want to cache every variation. This way I can cache the large menu aggressively, and use replacements when rendering. 

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