Jump to content

Getting fake URLs (with UrlSegments) returned in search results


adrian
 Share

Recommended Posts

I have read through several posts on fake URLs (for want of a better term) and UrlSegments, but I didn't find anything talking about search results - maybe I just missed the key post!

I have a database of people stored simply like this:

People (template = people)

--Joe Blogs (template = person)

--Jane Doe (template = person)

Within the 'person' template fields there is an ASM field that allows selection of several categories eg: staff, affiliate, contractor etc.

All simple enough - I want to display these people under different menu items / taxonomy, eg:

About > Staff > 

Partners > Affiliates >

Partners > Contractors >

Which is easy enough to handle with UrlSegments. But the main obstacle I am having is setting up the search template to return fake URL. I think I can probably make something work, but I feel like there should be an easier way to achieve what I want.

The key thing of course is that certain people will appear in more than one fake URL, which is why I am going down this path in the first place, rather than simply storing them under their correct page hierarchy. 

Thanks for any thoughts

Link to comment
Share on other sites

In the search file you have a foreach loop so you could just generate these fake urls on the fly. Like:
 
<?php

foreach($results as $result) {
	$url = $config->urls->root.$result->category.'/'.$result->name.'/';
	echo '<a href="'.$url.'">'.$result->title.'</a>';
}

?>

(doesn't tried it)

  • Like 1
Link to comment
Share on other sites

Thanks Nico - but how do we populate the $results array? How can any $pages->find selector actually find these "pages".

Thinking bigger here, I wonder if it might be possible to create an 'alias' module. Using my example above, there would be a new template setting, so I would go to this setting for the staff, affiliates and contractors templates and define that they are aliases for the people template. Then any $pages->find would also search these and consider them as part of the standard page hierarchy.

I wonder if the alternative template setting under advanced tab (requires $config->advanced true) could be of any use here?

Not sure if my logic really holds up, but I think the concept could be great.

Link to comment
Share on other sites

Well, normally you do a search kind of like this:

<?php

$results = $pages->find('headline|title|body%='.$config->input->get->q);

?>

This will result in a PageArray, so you can run the foreach loop after it like I said before

Link to comment
Share on other sites

Thanks Macrura - very interested to see what you come up with.

Conceptually I can't figure out the best way to go from the returned results, which actually 404 in my case because there is no template file for the actual page in the tree, to the working fake URL. 

I could set up custom replacements for these before returning the search results. This really should be trivial, but I guess I am looking for a more generic and portable solution. Part of the issue is that it would need to return more than one result - eg it would need to return links to:

About > Staff > 

Partners > Affiliates >

Partners > Contractors >

 

even though the actual result returned from the $pages->find would simply be People > person.

 

This could also be achieved for this case, but I have a couple of other completely separate taxonomy issues like this as well. Just starting to seem ugly.

Without going the module route, I am wondering about maybe a field for the relevant templates that allows specifying the actual working (fake) URLs and building the search page to check these fields before returning results and return all the possible paths as results.

Not sure how well I described that, but I think I have a working idea at least. I'll tackle this tomorrow and post when I have a working solution.

Link to comment
Share on other sites

note this is untested, and i haven't used clone before...but i think you are looking for something like this (though this needs help from a real php person)

<?php 

$results = $pages->find('title|category.title%='.$config->input->get->q);

$people = new Pagearray();

foreach ($results as $result) {
	
	$cats = $result->categories;

		foreach($cats as $cat) {	

			$result = clone $result;
			$result->category = $cat;

			$people->add($result);

		}
	
}
?>
Link to comment
Share on other sites

Ok, I have something that works. I am not sure how portable it is yet (I haven't really done any testing), but it seems to work well for my scenario. This is my entire search.php template file. Basically what it does is:

  • Find all pages with the search term (as the standard search.php does)
  • Iterate through all the fields within those returned pages to see if any pages within the $pages array have a name which matches any of the field names
  • If there is a match, build the link using the name of the matching page in the path
  • Add viewable() check to the standard pages found to prevent display of the People > links

The key thing (if it's not obvious), is that you must name the field (in my case a checkbox for each of staff, affiliates, contractors to match the name of the page that displays them (the fake url).I know this won't work at all in many scenarios, and I have some ideas for making it more portable and be able to handle situations where you want to use an ASM to choose the categories, but in a rush, so this will do for now :)

<?php

$content = '';
$resultText = '';
$count = 0;

if($q = $sanitizer->selectorValue($input->get->q)) {

	// Send our sanitized query 'q' variable to the whitelist where it will be
	// picked up and $content .=ed in the search box by the head.inc file.
	$input->whitelist('q', $q);

	// Search the title, body and sidebar fields for our query text.
	// Limit the results to 50 pages.
	// Exclude results that use the 'admin' template.
	$matches = $pages->find("title|body|summary%=$q, limit=50");

	if(count($matches)) {

		foreach($matches as $m) {

			$pm = '';

			foreach($m->fields as $field) {
			  $corrected_fieldname = str_replace('_','-',$field->name); //To match page name
			  $pagematches = $pages->find("name={$corrected_fieldname}");
			  $pagescount = count($pagematches);

			  if($pagescount>0){
				  foreach($pagematches as $pm){
				  	if($m->$field!=0){
				  		$count++;
				  		$content .= "<li><p><a href='{$pm->url}{$m->name}/'>". (($pm->title!='') ? $pm->title . " > " : "") . $m->title . "</a><br />{$m->summary}</p></li>";
				  	}
				  }
			  }
			}
			if($m->viewable()){
				$content .= "<li><p><a href='{$m->url}'>". (($pm && $pm->title!='') ? $pm->title . " > " : "") . $m->title . "</a><br />{$m->summary}</p></li>";
				$count++;
			}
		}

		$content .= "</ul>";

		$resultText .= "<h2>Found $count pages matching your query:</h2>" .
			"<ul class='nav'>";

	} else {
		$content .= "<h2>Sorry, no results were found.</h2>";
	}
} else {
	$content .= "<h2>Please enter a search term in the search box (upper right corner)</h2>";
}

$content = $resultText . $content;

include("./main.inc");
Link to comment
Share on other sites

Hey Ryan,

I should have explained the logic behind the viewable(). It is there to make sure that any pages that get returned have a template. Because $m contains all pages in the tree it will also contain the "People" parent and all its children. This was my way of preventing these from being displayed in the search results, without needing to make them hidden.

The reason I didn't want to rely on hidden is because I will have users creating these people child pages and figured they may not always/ever remember to set them hidden, although now that I think about it, I bet it is possible to force child pages to be hidden - is it?

Curious if you have any suggestions for a better way to do this - I do feel like I am hacking a little to make this work with proper search results and breadcrumbs etc. It all works, but I feel like there should be a more standard way of doing it.

Link to comment
Share on other sites

Just wanted to note that I just updated the code block above.

It now includes an additional 'if' to make sure that the checkbox field in question is actually checked.

It also accurately counts the number of returned results.

And it appends the parent title to the link title so users can see the difference between two links that otherwise look the same - eg Affiliates > Joe Blogs and Contractors > Joe Blogs.

Seems to be behaving now, but I am sure I will come across another problem scenario or two before it is fully dialed.

Link to comment
Share on other sites

Curious if you have any suggestions for a better way to do this - I do feel like I am hacking a little to make this work with proper search results and breadcrumbs etc. It all works, but I feel like there should be a more standard way of doing it.

I'm a little confused by how the whole thing works, despite reading the code a couple times. But if it works, then seems like it should be fine. The only thing I'd worry about here is scale, as you have potentially numerous find operations going on in these loops. But if you don't need it to scale too large, then it may not be an issue. Since I'm confused by the code itself, let's step back and go to the original question: 

Which is easy enough to handle with UrlSegments. But the main obstacle I am having is setting up the search template to return fake URL. I think I can probably make something work, but I feel like there should be an easier way to achieve what I want.

Another option here would be to go ahead and implement a template file for your 'person' pages, and just let your search connect with the output of those pages.

If you didn't want to go that route, you could always just make your search output loop handle pages using template 'person' differently: 

if($m->template == 'person') {
  foreach($m->categories as $c) {
    echo "<li><a href='$c->url'>$c->title</a></li>";
  }
} else {
  echo "<li><a href='$m->url'>$m->title</a></li>";
} 
  • Like 1
Link to comment
Share on other sites

Hey Ryan,

I don't really want to have a template for the person pages, because unless I am not understanding, this would result in search returning links to pages like:

/people/joe-blogs/

The would be a page that doesn't exist within the site's menu system and would send the user to something out of context. The template for my staff vs partners is also quite different - I want quite different output I was tempted to just separate and duplicate those people that are multiple categories, but that goes against the grain and doesn't make sense.

I really like the simplicity of your approach to just handle the search output differently for a specific template. Obviously much cleaner and more scalable than my approach.

What I was attempting to do with that messy code above was create something portable (although I agree it probably doesn't scale very well). They way that code works is to look for any field names in the matched pages

foreach($m->fields as $field) {
 

that match page names elsewhere in the page tree 

$pagematches = $pages->find("name={$field->name}");
 

For each match where the field is checked, generate a page with the matched page name.

if($pagescount>0){
                 foreach($pagematches as $pm){
                     if($m->$field!=0){
 

So this code can be used (I think :)) to match any of these scenarios that come up, without having to define the templates that need different treatment.

That said, if you couldn't understand it, then it probably isn't a good approach. I'll probably leave it as is for now since it seems to work fine, but maybe next time I come across this situation I'll take your approach instead, unless you can think of a way to clean up what I have into something that could be scalable and fully portable :)

I still have in my mind some module that would allow the ability to define custom rules to manipulate the pagearray based on these sorts of categorizations so that all API calls were seamless. In this example, people/person would be removed from the pagarray and 

About > Staff > 

Partners > Affiliates >

Partners > Contractors >

 

would be manipulated to return the fields and data from the person template. Although as I try to describe this, it sounds pretty messy :) 

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