Jump to content

sorting by page title, with some complexity


evan
 Share

Recommended Posts

Hi,

Is there an easy way to add rules to the "sort" selector, or otherwise manipulate search results directly with PHP?  I'm trying to sort results by Page title, but I want to ignore any initial "the", "an", or "a."

I know I can export the results into an array, manipulate them, and then display them, but that would disrupt pagination, and seems clunky. Surely there's an elegant solution?

Thanks!

-evan

Link to comment
Share on other sites

Hi evan,

maybe this does work?

$pages->find('!title^=the |an |a , sort=title');

Just an idea. There are some limits on the length of words, i'm not an expert here.

If it doesn't work, I'd go with the array.

Link to comment
Share on other sites

Hi evan,

maybe this does work?

$pages->find('!title^=the |an |a , sort=title');

Just an idea. There are some limits on the length of words, i'm not an expert here.

If it doesn't work, I'd go with the array.

Not really. This excludes pages such as "their", "another", "about"

Link to comment
Share on other sites

Just to clarify, I'm not looking to exclude Pages that begin with the/a/an, but rather alphabetize them by the second word if they do start with any of those.  For example, given these Page titles:

Aguirre, the Wrath Of God
The Wild Blue Yonder
Cave of Forgotten Dreams
Stroszek
Lessons of Darkness
A Heart Of Glass

It should produce:

Aguirre, the Wrath Of God
Cave of Forgotten Dreams
A Heart Of Glass
Lessons of Darkness
Stroszek
The Wild Blue Yonder

What would be amazing if there was an optional field in find() that would let you call up a PHP function, in which you could manipulate the results.   Unless, of course, you can do something like that already.

-evan

Link to comment
Share on other sites

Add a text field to add an alternative sort title to sort by it instead. No other way if you still want to keep it easy with pager.

Yeah, I was thinking that.  I guess that'll have to do.

-evan

Link to comment
Share on other sites

what about a simple string replace on the search results (get rid of leading the, an, a)..

$sort_title = preg_replace("/^(the¦a¦an)\b\ */i", "", "Some title");

That'll work, but as soma said, I'll have to store that value in the Page in order for it to be alphabetized as I want it to.  Thanks!

-evan

Link to comment
Share on other sites

Maybe you can get InputfieldPageName module to the site/modules/ folder and change the JS name behaviour. 

( making the name fields javascript remove the / an / a / )

 
$title = "The tralalala";
$title = trim(preg_replace("/^(the¦a¦an)\b\ */i", "", $title));
$iLookLikeName = $sanitizer->pageName($title);
$pages->find("name=$iLookLikeName, sort=name");
  • Like 1
Link to comment
Share on other sites

I ended up going with soma's suggestion, and just storing the alternate title as field sort_title in the Page.

Here's the function I made, triggered by the hourly LazyCron:

function createSortTitles(HookEvent $e) {
	$items = wire('pages')->find('template=item');
	foreach ($items as $item) {
		if ($item->sort_title=="") {
		$item->setOutputFormatting(false);
		$new_title = (preg_match( "/^(the|a|an)\s/i", $item->title)) ?
			substr(strstr($item->title," "), 1) : $item->title;
		$item->sort_title = $new_title;
		$item->save();
		}
	}
}

For some reason I couldn't get the selector sort_title='' to work correctly -- it would only return a portion of the Pages.  Hence the conditional instead.

-evan

  • Like 1
Link to comment
Share on other sites

With just a little more work you could put that function into a module that runs on page save which would then be ideal for your needs :)

For example, something like this:

<?php
class SortPageTitles 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's title, typically a little more descriptive than the class name
            'title' => 'Sort Page Titles',

            // version: major, minor, revision, i.e. 100 = 1.0.0
            'version' => 100,

            // summary is brief description of what this module is
            'summary' => 'Sorts pages by title by second word if first word is "a, the, an" etc.',
            
            // Optional URL to more information about the module
            'href' => 'http://processwire.com/talk/topic/3306-sorting-by-page-title-with-some-complexity/',

            // 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,
            );
    }

    /**
     * Initialize the module
     *
     * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called
     * when ProcessWire's API is ready. As a result, this is a good place to attach hooks.
     *
     */
    public function init() {

        // add a hook after the $pages->save, to sort certain pages if necessary
        $this->pages->addHookAfter('save', $this, 'sorttitles');
    }

    /**
     * Hooks into the pages->save method and sorts pages with a certain template based on certain criteria
     *
     */
    public function sorttitles($event) {
        $page = $event->arguments[0];
        
        // Only run if the page we just saved has the "item" template
        if ($page->template == 'item') {
            $items = wire('pages')->find('template=item');
            foreach ($items as $item) {
                if ($item->sort_title=="") {
                $item->setOutputFormatting(false);
                $new_title = (preg_match( "/^(the|a|an)\s/i", $item->title)) ?
                    substr(strstr($item->title," "), 1) : $item->title;
                $item->sort_title = $new_title;
                $item->save();
                }
            }
        }
    }
}

I've not tested it, but it's based on the helloworld module, so if you download it (attached) and put it in your /site/modules folder it should work for you, and hopefully be a good example of how easy it is to write modules too :)

SortPageTitles.module

  • Like 3
Link to comment
Share on other sites

Hey, nice!  I did notice that there was a page->save() hook after the fact, maybe I'll try that out.  It was just quicker for me to do LazyCron, as I'd done it before.  But it's always good to know a little more about PW!

-evan

Link to comment
Share on other sites

No probs. On a few sites I've ended up creating a little site-specific module like this to check for various templates and do certain things after page save among other things and they can be really useful.

Link to comment
Share on other sites

Well it's sort of specific to the template/fields evan has on his site, so no point in submitting it really unless you're reproducing his exact same structure.

All it is is his function inside the default helloworld module that comes with every PW install (check inside site/modules directory, and read more here: http://processwire.com/api/modules/ ). You'll be up and running with your own little helper modules in no time :)

Link to comment
Share on other sites

... and end up in endless loop land. ;-)

Better just save the field only $item->save('sort_title') so the hook doesnt get called again and again and...

Ah yes, Soma is of course correct that since it runs on page save and saves the page at the end it will go on FOREVER. Like I said, it wasn't tested :P

Changing $item->save to $item->save('sort_title') will of course work like he says.

Link to comment
Share on other sites

If you hook onto page->save(), it's no longer necessary to loop over pages->find():

    public function sorttitles($event) {
        $page = $event->arguments[0];
        
        // Only run if the page we just saved has the "item" template
        if ($page->template == 'item') {
                $new_title = (preg_match( "/^(the|a|an)\s/i", $page->title)) ?
                    substr(strstr($page->title," "), 1) : $page->title;
                $page->sort_title = $new_title;
                $page->save('sort_title');
                }
            }
        }
    }
}

-evan

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

×
×
  • Create New...