html - PHP Search Results display
Get the solution ↓↓↓I am creating a basic search feature for my framework and looking for some advice as to how to display the search results the best way (google style). My MYSQL query returns distinct pages based on the search query. The results back from MySQL is perfect, I just need to do the following:
An example could be that someone search the term "Hello World". My search results will return all rows which contain both "hello" and "world".
What I am trying to achieve is:
- Highlight the words in the search query but only a fragment of the results. I would like to only return 200 characters and highlight (bold) the first occurrence of any of the words in the search term.
- The copy displayed would have been created in a CMS and has html tags. I am able to strip the html tags before displaying it but want to get feedback if I am doing it the correct way.
The code I am using currently is:
// The query string:
<?php $q = urldecode($_GET['qString']); ?>
// Run a loop through the results:
<?php foreach ($this->get("pageResults") AS $result): ?>
// a clickable H3 to the actual page:
<h3><?= $this->html->link($result['sub_heading'] . " " . $result['heading'], array("controller" => "pages", "action" => "viewer", "properties" => array($result['name']))) ?></h3>
<?php
// Strip all html characters as the content comes from an WYSIWYG editor:
$value = preg_replace('/<[^>]*>/', '', $result['content']);
// Find the position within the text:
$position = stripos($value, $q);
// If a positive position, display 200 characters and start -100 from the first occurance
if ($position == true) {
$string = substr($value, $position - 100, 200);
} else {
$string = " ... ";
?>
<p><?= $string ?></p>
<hr />
<?php endforeach; ?>
The main problem I run into here is:
- The search results would have returned rows even if the query string
was not precise (so it will return a result if the column contained
"hello" and "world" whereas
stripos
will only find "hello world". - I do'nt know the best way to wrap
<strong></strong>
tags around the first occurrence of a word or phrase within the stripped html. I understand that this may be a tricky thing to achieve especially due to the occurrence issue. I can live without this feature but if there is a nifty way of doing it that would be great :)
Any ideas would greatly be appreciated!
Answer
Solution:
I would advice you to read up about Natural Language Full-Text Searches
This is the most (based on my opinion), optimized way when doing a search functions.
Answer
Solution:
The search results would have returned rows even if the query string was not precise (so it will return a result if the column contained "hello" and "world" whereas stripos will only find "hello world".
This seems like a simple answer, but seeing as that you are passing the query string through the url I assume it will look something like this:
?searchText=Hello%20World
So you could break out the words on the spaces (using explode) and create a position array:
$positionArray = array();
$qs = explode($q, '%20');
$value = preg_replace('/<[^>]*>/', '', $result['content']);
foreach( $qs as $qword ){
$position = stripos($value, $qword);
array_push($positionArray, $position);
}
So now you would have an array of positions where your words appear in the results:
positionArray = [4, 15, 32];
So you could start the relevant highlighting tag (strong or whatever you're using) at these positions and then close them at the end of the word OR you could find the start position AND the end positions of the words using something like this:
foreach( $qs as $qword ){
$start_position = stripos($value, $qword);
$end_position = $start_position + strlen($qword);
array_push($positionArray, {qword: $qword, start_position:$start_position, end_position:$end_position});
}
Unfortunately I havent got time right now to think about how you would insert the tags at these positions, Im sure you'll figure it out (but you could use something like substr_replace). I hope this has given you some ideas anyway.
Answer
Solution:
Here is a fairly simple way to implement what you're requesting.
Firstly, you haven't stated what transformations you're performing on the search input, but I would guess that you're splitting the words up and doing a case insensitive search. I would therefore create a data structure containing the original search string and a parsed version with the words split up and lowercased:
// $input is your sanitised query
$arr = explode(" ", strtolower($input));
$search_arr = [
'original' => $input,
'parsed' => $arr
];
Now, dealing with the results from the database: let's call$text
the result from the DB.
# strip the html tags
$stripped = strip_tags($text);
# first, see if the original search query is in the page
$pos = stripos($stripped, $search_arr['original']);
if ($pos !== false) {
# if it is, take a 200 character snippet of the page (note that
# if the search string occurs earlier than the first 50 characters,
# we just take the first 200 characters of the page [I used 50 rather
# than 100 as 100 seemed too many]):
if ($pos < 50) {
$stripped = substr($stripped, 0, 200);
}
else {
$stripped = substr($stripped, $pos-50, 200);
}
# use a regular expression to enclose the search string in a <strong> tag
$stripped = preg_replace("/{$search_arr['original']}/i","<strong>$1</strong>", $stripped);
}
else {
# otherwise, for each word in the parsed version of the search query...
foreach ($search_arr['parsed'] as $s) {
# surround it with <> and </> (I'm doing this in case part of the query
# matches within the <strong> tag - of course, if <> and </> appear in
# the source text, this could be a problem!)
$stripped = preg_replace("/($s)/i", "<>$1</>", $stripped);
}
# now replace the <> and </> with strong tags
$find = [ '<>', '</>'];
$replace = ['<strong>', '</strong>'];
$stripped = str_replace($find, $replace, $stripped);
# find the first <strong> tag...
$pos = strpos($stripped, "<strong>");
if ($pos < 50) {
$stripped = substr($stripped, 0, 200);
}
else {
$stripped = substr($stripped, $pos-50, 200);
}
}
echo $stripped;
This is fairly rough and you'll probably want to refine things, but it should give you an idea of how to proceed.
Share solution ↓
Additional Information:
Link To Answer People are also looking for solutions of the problem: cannot access offset of type string on string
Didn't find the answer?
Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.
Similar questions
Find the answer in similar questions on our website.
Write quick answer
Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.