AJAX a knižnica článkov s fulltextovým vyhľadávaním – práca s článkami
V tomto článku bude uvedený popis súborov, ktoré umožňujú zobrazenie článkov podľa parametrov nastavených vo vstupnom formulári v úvodnej stránke.
Súbor clanky.class.php
K zlepšeniu funkcionality systému bola vytvorená trieda Clanky
. V triede Clanky
sú atribúty polí (premenné) typu public
a private
, metódy (funkcie), konštruktor a deštruktor. Trieda Clanky
slúži ostatným súborom na vytváranie a spracovanie výsledkov databázy. Oddelenie vytvárania vrstiev funkcionality aplikácie umožňuje flexibilné a rozšíriteľné aplikácie.
<?php
// zavedie súbor pre ošetrenie chýb a konfiguračný súbor
require_once(‚error.php‘);
require_once(‚config.php‘);
// definuje triedu Clanky
class Clanky {
// premenná pre požiadavku SQL
public $mQuery;
// premenná určujúca stĺpce pre match
public $mCol;
// ovládač dátabaze
private $mMysqli;
// konštruktor triedy
function __construct() {
$this->mMysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE);
}
// deštruktor triedy
function __destruct() {
if ($this->mMysqli) $this->mMysqli->close();
}
// funkcia pre spracovanie fulltextového vyhľadávania
public function createFullText($query, $col,$whole) {
// zakódovanie dát pre ich bezpečné použitie v príkazoch SQL
$query = $this->mMysqli->real_escape_string(trim($query));
$col = $this->mMysqli->real_escape_string($col);
$whole = $this->mMysqli->real_escape_string($whole);
// zadefinovanie stĺpcov pre match vo fulltextovom vyhľadávaní
$this->mCol = $col == ‚autori‘ ? $col : $col.‘,nazov‘;
// zostavenie požiadavky pre SQL
switch ($col){
case ‚autori‘:
$query = ereg_replace(quotemeta(„+|-|*|~|\\|\“|<|>|(|)“),““,$query);
$this->mQuery = ‚“‚.$query.'“‚;
break;
case ‚abstrakt‘:
$words = explode(‚ ‚,$query);
for ($i=0;$i<count($words);$i++) if (Strlen($words[$i])>3 && !$whole) $words[$i] .= ‚*‘;
$this->mQuery = implode(‚ ‚,$words);
break;
}
}
// vráti počet článkov
public function getNumRows ($query) {
if ($result = $this->getQuery($query)) {
$numRows = $result->num_rows;
$result->close();
}
return $numRows;
}
// vráti celkový počet článkov pri SELECT COUNT(*)
public function getNumAllRows ($query) {
if ($result = $this->getQuery($query)) {
$row = $result->fetch_row();
$result->close();
}
return $row[0];
}
// vráti obsah riadku
public function getRow ($query) {
if ($result = $this->getQuery($query)) {
$row = $result->fetch_array(MYSQLI_ASSOC);
$result->close();
}
return $row;
}
// vráti príkaz SQL
public function getQuery($query) {
return $this->mMysqli->query($query);
}
}
}
?>
Pri tvorbe databázy používame objektove orientovanú knižnicu mysqli, ktorá bola implementovaná v PHP 5. Pomocou konštruktora __construct
sa vytvára inštancia triedy a automaticky vytvára spojenie na databázu. Deštruktor __destruct
sa volá po zániku objektu a tým sa uzatvára databázové spojenie. Pre vysvetlenie činnosti fulltextového vyhľadávania odporúčame preštudovať si článok Erika Hoffmanna Fulltextové vyhľadávanie v MySQL – prax.
Súbor clanky-top.php
Súbor clanky-top.php sa načíta v súbore clanky.php a pomocou globálnych premenných $_POST
, respektíve $_GET
, nastavuje $_SESSION
. Následne inicializuje premenné $_question
, $_col
, $_whole
, $_lim
a $_skip
použité v súbore clanky.php.
<?php
// zavedie súbor pre ošetrenie chýb a pre triedu Clanky
require_once(‚error.php‘);
require_once(‚clanky.class.php‘);
// štart session
session_start();
// nastaví session podľa $_POST a $_GET
if (isset($_POST[‚q‘])) {
$_SESSION[‚values‘][‚s_query‘] = $_POST[‚q‘];
if (isset($_POST[‚whole‘])) $_SESSION[‚values‘][‚s_whole‘] = ‚yes‘; else $_SESSION[‚values‘][‚s_whole‘] = “;
if (isset($_POST[‚col‘])) $_SESSION[‚values‘][‚s_col‘] = $_POST[‚col‘];
}
if (isset($_GET[‚skip‘])) $_SESSION[‚values‘][‚s_skip‘] = sprintf(„%d“, $_GET[‚skip‘]);
// nastavi premenne pre subor clanky.php
$_query = $_SESSION[‚values‘][‚s_query‘]; $_col = $_SESSION[‚values‘][‚s_col‘];
$_skip = $_SESSION[‚values‘][‚s_skip‘]; $num = $_SESSION[‚values‘][‚s_num‘];
$_whole = $_SESSION[‚values‘][‚s_whole‘]; $lim = ROWS_PER_VIEW;
?>
Súbor clanky.php
Súbor clanky.php je vyvolaný klasicky kliknutím na button „Články“ vo vstupnom formulári úvodnej stránky. Metódou POST sú odovzdané parametre formulára a zobrazia sa nájdené články v databáze. Počet zobrazených článkov je nastavený v súbore config.php. V prípade väčšieho počtu článkov ako je nastavený počet zobrazených článkov, môžeme pomocou príkazov v spodnom riadku zobrazovať nasledujúce stránky s článkami. Súčasne s XHTML kódom stránky sa načítavajú súbory clanky.css a abstrakt.js.
<?php
// zavedie súbory pre ošetrenie chyb a clanky_top.php.
require_once(‚error.php‘);
require_once(‚clanky_top.php‘);
echo ‚<?xml version=“1.0″ encoding=“utf-8″ ?>‘.“\n“;
?>
<!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Strict//EN“ „http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd“>
<html xmlns=“http://www.w3.org/1999/xhtml“ xml:lang=“sk“ lang=“sk“>
<head>
<meta http-equiv=“Content-Type“ content=“text/html; charset=utf-8″ />
<meta http-equiv=“Content-Language“ content=“sk“/>
<title>Knižnica–Články</title>
<link rel=“stylesheet“ type=“text/css“ href=“clanky.css“ />
<script type=“text/javascript“ src=“request.js“></script>
<script type=“text/javascript“ src=“abstrakt.js“></script>
</head>
<body>
<h1>Knižnica článkov</h1>
<div id=“wrap“>
<h2>Články</h2>
<?php
// vytvorí inštanciu triedy Clanky
$_sql = new Clanky();
// spracuje premenné pre fulltextové vyhľadávanie
$_sql->createFullText($_query, $_col, $_whole);
$query = „SELECT *“;
// vytovorí SQL pre zobrazenie všetkých článkov
if ($_query == ‚?‘) {
$num = $_SESSION[‚values‘][‚s_count‘];
$query .= “ FROM clanky“;
}
// vytvorí SQL pre zobrazenie článkov pre fulltextové vyhľadávanie, min. tri znaky
elseif (StrLen(trim($_query)) > 3) {
if ($_col != ‚autori‘) $query .= „,MATCH($_sql->mCol) AGAINST(‚$_sql->mQuery‘ IN BOOLEAN MODE) AS vaha“;
$query .= “ FROM clanky WHERE MATCH($_sql->mCol) AGAINST(‚$_sql->mQuery‘ IN BOOLEAN MODE)“;
// poradie pri fulltextovom vyhľadávaní je určené váhou
if ($_col != ‚autori‘) $query .= “ ORDER BY vaha DESC“; else $query .= “ ORDER BY nazov ASC“;
// zobrazenie mena autora alebo kľúčových slov
if ($_col != ‚autori‘) echo ‚<p>Kľúčové slová: ‚; else echo ‚Autor: ‚;
echo ‚<i>‘.htmlspecialchars($_query).'</i></p>‘;
// ak počet riadov nebol načítaní v JS
if ($num == -1) $num = $_sql->getNumRows($query);
// ošetrenie chýb
if ($num == 0) echo ‚<p>Ľutujeme, ale nenašiel sa žiaden článok!</p>‘;
}
else echo ‚<p>Ľutujeme, ale nezadali ste meno autora alebo kľúčové slová (minimálne štyri znaky)!</p>‘;
if ($num > 0) {
echo ‚<p id=“top“>Počet článkov: <b>‘.$num.'</b>‘;
if (trim($_query) != ‚?‘) echo ‚ z ‚.$_SESSION[‚values‘][‚s_count‘];
if ($num > $lim) echo ‚ <span>Stránka: <b>‘.sprintf(„%d“,($_skip/$lim+1)).'</b> (‚.sprintf(„%d“, ($num-1)/$lim+1).‘)</span>‘;
echo ‚</p> <ul>‘;
$query .= “ LIMIT $_skip, $lim“;
// vykoná SQL požiadavku
if ($result = $_sql->getQuery($query)) {
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
// tvorba zoznamu článkov
echo ‚<li><span><a href=“abstrakt.php?id=‘.$row[„ID“].'“ title=“Abstrakt-html“><img src=“icon_html.png“ alt=“Abstrakt-html“ /></a><a href=“‚.$row[„clanok“].'“ title=“Článok-pdf“><img src=“icon_pdf.png“ alt=“Článok-pdf“ /></a></span>‘.$row[„autori“].‘: <em>‘.$row[„nazov“].‘, </em>‘.$row[„kde“].'<div /></li>‘;
echo ‚</ul>‘.“\n“;
$result->close();
}
}
else echo ‚<p>Ľutujeme, ale sa nepodarilo pripojiť na server, pokúste sa opäť!</p>‘;
// tvorba foot
echo ‚<p class=“foot“>‘;
if ($num>$_skip && $_skip>=$lim) echo ‚<a href=“clanky.php?skip=‘.($_skip – $lim).'“>predchádzajúca stránka</a>‘;
echo ‚ <a href=“./“>úvodná stránka</a> ‚;
if ($num>$_skip+$lim) echo ‚<a href=“clanky.php?skip=‘.($_skip + $lim).'“>nasledujúca stránka</a>‘;
echo ‚</p>‘.“\n“;
?>
</div>
</body>
</html>
Súbor abstrakt.js
Pomocou JavaScriptu a objektu XMLHttpRequest môžeme načítať – bez opätovného načítania stránky – obsah vybraného abstraktu kliknutím na príslušnú ikonku. Súbor abstrakt.js spolupracuje so súborom abstrakt.php. V prípade asynchrónneho spojenia okrem parametra id
článku posielame metódou GET aj parameter xml=1
.
//spustí po načítaní stránky
window.onload = function() {
// vytvorí odkazy na <li>
var lis = document.getElementsByTagName(‚li‘);
// pri stlačení ikonky abstraktu definuje odkaz
for (var i=0; i<lis.length; i++) {
lis[i].getElementsByTagName(‚a‘)[0].onclick = function() {
// vytvorí inštanciu objektu XMLHttpRequest
var request = requestObj ();
// vytvorí odkaz na element <div>
var div = this.parentNode.parentNode.lastChild;
// pri kliknutí sa obsah abstraktu schová
div.onclick = function () {this.innerHTML = „“; this.className = „“;}
// ak je objekt XMLHttpRequest
if(request) {
// metóda pre spracovanie odpovede zo servera
request.onreadystatechange = function() {
// ak prebehlo všetko v poriadku
if (request.readyState == 4) {
if (request.status == 200) {
try {
// extrahuj XML zo servera
var rootXML = request.responseXML.documentElement;
// ošetrenie chyby pre Firefox
if (rootXML.nodeName == „parsererror“) throw(„“);
// načítanie dát do elementu <div>
else div.innerHTML = rootXML.firstChild.data;
}
// sprava pre uzivatela v pripade chyby servera
catch (e) {div.innerHTML = „Ľutujeme, nepodarilo sa pripojiť na server, pokúste sa opäť!“;}
} else {div.innerHTML = „Ľutujeme, nepodaril sa prenos zo servera, pokúste sa opäť!“}
}
}
// zobraz obsah abstraktu
div.className = „show“;
// správa pre prípad pomalého Internetu
if (div.innerHTML == „“) div.innerHTML = „<b>Abstrakt</b> sa načítáva…“;
// na serveri spusti stránku abstrakt.php s parametrami id a xml
request.open(„GET“, this.href+“&xml=1″, true);
// pošle žiadosľ na server
request.send(null);
}
// v prípade neúspechu vráti true a vykoná sa pôvodný odkaz v HTML
else {return true;}
// v prípade úspešného načítania abstraktu vráti false
return false;
}
}
}
Každý článok používa vlastný objekt XMLHttpRequest. Toto najjednoduchšie riešenie zabezpečuje, že nedôjde k strate požiadavky na server. Pri spracovaní odozvy sa postupuje štandardným spôsobom. Chyby sa vychytávajú pomocou metódy try/catch
v prehliadačoch MSIE a Opera. Firefox generuje hlásenie o chybe v koreni súboru XML, ktorý nazýva „parsererror“. V prípade Firefoxu vygeneruje sa pomocou throw
výnimka, ktorá sa zachytí ako u ostatných prehliadačoch v catch
. V prípade korektnej odozvy servera sa obsah abstraktu načíta do kontajnera div
pomocou innerHTML
. V prípade pomalého pripojenia k internetu sa objavuje správa, že sa súbor načítáva.
Súbor abstrakt.php
Súbor abstrakt.php v úvode načíta súbor clanky.class.php pre načítanie obsahu abstraktu z databázy. Súbor spracúva požiadavky generované JavaScriptom. V prípade, ak prehliadač nemá implementovaný objekt XMLHttpRequest alebo je zakázaný JavaScript, súbor abstrakt.php je volaný odkazom priamo zo stránky clanky.php. V prvom prípade je generovaný súbor typu XML, v druhom prípade súbor typu XHTML, ktorý je zobrazený samostatne v okne prehliadača.
<?php
// zavedie súbory pre ošetrenie chýb a triedy Clanky
require_once(‚error.php‘);
require_once (‚clanky.class.php‘);
// štart session
session_start();
// nastavenie premených podľa $_GET
if (isset($_GET[‚id‘])) $user_id = $_GET[‚id‘]; else $user_id = 0;
if (isset($_GET[‚xml‘])) $user_xml = $_GET[‚xml‘]; else $user_xml = 0;
// vytvorí inštanciu triedy Clanky
$_sql = new Clanky();
// tvorba SQL príkazu
$query = sprintf(„SELECT autori,nazov,abstrakt FROM clanky WHERE ID = %d“, $user_id);
// vykoná prikaz SQL
$row = $_sql->getRow ($query);
// ak požiadavka je cez AJAX, tvorba XML súboru
if ($user_xml==1) {
header(‚content-type: text/xml‘);
echo ‚<?xml version=“1.0″ encoding=“utf-8″ ?><abstrakt><![CDATA[‚.$row[„abstrakt“].‘]]></abstrakt>‘;
}
// inak tvorba XHTML súboru
else {
// . . . . . .
// generuje sa XHTML abstraktu
// . . . . . . . . . .
}
?>
V prípade vytvorenia súboru XHTML sa súčasne načíta aj abstrakt.css, ktorý určuje vzhľad stránky.
Súbor o_kniznici.html
Klasický statický hypertextový súbor slúži ako zdroj informácii o použití knižnice. Súčasne sa načíta aj súbor o_kniznici.css, ktorý určuje vzhľad stránky.
Záver
Klasické statické stránky sa stávajú minulosťou. V súčasnosti sa dostávajú do popredia interaktívne stránky, využívajúce PHP script na strane servera a JavaScript spojený s objektom XMLHttpRequest na strane klienta. Internetové aplikácie sa svojím správaním približujú desktopovým aplikáciám, ale ich realizácia býva spravidla jednoduchšia. Klasické statické stránky sa používajú najmä ako informačné stránky.