Jump to content

Including dynamic content in search results


joe_ma
 Share

Recommended Posts

Hi

On a new site I use pw as a data base for addresses (located in the admin section of the page tree). On the pages these addresses are displayed in a table according to certain selectors.

How can I include the content of these tables in the search results, in a way that the resulting links don't go to the data base but to the page, where the address is listed?

Like this:

Page about old age lists all addresses that have anything to do with old age like:

- social insurance

- rest home 1

- rest home 2

You get the idea.

Now I'd like the search to "see" that "rest home 1" exists in the data base, but links to the page about old age.

With the default settings of the search template, search results don't list information from the data base. I.e. if I search for "rest home 1", nothing is found.

Thanks for help.

Link to comment
Share on other sites

  • 2 weeks later...

You can do that, but there's no way to detect this automagically. You'd need to define this connection either in code or even in the data layer by using e.g. a pagefield to link pages to their appearance-pages (if different).

Link to comment
Share on other sites

Further to what LostKobrakai said regarding storing your references to other pages in a Page field...

So you have these pages somewhere in your site:

  • social insurance
  • rest home 1
  • rest home 2

And then you have a page "old age" that stores references to the pages above using a Page field called "addresses". You should be able to match "rest home 1" to page "old age" with a search selector like:

$pages->find("addresses=rest home 1");
Link to comment
Share on other sites

Thanks a lot for both of your answers.

Yes, I think I didn't make myself very clear here. So, let me try it again:

The website in question gives all sorts of information for people who deal with migration. I.e. administration people, employers and migrants themselves. Have a look for yourselves.

There is also a data base with somewhat over 600 addresses. Each of them is a page, but doesn't have a template to be displayed. 

Both the information pages and the address pages have an asm select field for category.

On the information pages these addresses are displayed according to their categories in a table. See this example:

At the bottom of this page you find the addresses for the category "wohnen".

What I'd like to achieve is, that (staying with the example above) when someone searches for "Ombudsstelle" (second row of the address table), the search results in the url of the information page rather than the address page of the "Ombudsstelle".

Link to comment
Share on other sites

Here is a start for how you could create your search. It needs to be fleshed out for usage as global site search.

For search input "rest home 1"...

$addresses = $pages->find("template=address, title=rest home 1");
$categories = new PageArray();
foreach ($addresses as $address) {
    $categories->add($address->categories);
}
$categories = $categories->unique(); // not strictly necessary
$results = $pages->find("template!=address, categories=$categories");

As an aside, because you are using Javascript pagination for your address list you could consider populating the filter field from a get variable for search results. To make it easier for visitors to see the address they have searched for if that address appears on page 3 or whatever.

Nice looking site, BTW.

Link to comment
Share on other sites

Nearly there, but not quite …

So my search code looks like this:

	// Find pages that match the selector
	$matches = $pages->find($selector); 	
	$cnt = $matches->count;

	// did we find any matches?
	if($cnt) {

		// yes we did: output a headline indicating how many were found.
		// note how we handle singular vs. plural for multi-language, with the _n() function
		$content = "<h2>" . sprintf(_n('Found %d page', 'Found %d pages', $cnt), $cnt) . "</h2>";

		// create dt-list with results
		$content .= "<dl>";
		foreach ($matches as $m){
			if ($m->parent->id = 1293) { // Page is an address and has no template to be displayd
				$rubriken = new PageArray();
				foreach ($m as $adr){
					$rubriken->add($adr->adr_rubrik);  // add the categories from the pagefield (asm select) on the address page
				}
				$rubriken = $rubriken->unique;
				$results = $pages->find("template=basic-page, adr_rubrik=$rubriken"); // find the information pages with template basic-page that have the same categories as the address page
				foreach ($results as $r){
					$content .= "<dt><a href='$r->url'>$r->title</a><br><span>URL: $r->url</span></dt>";
							if(strlen($r->body) > 250) { 				// Limit displayed text to 150 signs
							 $r->body = substr($r->body,0,250).'…';
						     $string_ende = strrchr($r->body, ' ');
	        				 $r->body = str_replace($string_ende," …", $r->body);
							}
							$content .= "<dd>" . strip_tags($r->body) . "</dd>";	
				}
			}
			else { 
					$content .= "<dt><a href='$m->url'>$m->title</a><br><span>URL: $m->url</span></dt>";
							if(strlen($m->body) > 250) { 				// limit displayed text to 150 signs
							 $m->body = substr($m->body,0,250).'…';
						     $string_ende = strrchr($m->body, ' ');
	        				 $m->body = str_replace($string_ende," …", $m->body);
							}
							$content .= "<dd>" . strip_tags($m->body) . "</dd>";		
			}
		}
		$content .= "</dl>";

It seems like this is producing some kind of loop: the results keep repeating themselves.

But I can't find out, where I made a mistake.

As an aside, because you are using Javascript pagination for your address list you could consider populating the filter field from a get variable for search results. To make it easier for visitors to see the address they have searched for if that address appears on page 3 or whatever.

Nice idea. But I haven't got a clue on how to do this. The filter is setup by the dataTable javascript.

Link to comment
Share on other sites

I suggest you go a different route. The downside to the way you are finding pages in your code above is that you can't do a consistent pagination of results. This is because you do a foreach() within your results list which produces an unknown number of additional results. So say if you need to apply a limit of 20 results for pagination you could end up with 22 on page 1 and 37 on page 2, etc.

To avoid this it's best to get your results from a single $pages->find() operation. You don't show what is going into your $selector variable above, but below is some example code. The variable $search_term would be the sanitized term(s) coming from your search form.

// Define the piece of the selector that checks fields for the search term
// Let's say we only need to check the body field
// But you can expand this with any other fields you need to search
$selector_piece = "body~=$search_term";

// First we find any matching address pages
$addresses = $pages->find("template=address, address_field~=$search_term");
$categories = new PageArray();
foreach($addresses as $address) {
    // And add their categories to a PageArray
    $categories->add($address->categories);
}
$categories = $categories->unique(); // Strip out any duplicates
if($categories->count) {
    // Now we use OR-groups to say either the body must contain the search term
    // or the page must have the same category as one of the matching addresses
    $selector_piece = "($selector_piece), (categories=$categories)";
}

// Search the website pages, but not address pages because we already dealt with them
// If there is the potential of a lot of matches you should apply a pagination limit
$matches = $pages->find("$selector_piece, template!=address, limit=20");

// Output the matches, if any
if($matches->count) {
    foreach($matches as $match) {
        echo "<p><a href='{$match->url}?searched={$search_term}'>{$match->title}</a></p>";
    }
} else {
    echo "<p>Sorry, no matches.</p>";
}

And then in your pages where you show addresses in a filterable table:

<script>
$(function() {
    $.urlParam = function(name) {
        var results = new RegExp('[\?&]' + name + '=([^]*)').exec(window.location.href);
        if(results==null) {
            return null;
        } else {
            return results[1] || 0;
        }
    };
    var searched = $.urlParam('searched');
    if(searched){
        $('#filter').val(searched);
    }
});
</script>

Adjust $('#filter') to match your filter field as needed. You might need to attach this to a callback that runs once the table plugin has initialised.

Link to comment
Share on other sites

Wow!

Thank you very much for this concise answer.

My search.php now looks like this:

<?php

// look for a GET variable named 'q' and sanitize it
$q = $sanitizer->text($input->get->q); 

// did $q have anything in it?
if($q) { 
  $input->whitelist('q', $q); 
  $q = $sanitizer->selectorValue($q); 

// Search the title and body fields for our query text.
$selector = "title|body~=$q"; 

// First we find any matching address pages (category field = adr_rubrik)
  $addresses = $pages->find("template=adress_daten, title|adr_beschreibung~=$q");
  $categories = new PageArray();
    foreach($addresses as $address) {
      // And add their categories to a PageArray
      $categories->add($address->adr_rubrik);
    }
    $categories = $categories->unique(); // Strip out any duplicates
    if($categories->count) {
    	// Now we use OR-groups to say either the body must contain the search term
    	// or the page must have the same category as one of the matching addresses
    	$selector = "($selector), (adr_rubrik=$categories)";
    }
    // Search the website pages, but not address pages because we already dealt with them
    // If there is the potential of a lot of matches you should apply a pagination limit
    $matches = $pages->find("$selector, template!=adress_daten, limit=20");

if($user->isLoggedin()) $selector .= ", has_parent!=2"; 

$cnt = $matches->count;

// did we find any matches?
if($cnt) {

  // yes we did: output a headline indicating how many were found.
  $content = "<h2>" . sprintf(_n('Found %d page', 'Found %d pages', $cnt), $cnt) . "</h2>";
  // create dt-list with results
  $content .= "<dl>";
  foreach ($matches as $m){
    $content .= "<dt><a href='$m->url'>$m->title</a></dt>";
      if(strlen($m->body) > 250) { // limit to 150 signs
        $m->body = substr($m->body,0,250).'…';
        $string_ende = strrchr($m->body, ' ');
        $m->body = str_replace($string_ende," …", $m->body);
      }
    $content .= "<dd><span><a href='$m->url'>URL: $m->url</a></span><br>" . strip_tags($m->body) . "</dd>";		
  }
  $content .= "</dl>";

} else {
  // we didn't find any
  content = "<h2>" . __('Sorry, no results were found.') . "</h2>";
}

} else {
	// no search terms provided
	$content = "<h2>" . __('Please enter a search term in the search box (upper right corner)') . "</h2>";
}

This works like a charm, thanks to Robin's help.

Only thing: pagination doesn't seem to work. The limit in $matches limits the results themselves, not the display of the results. When I set the limit to e.g. 5, even the title says «Found 5 pages», even when there are more results than this.

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