Segítőkész kereső zavaró újraposztolás nélkül
Sok weboldalon visszatérő probléma, hogy egy keresést követően szeretnénk visszatérni az azt megelőző oldalra, a böngésző egy figyelmeztető ablakban felkínálja, hogy szeretnénk-e újra elküldeni az űrlap adatait. A kérdés bár biztonsági okokból merül fel, de a létezése igen bosszantó a felhasználók számára. Érthető, hogy nem szeretnénk ezzel a figyelmeztetéssel farkas szemet nézni, hiszen megakasztja a böngészésünket. Rosszabb esetben az idegességünk még pánikba eséssel is társul és a laikusabb felhasználók még a weboldalt is elhagyják emiatt.
A figyelmeztetés
De mit lehet tenni?
A megoldás itt sem lehetetlen. Én a keresés feldolgozását egy külön aloldalon végzem el, ez most a példánkban a "result.php" lesz. A "result.php"-ban először is elvégzünk minden szükséges szanitálást a biztonságos bejelentkezés című blog bejegyzésemben már bemutatott módon. Ennek a szerepe, hogy felhasználók akár tudatosan, akár véletlenül ne tudjanak olyan parancsot futtatni a szerverünkön, amellyel kárt tudnának okozni a weboldalunk működésében, vagy a tárolt adataink biztonságában.
Amikor már biztosak vagyunk abban, hogy az elküldött keresőkifejezés biztonságos, akkor ezt a kereső kifejezést elmentjük egy munkamenet változóba. Ezt követően pedig újratöltjük a keresés végeredményét listázó aloldalt. Erre a célra a demoban található "functions.php"-ban található egy "get_url_root" nevű függvény, aminek az a feladata, hogy egészen a kommunikációs protokolltól kezdve felépítse a teljes url sémát. Ezzel biztosak lehetünk abban, hogy az átirányítás mindig a megfelelő helyre fog mutatni, bármilyen mappa szerkezetben is legyen maga a feldolgozást végző fájl.
PHP
<?php
function get_http_protocol()
{
if (preg_match("/HTTPS/", $_SERVER['SERVER_PROTOCOL']))
{
$protocol = 'https://';
}
else
{
$protocol = 'http://';
}
return $protocol;
}
function get_url_root()
{
$url_root = get_http_protocol().$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url_array = explode('/', $url_root);
$url_slug_count = count($url_array) - 1;
$i = 0;
$url_root = '';
foreach ($url_array as $url_slug)
{
if ($i < $url_slug_count)
{
if ($i > 0)
{
$url_root .= '/';
}
$url_root .= $url_slug;
}
$i++;
}
return $url_root;
}
?>
Amikor az aloldal frissült, megvizsgáljuk, hogy létezik-e a munkamenet változónk. Ha igen, a biztonság kedvéért ezen a változón is elvégezzük a szükséges szűréseket, majd végrehajtjuk a keresést az adatbázisban. A keresésünk pontosságát azzal tudjuk fokozni, hogy ha a kifejezés több szóból áll, akkor a szóközök mentén szétbontjuk a kifejezést. Majd a lekérdezést úgy végezzük el, hogy ezeket a szavakat különállóan helyezzük el az SQL utasításban. A "$search_column_array" tömbben pedig felsorolhatjuk, hogy milyen adatbázis oszlopokat szeretnénk bevonni a keresésbe. Ezeknek a súlyozását szintén a következő leírásban vesszük át. A lekérdezés végeredményét pedig linkekbe ágyazva kilistázzuk.
PHP
<?php
if (filter_input(INPUT_POST, 'search'))
{
$_POST = array_map('clear_tags', $_POST);
$_POST = array_map('clear_sql_injection', $_POST);
if (filter_input(INPUT_POST, 'search_text'))
{
$_SESSION['search_text'] = $_POST['search_text'];
header('HTTP/1.1 301 Moved Permanently');
header('Location: '.get_url_root().'/result.php');
exit;
}
}
elseif (filter_var($_SESSION['search_text']))
{
$_SESSION['search_text'] = clear_tags($_SESSION['search_text']);
$_SESSION['search_text'] = clear_sql_injection($_SESSION['search_text']);
$search_where = '';
$search_column_array = array('username', 'email');
$search_column_count = count($search_column_array);
$search_text_array = explode(' ', $_SESSION['search_text']);
$search_text_count = count($search_text_array);
if ($search_text_count == 1)
{
foreach ($search_column_array as $column)
{
if (filter_var($search_where))
{
$search_where .= " OR ";
}
$search_where .= $column." LIKE '%".$_SESSION['search_text']."%'";
}
}
else
{
$i = 1;
foreach ($search_column_array as $column)
{
$y = 1;
foreach ($search_text_array as $search_slug)
{
if ($y == 1)
{
$search_where .= "(";
}
$search_where .= $column." LIKE '%".$search_slug."%'";
if ($y < $search_text_count)
{
$search_where .= " AND ";
}
else
{
$search_where .= ")";
}
$y++;
}
if ($i < $search_column_count)
{
$search_where .= " OR ";
}
$i++;
}
}
$select = mysqli_query($GLOBALS['connect'], "SELECT * FROM users WHERE ".$search_where." ORDER BY username ASC");
if (mysqli_num_rows($select) > 0)
{
while ($data = mysqli_fetch_assoc($select))
{
echo '<a href="'.get_url_root().'/detail.php?user='.$data['u_id'].'" title="'.$data['username'].'">'.$data['username'].'</a><br/>';
}
}
else
{
echo '<p class="alert alert-danger">Nincs találat...</p>';
}
}
?>
Eredmény a "web mester" keresésre
SELECT * FROM users WHERE (username LIKE '%web%' AND username LIKE '%mester%') OR (email LIKE '%web%' AND email LIKE '%mester%') ORDER BY username ASC
Ezek a linkek tovább irányítanak bennünket egy következő adatlapra. A példa kedvéért most a keresett felhasználó "adatlapjára". Az adatlapot megjelenítő aloldalon szintén a már említett okok miatt elvégezzük a szokásos biztonsági ellenőrzésünket. Majd ha mindent rendben találtunk elvégezzük a keresést. Ebben az esetben viszont, ha nincs találat a keresésre, vagy valamilyen gyanú merül fel a keresésben résztvevő adatokkal kapcsolatban azonnal átirányítjuk a felhasználót a főoldalra. Csak akkor jelenítjük meg az eredményt, ha az biztosan nem jelent ránk nézve veszélyt.
Ezt követően nincs más dolgunk, csak meg kell nyomnunk vagy az egerünkön lévő, vagy a böngészőnkben lévő visszalépés gombot és meg is pillanthatjuk a csodát. Nincs többé zavaró felugró ablak és újra a találati listát látjuk.
Az itt bemutatott megoldást nem csak a keresőknél alkalmazhatjuk, hiszen remekül helyt áll bármilyen űrlap esetén is. Legyen szó akár egy bejelentkező, vagy regisztrációs űrlapról. Teszem hozzá, hogy keresésnél többnyire POST, helyet GET metódust használnak, hogy a találati listát esetlegesen meg tudják másokkal is osztani a látogatók. POST esetén ugyanis a keresési paraméter nem kerül be az URL-be. Pusztán a példa bemutatása érdekében alkalmaztam így.
Hát kell nekünk ennél több? Igen!
Bizony. Ha már eljutottunk idáig, akkor bemutatok még egy kényelmünket szolgáló lehetőséget. Méghozzá a Google keresőjénél már látott elő találati lista megjelenítését az űrlap beviteli mezője alatt.
Ehhez szükségünk lesz egy jQuery függvénykönyvtárra és az alábbi AJAX hívásra.
JavaScript
<script type="text/javascript">
$(document).ready(function() {
var searchTimer;
$('#search_text').on('keyup', function(event) {
var result_id = $(this).attr('id');
clearTimeout(searchTimer);
if (event.which != '13') {
var input_value = $(this).val();
if (input_value.length > 2) {
searchTimer = setTimeout(function() {
$.ajax({
url: 'ajax_search.php',
type: "POST",
data: ({
action: 'search',
search_text: input_value
}),
success: function (data) {
if (data != '') {
$('#'+result_id+'_result').html(data);
$('#'+result_id+'_result').fadeIn(500);
}
}
})
}, 1000);
}
}
});
$(document).on('click', function(event) {
if ($('#search_text_result').css('display') == 'block') {
$('#search_text_result').fadeOut(500);
}
});
});
</script>
Ez a JavaScript kód, annyit csinál, hogy figyeli, mit gépelünk be a beviteli mezőbe. Tetszőlegesen szabályozható, hogy mennyit várakozzon, mielőtt elküldené az adatokat az őt háttérben feldolgozó php fájlunknak. Az időzítésre azért van szükség, mert miközben gépel a felhasználó nem lenne érdemes feleslegesen lekérdezéseket futtatni a háttérben. Éppen ezért várunk 1 másodpercet, mielőtt elkezdjük a keresést. Ez idő eltelte után feltételezzük, hogy a felhasználó már befejezte a gépelést. Ennek az eltelt időnek az intervallumát szabadon lehet beállítani, ez csupán a javasolt érték. Mindemellett megvárjuk míg legalább 3 karaktert be nem gépel a felhasználó. Ezen felül pedig kizárjuk az "enter" billentyű lenyomását az ajax hívás indítása előtt. Ezt úgy tehetjük meg, hogy megvizsgáljuk a lenyomott gomb esemény kódját. Az "enter" esetén ez a 13.
A többi eseménykódot itt láthatod:
https://css-tricks.com/snippets/javascript/javascript-keycodes/
PHP
<?php
if (filter_input(INPUT_POST, 'action') == 'search')
{
$_POST = array_map('clear_tags', $_POST);
$_POST = array_map('clear_sql_injection', $_POST);
if ((filter_input(INPUT_POST, 'search_text')) && (strlen($_POST['search_text']) > 2))
{
$search_where = '';
$search_column_array = array('username', 'email');
$search_column_count = count($search_column_array);
$search_text_array = explode(' ', $_POST['search_text']);
$search_text_count = count($search_text_array);
if ($search_text_count == 1)
{
foreach ($search_column_array as $column)
{
if (filter_var($search_where))
{
$search_where .= " OR ";
}
$search_where .= $column." LIKE '%".$_POST['search_text']."%'";
}
}
else
{
$i = 1;
foreach ($search_column_array as $column)
{
$y = 1;
foreach ($search_text_array as $search_slug)
{
if ($y == 1)
{
$search_where .= "(";
}
$search_where .= $column." LIKE '%".$search_slug."%'";
if ($y < $search_text_count)
{
$search_where .= " AND ";
}
else
{
$search_where .= ")";
}
$y++;
}
if ($i < $search_column_count)
{
$search_where .= " OR ";
}
$i++;
}
}
$select = mysqli_query($GLOBALS['connect'], "SELECT * FROM users WHERE ".$search_where." ORDER BY username ASC");
if (mysqli_num_rows($select) > 0)
{
while ($data = mysqli_fetch_assoc($select))
{
echo '<a href="'.get_url_root().'/detail.php?user='.$data['u_id'].'" title="'.$data['username'].'">'.$data['username'].'</a>';
}
}
else
{
echo '<p>Nincs találat...</p>';
}
}
}
?>
CSS
#result_container {
position: relative;
}
#search_text_result {
display: none;
position: absolute;
top: 34px;
left: 0px;
width: 100%;
min-height: 34px;
height: auto;
font-size: 14px;
background-color: #ffffff;
border: 1px solid #cccccc;
border-radius: 4px;
}
#search_text_result a {
display: inline-block;
width: 100%;
padding: 6px 12px;
color: #428bca;
border-bottom: 1px dashed #cccccc;
text-decoration: none;
}
#search_text_result a:last-child {
border-bottom: 0px;
}
#search_text_result a:hover {
color: #428bca;
text-decoration: none;
background-color :#eeeeee;
}
#search_text_result p {
display: inline-block;
width: 100%;
padding: 6px 12px;
margin: 0px;
text-decoration: none;
}
Amint ezek a feltételek teljesültek, és a begépelt adatokat eljuttattuk az ajax kérésünket feldolgozó php fájlnak, az szintén elvégzi a már jól ismert ellenőrzéseket. A hacker, akarom mondani az ördög sosem alszik. Majd a találatokat kilistázzuk egy arra létrehozott tárolóba. Az erre a célra beillesztett tároló elem megjelenését CSS segítségével testreszabhatjuk. A tárolóból való kikattintás esetén pedig elrejtjük magát a tárolót. Ugye mennyivel kényelmesebb most a keresés?
Ha még ez sem lenne elég?
Ha sok az adott keresési kifejezésre a találat, akkor a találati listát kiegészíthetjük még egy lapozóval is. Adott esetben akár az is megoldás lehet, hogy csak a 10 legfrissebb, vagy 10 legnépszerűbb, vagy a 10 legpontosabb találatot listázzuk csak ki, és aláteszünk egy gombot, ami átirányítja a felhasználót a teljes találati listához. Akár még kombinálhatjuk is a megoldást azzal, hogy a találati lista fülekre van osztva, melyben egyszerre szerepel a 10 legfrissebb, 10 legnépszerűbb és a 10 legpontosabb találat.
Példa a Pixeden.com weboldalon a lapozható előkeresési találatok listájára.
Tovább fokozhatjuk a felhasználó kényelmét, ha a találatokon megjelöljük, hol szerepel bennük a keresőszó. Ezt megtehetjük egy aláhúzással, félkövér betűtípussal, vagy akár eltérő háttérszínnel is. Ez már teljes mértékben a fantáziánkra van bízva.
Ha mindezt a keresőt egy webáruházba szeretnénk beépíteni, akkor az előkeresési találatokba elhelyezhetjük még akár a keresett termék miniatűr képét, árát, vagy éppen a készletét is. Gyakorlatilag bármit, amivel kényelmesebbé és hasznosabbá tudjuk tenni a felhasználóink számára a keresést.
Egy általam készült webáruházban alkalmazott kereső. A termékek a példa kedvéért szándékosan vannak megváltoztatva.
Következő bejegyzésemben be fogom mutatni, hogyan kövessük nyomon a kereséseket és tegyünk javaslatokat a felhasználónak hibás keresés esetén.
Leírásaink azon kezdő és haladó programozóknak nyújtanak segítséget, akik már minimális szinten foglalkoztak weboldalkészítéssel. Ha szeretnél jobban elmélyülni a témában, vagy elsajátítani alapokat, még tovább fejlődni, akkor nézz körbe tanfolyam kínálatunkban, ahol a kezdőtől a profi szintig nyújtunk képzéseket a számodra.