Jump to content

search results should be grouped


horst
 Share

Recommended Posts

On a little site I have a pagetree like:

home
    main
        sub
            page
            page
        sub
            page
    main
        sub
            page
        sub
            page
            page
    main
        ...

every page use the same template, every page has a tag field with a string shown as above (main, sub or page)

The same structure is used for the navigation and sitemap on the frontend too.

What I would like to achieve is the same presentation for search results. Therefor I need a results array ordered this way. Is it possible to cover this with the search selector only?

The amount of total pages is less than 100 and ever would be less than 300 in the future too. The pages are fetched from a big german online electronic kompendium. I need the site to filter only the very basics from the online site which seems to hold many thousand pages. It is for local / private use only. (for my son as a searchable reference)

Link to comment
Share on other sites

I've done something like that in building a menu based on search results.

In your case you could generate the sitemap but only show an item after a check if it exists in your search result.

More in detail:

You can create a basic php array from the sitemap as well from the search result with page id's.

Then, showing the sitemap you could check for every item with php function "in_array" if the page id exists in the search array and only then echo the item.

Worked perfect for me in a travelsite I built where people first choose a sort of trip.

Based on the choice the site is searched for that type of trip (multiselectfield in every page) and after that a two level menu is created based on parts of the world and countries. So: part of the world - country - trip, more or less the same as your main - sub - page.

Hope this gives you a direction.

  • Like 2
Link to comment
Share on other sites

Hi reems, this sounds really good and I have tried it out.

I use a slightly modified renderNav function from the current dev branch minimal site profile for that. But unfortunately the page ids of the main and sub pages are not within the matches of a search query. With this way to generate the sitemap / search results output it does not work.

// $items always is the homepage
// $matches is an array with key-values the same: $matches[id] = $id;
function renderNav4($matches, $items, $maxDepth = 0, $fieldNames = '', $class = 'nav') {

    // if we were given a single Page rather than a group of them, we'll pretend they
    // gave us a group of them (a group/array of 1)
    if($items instanceof Page) $items = array($items);

    // $out is where we store the markup we are creating in this function
    $out = '';

    // cycle through all the items
    foreach($items as $item) {

        if('site-map'==$item->name) continue;

        $show = isset($matches[$item->id]);

        // markup for the list item...
        // if current item is the same as the page being viewed, add a "current" class to it
        $out .= $show ? ($item->id == wire('page')->id ? "<li class='current'>" : "<li>") : '';

        // markup for the link
        $out .= $show ? "<a href='$item->url'>$item->title</a>" : '';

        // if there are extra field names specified, render markup for each one in a <div>
        // having a class name the same as the field name
        if($fieldNames) foreach(explode(' ', $fieldNames) as $fieldName) {
            $value = $item->get($fieldName);
            if($value) $out .= $show ? " <div class='$fieldName'>$value</div>" : '';
        }

        // if the item has children and we're allowed to output tree navigation (maxDepth)
        // then call this same function again for the item's children
        if($item->hasChildren() && $maxDepth) {
            if($class == 'nav') $class = 'nav nav-tree';
            $out .= renderNav4($matches, $item->children, $maxDepth-1, $fieldNames, $class);
        }

        // close the list item
        $out .= $show ? "</li>" : '';
    }

    // if output was generated above, wrap it in a <ul>
    if($out) $out = "<ul class='$class'>$out</ul>";

    // return the markup we generated above
    return $out;
}

Using it this way produces only a list of pages, not the hierarchical structure of the sitemap. I need the main and sub branches, but only where they contain at least one page in matches.

Any other ideas or tips?

Link to comment
Share on other sites

Hi,

In the travelsite I have taken a little different approach.
I'm just a sparetime php scripter, so I'me sure the more experienced ones can simplify the following code or come up with a better solution.
I've changed what I use a little bit to fit your situation.
If there would be more levels of pages a recursive function would also be better I think.
$selection = $pages->find("your search");

// check if there is a search result
if(count($selection) > 0)
	{
	foreach($selection as $selected) 
	  {
	  // get id's of found pages
	  #selectionid[] = $selected->id;
	  
	  // get id's of parent pages
	  $firstparentsid[] = $selected->parent_id;
	  
	  // get parents for next foreach
	  $firstparents[] = $selected->parent;
	  }	
	  
	foreach($firstparents as $first) 
	  {
	  // get page id's of next level
	  $secparentsid[] = $first->parent_id;
	  }
	
	// combine all id's
	$all_ids = array_merge($selectionid , $firstparentsid , $secparentsid);
	// remove double results
	$all_ids = array_unique($all_ids);        
	}
In my case where I use this in the travel site to build menus it's different from your case because my mainmenu and submenu are build seperately (different templates) where a sitemap has to combine the different levels.
In your case you can check if the pageid of a link is included in the $all_ids array before printing it in your sitemap.
 
I have a sitemap in the travelsite for which I use the listChildrenTree function in this topic:
https://processwire.com/talk/topic/1886-submenu-with-overview-links/
 
It should be simple to add a check with the array to the echo statements.
 
 
  • Like 1
Link to comment
Share on other sites

Hi reems, thanks!

Yes what was needed was to collect the ids of the main- and sub- levels too. The above posted renderNav4 function need no changes then. Just passing an array with involved main-, sub- and page-ids to it works. :)

The only thing I would do a bit different is to collect the ids in assoc key=>value arrays:

// $myArray[] = $page->id;
$myArray[$page->id] = $page->id;

This way you don't get double or multiple entries (no need for calling array_unique), but much more important: you are able to check for existence of an id by calling

if(isset($myArray[$id]))

what is much, much faster than

if(in_array($id, $myArray))

So, I think this only becomes important with huge arrays and a high amount of calls to the function.

post-1041-0-16833800-1407447872_thumb.jp

  • Like 1
Link to comment
Share on other sites

Nice work :)

And your advice about the arrays is something I certainly going to implement. Thanks.

But....what about the different colors in your sitemap, how did you do that? 
Bases on page tags or so?

  • Like 1
Link to comment
Share on other sites

Yes, I have added three fields to the template: mainid, subid and tag.

I use an array with a predefined structure that I use for adding pages via API:

$struktur = array(
    'grundlagen' => array(
        'elektrotechnische-physik',
        'stromkreisgesetze',
        'elektrotechnische-chemie',
        'wechselstrom-und-wechselspannung',
        'signal-und-energiequellen',
        'messtechnik',
        'praxis'
    ),
    'bauelemente' => array(
        'lineare-und-nichtlineare-widerstaende',
        'kapazitive-und-induktive-bauelemente',
        'halbleiter',
        'transistoren',
        'integrierte-schaltungen',
        'sonstige-bauelemente',
        'praxis'
    ),
    'schaltungstechnik' => array(
        'grundschaltungen',
        'transistorschaltungen',
        'stabilisierungsschaltungen',
        'anwendungen-mit-operationsverstaerker',
        'praxis'
    )
);

Here a bit simplyfied the import process:

foreach($struktur as $main => $subs) {
    $mainPage = $pages->get("/$main/");  // let out the check if the mainPage exists or need to be created here
    foreach($subs as $sub) {
        $subPage = $pages->get("parent=$mainPage, name=$sub");// let out the check if the subPage exists or need to be created here
        // ... execute code for fetching the urls for this subPage here ...
        foreach($urls as $url) {
            $dump = @file_get_contents($url, false, $context);
            if(!$dump) continue;
            // fetch or create this page
            $newPage = $subPage->child("source_url={$url}");
            if(0==$newPage->id) {
                // create a new page
                $newPage = new Page();
                $newPage->parent = $subPage;
                $newPage->source_url = $url;
                $newPage->tag = 'page';            // can be one out of main, sub, or page
                $newPage->mainid = $mainPage->id;  // store main parent id
                $newPage->subid = $subPage->id;    // store sub parent id
                ... (template, title, name, etc)
                $newPage->save();
            }
            $assetsUrl = '/site/assets/files/' . $newPage->id . '/';
            // ... parse and adjust the body content, store it into $newContent ...
            $newPage->of(false);
            $newPage->body = $newContent;
            $newPage->save();
            wire('pages')->uncache($newPage);
        }
    }
}

This way i have all needed information in the contentpage itself. In my search template I use

        $selector = "title|body%=$q, tag=page, limit=$limit";
	$matches = $pages->find($selector);

        $selection = array();
        foreach($matches as $match) {
            $selection[$match->id] = $match->id;
            if(0<intval($match->mainid)) $selection[$match->mainid] = $match->mainid;  // add involved mainPage to the ids
            if(0<intval($match->subid)) $selection[$match->subid] = $match->subid;      // add involved subPage to the ids
        }

In my case here this was all to do to get it working with the above posted renderNav4. Into the generated output of the renderNav4 I have added css classes:

"<li class='main{$item->mainid} type{$item->tag}'>"

I only need to lookup the ids of the three mainPages and add these lines to main.css:

    li.main2908 a, li.main2908 { color: #465E8A !important; }
    li.main2916 a, li.main2916 { color: #5B7D12 !important; }
    li.main2924 a, li.main2924 { color: #AB5D65 !important; }

In the sidebar I display the list of the next higher level. It is a really nice and functional site what doesn't take much effort because of Ryans new minimalistic SiteProfile in dev-branch. :) I only had to manage the import and had to modify the renderNav function into two different behaviours. I love ProcessWire :wub:

Screenshots:

post-1041-0-08236300-1407512554_thumb.jp post-1041-0-01706500-1407512555_thumb.jp post-1041-0-57169500-1407512556_thumb.jp post-1041-0-55826000-1407512557_thumb.jp

  • Like 2
Link to comment
Share on other sites

A good day is a day I've learned again something (of PW)  ;)

+1

A bit OT regarding the threadtitle but another great thing that one can use with ProcessWire is the Hannacode module.

When fetching pages from an online resource and store them locally I want that links in the bodycontent point to the local resources if they are available. But which one is, will or should be locally stored isn't easy to determine when fetching the (first) pages is in progress. Therefore I installed Hannacode and store every a-href in the bodycontent as hannacode: [[source_url source_url=http://example.com/path/to/page.html]]

The codesnippet that get invoked for every link:

    $p = $pages->get("template=elektronik, source_url=$source_url");
    if(0<$p->id) {
        // page is locally available
        echo $p->url;
    } else {
        // page is only online available, add a css class to the tag that this can be shown in the page output
        echo $source_url . '" class="externLink';
    }

This way I can add or remove pages without breaking (locally) references in other pages.  :)

post-1041-0-74001000-1407516712_thumb.jp

Summary: created an importer script, modified the renderNav function, added one Hannacode and some css styles = a nice and comfortable to use site! :)

  • 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

  • Recently Browsing   0 members

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