Segítőkész kereső zavaró újraposztolás nélkül

Segítőkész kereső zavaró újraposztolás nélkül

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.

Segítőkész kereső zavaró újraposztolás nélkül
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;
}

Segítőkész kereső zavaró újraposztolás nélkül

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.

Segítőkész kereső zavaró újraposztolás nélkül
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.

Segítőkész kereső zavaró újraposztolás nélkül
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.

Demo letöltése

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.

Oszd meg barátaiddal is!

Facebook Twitter Linkedin

Elérhetőségeink

  • Címünk: 1139 Budapest, Frangepán utca 3. (1. emelet)

  • Ügyfélfogadás, beiratkozás: Hétfőtől - péntekig: 08:00-15:00

  • Telefonszámunk: 06 70 604 2060, vagy 06 1 4500 110

  • E-mail címünk:

Közösségünk