0

First of all, I'm new to development so need a little hand holding.

I've built a simple search field (with suggestions) and send users to a relevant landing page on an array match. I return an error message if the search isn't relevant. However, my code relies on the user clicking a suggestion or typing the whole word in the search field. Therefore, the usability is poor.

The code below will highlight my problem. Ideally, I need to match a field input 'br' to 'bristol' (as per my suggestion to the user). I think the solution is http://php.net/manual/en/function.levenshtein.php but I'm having problems implementing (like I said, I'm new). I would appreciate any guidance.

Thanks heaps for your time!

<?php

$counties = array("Avon",
"Bristol",
"Bedfordshire",
"Berkshire",
"Buckinghamshire"
);

if (in_array(strtolower($_GET["my_input"]), array_map('strtolower', $counties)))

{ header('Location: http://domain.co.uk/county/'.strtolower(str_replace(' ', '-', $_GET['my_input'])));
}

else

{
header('Location: ' . $_SERVER['HTTP_REFERER'] . "?message=tryagain");
exit;
}

?>
Ben F
  • 75
  • 8

4 Answers4

0

This should get you closer (if not) to your goal:

if(array_key_exists('my_input', $_GET){

   $input = $_GET["my_input"];
   $counties = array("Avon", "Bristol", "Bedfordshire", "Berkshire",  "Buckinghamshire");

   // no shortest distance found, yet
   $shortest = -1;

   // loop through words to find the closest
   foreach ($counties as $country) {

      $lev = levenshtein($input, $country);

      // check for an exact match
      if ($lev == 0) {
          $closest = $country;
          $shortest = 0;
          // break out of the loop; we've found an exact match
          break;
     }

       // if this distance is less than the next found shortest
       // distance, OR if a next shortest word has not yet been found
       if ($lev <= $shortest || $shortest < 0) {
           // set the closest match, and shortest distance
           $closest  = $country;
           $shortest = $lev;
       }
   }

    if($shortest < 5){ //insert your own value here ( accuracy )
        header('Location: http://domain.co.uk/county/'.strtolower($closest));
    }else{    
        // if not match
        header('Location: ' . $_SERVER['HTTP_REFERER'] . "?message=tryagain");
    }

}
MistaJase
  • 839
  • 7
  • 12
0

Could be optimized but in general:

foreach(array_map('strtolower', $counties) as $county) {
    if(substr($county, 0, strlen($_GET["my_input"])) == strtolower($_GET["my_input"])) {
        header('Location: http://domain.co.uk/county/'.str_replace(' ', '-', $county));
        exit;
    }
}
header('Location: ' . $_SERVER['HTTP_REFERER'] . "?message=tryagain");
exit;
  • Loop the $counties and compare the $_GET["my_input"] to the same number of beginning characters in $county
  • Make sure to use $county in the header and not $_GET["my_input"]

Obviously if the user enters Be then Bedfordshire being the first entry will be chosen. So long as your "suggestions" are in the same order it should work fine.

In case the user enters a trailing space or something, maybe trim():

if(substr($county, 0, strlen(trim($_GET["my_input"]))) == strtolower(trim($_GET["my_input"]))) {
AbraCadaver
  • 78,200
  • 7
  • 66
  • 87
  • Works perfect! The trim element appeared to break the code but I'll look into that further. Users are so used to typing a few characters that my code was falling down half the time and I was losing users. But this has done the trick. Thank you! – Ben F Jun 13 '16 at 21:52
0

you could use a startsWith function from this answer and iterate over your counties setting a flag if a match is found.

<?php

function startsWith($haystack, $needle) {
    // search backwards starting from haystack length characters from the end
    return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
}

$counties = array("Avon",
"Bristol",
"Bedfordshire",
"Berkshire",
"Buckinghamshire"
);

foreach (array_map('strtolower', $counties) as $county){
  if(startsWith($county, strtolower($_GET["my_input"]))){
    header('Location: http://domain.co.uk/county/'.strtolower(str_replace(' ', '-', $_GET['my_input'])));
    $match = true;
    break;
  }
}

if(empty($match))
  header('Location: ' . $_SERVER['HTTP_REFERER'] . "?message=tryagain");
Community
  • 1
  • 1
Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
0

Implementing levenshtein for your code would look something like:

$counties = array(
    "Avon",
    "Bristol",
    "Bedfordshire",
    "Berkshire",
    "Buckinghamshire"
);

$input = 'Bed';
$shortest = -1;

foreach($counties as $county) {

    $lev = levenshtein($input, $county);
    var_dump($county, $lev);

    if ($lev == 0) {
        $closest = $county;
        break;
    }

    if ($lev <= $shortest || $shortest < 0) {
        $closest  = $county;
        $shortest = $lev;
    }

}

echo $closest;

But I don't think this will do exactly what you're looking for because technically Avon is closer to Bed than Bedfordshire using this algorithm.

There is a similar_text function in PHP which might give you better results.

$counties = array(
    "Avon",
    "Bristol",
    "Bedfordshire",
    "Berkshire",
    "Buckinghamshire"
);

$input = 'Bed';
$mostSimilar = 0;

foreach($counties as $county) {

    similar_text($input, $county, $similarity);

    if ($similarity == 100) {
        $closest = $county;
        break;
    }

    if ($mostSimilar <= $similarity || $similarity < 0) {
        $closest  = $county;
        $mostSimilar = $similarity;
    }

}

echo $closest;
James
  • 565
  • 2
  • 7
  • 17