Lehet saját handler-t is írni, de most enm tértünk ki erre a lehetőségre. 🙂
Nézzük a hello-world-dbi.cgi programocskánkat majd a beágyazását az apartman “Képek” oldalába.
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Apache2::RequestRec ();
use Apache2::RequestUtil ();
my $r = Apache2::RequestUtil->request;
print "The referer in your HTTP-Header is: ", $ENV{'HTTP_REFERER'}, "\n";
my $dbh = DBI->connect('DBI:mysql:mylike', 'mylike', 'mylike1'
) || die "Could not connect to database: $DBI::errstr";
print "<br>\n";
my $results = $dbh->selectall_hashref('SELECT * FROM mylike', 'id');
foreach my $id (keys %$results) {
print "Value of ID $id is $results->{$id}->{'referer'}\n";
}
$dbh->disconnect();
Használjuk a DBI és az új Apache2-es Request modulokat, majd lekérdezzük a HTTP_REFERER-t illetve egy select-tel belefetchelünk az adatbázisba. 🙂
Figyelem! Nagyon fontos a helyes beállítás az apache config oldalon.
A kis appunkat beágyazzuk mint widget-et az alábbi módon.
Mit is kell tudjon egy tetszőleges oldalba beágyazható like alkalmazás? Tulajdonképpen esetünkben arról van szó, hogy egy tetszőleges oldalon a weblapról egy tetszésnyilvánítást rögzíthessünk illetve kijelezzük a tetszésnyilvánítások számát. Nyilvánvalóan adatbázisra lesz szükségünk a dinamikus működés biztosításához.
I. Először is két részre kell bontsuk a megvalósítást. Van egy számláló-kijelzés funkciónk, azaz hogy hány like volt eddig illetve egy “like” gomb, hogy a számláló értékét növelhessük. Továbbá beágyazhatóvá kell tennünk egy tetszőleges weboldalba a funkciót. Innentől like-widget-ről beszélhetünk (https://hu.wikipedia.org/wiki/Vez%C3%A9rl%C5%91elem).
II. Másodszor is hozzá kell kötnünk a like widget-ünket a megjelenés helyéhez. Ez a funkcionalitás a REFERER HTTP fejléc attribútum adatbázisban letárolásával és vizsgálatával oldható meg. Ez az attribútum a hivatkozott oldal címét tartalmazza (https://hu.wikipedia.org/wiki/Referer). Innen fogjuk tudni, hogy melyik a “hívó” webcím.
III. Harmadszor pedig meg kell vizsgálnunk, hogy a like funkciót felhasználóhoz vagy böngésző példányhoz kössük-e nyilvánvalóan azért, hogy ne lehessen egy személynek többször like-olnia ugyanazon a helyen. Ha felhasználóhoz kötjük, úgy a facebook stílusú megoldást kapjuk. Ez nekünk azonban túl bonyolult volna, így egyszerűen a böngészőben fogunk letárolni egy cookie-t, hogy az adott kliensen már történt egy like-olás azaz egy tetszésnyilvánítás adott webcímre.
A tervezési megfontolások nagyjából ennyit tesznek ki. Még foglalkozhatnánk a színes-szagos külcsínyről is, de azt most elhanyagoljuk. Egyszerűen egy félkövér szám lesz a like-ok száma, alatta pedig sortöréssel egy “like” feliratú gomb. Ha nem “szürke” azaz “disabled”, akkor adott kliens még nem like-olt, azaz lehet like-olni. Ennyi. 🙂
Most már, hogy egy űrlapon keresztül foglalásokat lehet elküldeni, szükségünk lesz egy admin oldalra, ahol az apartman ügyintézője meg tudja tekinteni, illetve jóvá tudja hagyni a foglalásokat.
Hogy mit jelent itt az hogy AJAX JSON? Elég érdekes öszvér elnevezés. 🙂 AJAX-szal normál esetben egy XML adatstruktúrát vinnénk át az aszinkron javascript üzenetben, ami valahogy így nézne ki egy konkrét foglalás (reservation) adataira.
Látható, hogy egy eléggé egyszerű adatszerkezet. A JSON leírása részletesen az alábbi címen érhető el: https://hu.wikipedia.org/wiki/JSON.
És akkor nézzük meg a konkrét implementációt. Először is bővítenünk kell amodels/Reservations_model.phpmodellünket három metódussal.
public function get_reservations() {
$this->db->select()->from('reservations')->order_by('created_at', 'DESC');
$query = $this->db->get();
return $query->result();
}
public function get_reservation($id) {
$query = $this->db->get_where('reservations', array('id' => $id));
return $query->result();
}
public function approve_reservation($id) {
$date = date('Y-m-d H:i:s');
$this->db->set('updated_at', $date);
$this->db->set('approved', 1);
$this->db->where('id', $id);
$this->db->update('reservations');
}
Az első get_reservations() metódus a foglalások keletkezésének idejével csökkenő sorrendben egy listát ad vissza a foglalások adataival.
A második get_reservation($id) metódus egy konkrét foglalás adatait adja vissza.
A harmadik approve_reservation($id) metódus pedig UPDATE-eli, tehát frissíti a megadott ID-jű foglalás rekordunkat a frissítés dátumával és beállítja az jóváhagyott foglalás értékét 1-esre, ami esetünkben a jóváhagyás tényét jelzi adatbázis szinten.
Nézzük a views/admin.phpview-nkat, hogy mi is kerül bele.
Látható, hogy ebbe egy táblázat került a foglalások adataival, melynek minden sora egy-egy foglalást tartalmaz. Erről az iterációról a <?php foreach ($query as $item):?><?php endforeach;?> blokk gondoskodik. Minden sort a foglalás ID-je azonosít. Minden sorhoz kerül egy-egy JavaScript blokk is a megfelelő Approve (Jóváhagy) gomb megfelelő “click” eseményének lekezelésével.
Felhívnám a figyelmet a JSON üzenet dekódolására. Ez a var obj = $.parseJSON(JSON.stringify(response)); sorban történik. Először szükséges sztringgé alakítani a válaszüzenetet, mivel az objektum-ként érkezik. Ez a JSON.stringify metódussal történik. Utána csak egy sima parse-olásra lesz szükségünk. 🙂
Nézzük meg végül a controllers/Apartments.phpcontroller-ünket. Két új metódussal bővül. Az admin() metódusban csak kirajzoljuk az oldalunkat a modell réteg lista SQL SELECT metódusának meghívásával. Az admin_approve_ajax($id) metódusban pedig SQL UPDATE hívás majd egy SQL SELECT hívás történik a modell rétegben, illetve a válasz üzenet JSON becsomagolása.
public function admin() {
$this->load->model('reservations_model');
$data['query'] = $this->reservations_model->get_reservations();
$this->template->set('title', 'Admin');
$this->template->load('menu_layout', 'contents' , 'admin', $data);
}
public function admin_approve_ajax($id) {
$this->load->model('reservations_model');
$this->reservations_model->approve_reservation($id);
$data = $this->reservations_model->get_reservation($id)[0];
header('Content-Type: application/json');
echo json_encode($data);
}
Látható, hogy viszonylag egyszerűen megoldottuk az egész adminisztrátori funkcionalitást az AJAX form vezérlés / adatátvitel és a JSON adatformátum hsználatával. 🙂
Látható, hogy a legfelső, az Approved true, tehát jóváhagyott rendelés – mivel csak angol nyelven implementáltam mindent az egyszerűség kedvéért – melletti Approve gomb már kiszürkült, nem megnyomható újra. Az Updated at – frissítés dátuma – pedig a késői vagy kora hajnali órát jelzi, amikor elkészültem a pilot kódokkal. 🙂
Szeretnénk elérni, hogy a dátum mezőket ki lehessen választani a népszerű módon úgy, hogy “feljön” egy kis naptár egy ún. popup ablakban, ahol rákattintunk a kívánt napra, és a kivlasztott dátum bemásolódik a beviteli mezőnkbe a megadott formátumban.
Ehhez le kell tölenünk a jQuery UI-t a https://jqueryui.com/download/all/ webcímről, majd az assets könyvtárunk megfelelő js, css és css/image könyvtáraiba kell bemásolnunk a megfelelő file-okat.
Ezek után a views/booking_ajax.php view-nkban a <script> részhez az alábbi két új függvényt fűzzük hozzá a $(document).ready(function() rész alá új elemként.
Itt egy sima inicializálásról van szó ID Selector alapján, a megfelelő ISO 8601 formátumban. Már működik is! 🙂
Lehetne hozzá sőt kell is még írni egy belső validációt, ami mégegyszer ellenőrzi a dátumformátumot adatbázisba írás előtt, de most ettől eltekintunk.
Ugyanígy kimaradtak az form serialize aspektusai is az előző fejezet AJAX űrlapküldésénél, továbbá még két apróság. Ugyanis a kettővel ezelőtti fejezetben nem másoljuk be a mező értékeket abban az esetben a form-unkba, ha valami validációs hiba történik, illetve az előző fejezetben nem töröljük a form mezőket (reset), ha sikeres a foglalás.
Apróságnak tűnnek, de ezek különböztetnek meg egy professzionális weblapot egy kezdetlegesebbtől. 🙂
Az AJAX, azaz Asynchronous JavaScript and XML arra szolgál, hogy ne kelljen az egész weblapot újratölteni a form elküldésekor, hanem csak az adatokat, mintegy csendben a “háttérben” elküldeni, majd valami <div> tag-ben megjeleníteni a kívánt felhasználói üzeneteket. Részletesebb leírás az alábbi címen található https://hu.wikipedia.org/wiki/Ajax_(programoz%C3%A1s).
Nem nagyobb feladatra vállalkozunk, minthogy az előbbi foglalás-űrlap elküldésünket aszinkron JavaScript hívásra cseréljük.
Először pár apróság. A views/layouts/menu_layout.php file-ban előre kell vennünk a fejlécbe a JavaScript forrás beillesztéseket, hogy tudjuk használni a jQuery funkcionalitásait.
Aztán át kell “drótoznunk” a menüt pl. a views/photos.php file-ban.
Nézzük mi változott! Minden form mezőnek, az elküldő gombnak és a felhasználói üzenet résznek (#response_message_text és #response_message) egyedi azonosítót, id-attribútumot adtunk, hogy a JavaScript részben #id néven hivatkozhassunk rájuk. Ezt a hivatkozási módszert amúgy css szelektornak hívjuk, ezen belül is ún. ID Selector, részletek itt https://www.w3.org/wiki/CSS3/Selectors.
Látható a <script> tag-ben az ajax hívásunk. Kiolvassuk az űrlapmezők értékeit, majd egy hívást indítunk, mind success (siker), mind error (hiba) esetére megfelelő válaszokkal.
Modellünk nem változik viszont a controller-ünkbe elkél két új metódus. Nézzük a controllers/Apartments.php file-t.
public function booking_ajax() {
$data = array();
$this->template->set('title', $this->lang->line('menu_booking'));
$this->template->load('menu_layout', 'contents', 'booking_ajax', $data);
}
public function book_ajax() {
$this->load->library('form_validation');
$this->form_validation->set_rules('name', 'Name', 'required');
$this->form_validation->set_rules('email','Email','trim|required|valid_email');
$this->form_validation->set_rules('checkin', 'Check-in', 'required');
$this->form_validation->set_rules('checkout', 'Check-out', 'required');
if($this->form_validation->run() == FALSE) {
echo validation_errors();
} else {
$this->load->model('reservations_model');
$this->reservations_model->insert_reservation();
echo "Thanks for booking!";
}
}
A booking_ajax metódus csak betölti a view-nkat.
A book_ajax metódus már összetettebb. Tartalmaz egy ellenőrzést, ami email címnél valid_email-lel is bővül, tehát megvizsgálja a formátumot is és egy trim-mel, ami az esetleges felesleges ún. “trailing space” karaktereket vágja le (üres karakterek az elején és a végén).
Látható, hogy validációs hibák esetén visszatér hibával, ez ki is lesz írva a view-nkban a megfelelő div-nél, illetve ha sikerült, akkor a “Köszönet a foglalásért!” üzenettel válaszol. Ennyi az egész :).
És akkor vessünk egy pillantást egy sikeres foglalásra!
Amint megígértem, ebben az évben folytatom, és remélhetőleg be is fejezem a webprogramozásról szóló online könyvemet. 🙂
Hogy jobban megértsük a webprogramozást, egy kis architektúrális ismertetőt kell tartsak. Általánosságban már szóltam a kliens-szerver felépítésről (https://wlammpp.wordpress.com/2019/10/03/mit-ertunk-webprogramozas-webfejlesztes-alatt). Egy webes alkalmazás a felhasználóval a böngészőn (browser) keresztül “lép interaktív kapcsolatba”. Ez azt jelenti, hogy a felhsználó egy böngésző ablakban illetve tabfülön keresztül látja illetve használja a grafikus felhasználói felületet (GUI – graphical user interface, ide tartozik még a népszerű Ui / UX Design fogalma, vagyis a felhasználói felület és élmény tervezés). A felhasználó az alkalmazás funkcióit a böngészőben megjelenő felületen keresztül éri el: elolvas információkat, regisztrál egy webes űrlapon (formon) keresztül, keres, navigál, “kattint” (click-el). Ezek közül komolyabb programozást az űrlapkitöltés és a keresés igényel, illetve a különböző grafikai látványelemek is.
Az egész webprogramozás illetve -fejlesztés azért tűnhet bonyolultnak, mert egyrészt kellenek a “szerver oldali” dolgok: egy adatbázis – esetünkben relációs -, amit az ún. SQL nyelven lehet manipuláni, lekérdezni, feltölteni, frissíteni illetve törölni (https://hu.wikipedia.org/wiki/SQL), továbbá egy beágyazott, illetve szerver oldalon értelmezett (ún. script) nyelv, ez a PHP 7-es verzió konkrét esetünkben (https://www.php.net/), amely a funkcionalitást, az “üzleti folyamatot” valósítja meg (implementálja). A PHP Hyprtext Preprocessor-nak hirdeti magát, ami azt jelenti, hogy a HTML nyelven (ez a weblapok nyelve) írt hypertext-be van beágyazva, amit a webszerverünk megfelelő modulja értelmez, amikor valaki az adott weboldalt a böngészőjében megnyitja. Tehát nincs előre lefordítva, mint például egy C nyelven írt kernel modul (Linux), hanem “on the fly” fordul (compile), és “fut” (run). Ezt szemléltetném is egy pár soros kódban.
<!DOCTYPE html>
<html>
<head>
<title>Lapcím</title>
</head>
<body>
<h1>Ez egy címsor.</h1>
<p>Ez egy bekezdés.</p
<!-- Ez egy megjegyzés. Ide jön a PHP kódrészlet. -->
<?php
phpinfo();
?>
</body>
</html>
Ebben a rövid kódrészletben (snippet) ún. HTML tag-eket láthatunk. Az eleje egy dokumentum típus megjelölés, majd html, head – fejléc, body – törzs olvashatóak. Látható, hogy <? ?> jelek között van a PHP kódrészlet, ami konkrétan egy hosszú és részletes kiírást eszközöl a szerverünk beállításáról, ha megnézzük böngészőben (írtam már róla itt: https://wlammpp.wordpress.com/2019/10/04/i-resz-wordpress-webfejlesztes-a-wordpress-telepitese). Ezek a tag-ek mind “kacsacsőrök” között vannak. Ez a leíró nyelvek sajátossága. Az XML is ilyen formátumú. 🙂
A HTML oldal önmagában statikus, ha pl. PHP kódrészletet nem tartalmaz. Lehet benne hivatkozás képre, hangra, videóra, tehát Hypertext, ugyanakkor nem igényel szerveroldali processzálást (feldolgozást). Ezért a szerveren statikus oldalként kerül kiszolgálásra. Erre általában az oldalt tartalmazó állomány nevének kiterjesztése utal, ez többnyire .html – PHP oldalak esetében pedig .php. A nagy különbség egy “honlap” és egy “webalkalmazás” között abban rejlik, hogy milyen és mennyi “kód” van mögötte és benne a szerveren, illetve a kliens oldalon JavaScript formájában, értsd: hogy mennyire interaktív. Alábbiakban egy kis összefoglaló a HTML struktúráról, a tag-ekről.
<!-- Nyitó és záró tag, közte a tartalom. -->
<tagnev>Töltelék.</tagnev>
<!-- Önmagában záródó tag (XML helyes), paraméterekkel. -->
<tagnev parameter1="ertek1" parameter2="ertek2" />
<!-- A legegyszerűbb "beágyazás". -->
<tag1>
<tag2 />
</tag1>
Ha ez már bonyolult volna, szólnom kell még a kliens oldalon fontossá váló komponensekről. Ezek az előbbiekben taglalt az oldalak alapvető struktúrájáért és megjelenéséért felelős HTML (Hypertext Markup Language – https://www.w3schools.com/html/), a látványért felelős CSS (https://hu.wikipedia.org/wiki/Cascading_Style_Sheets, https://www.w3schools.com/css/) illetve a dinamikus működésért felelős JavaScript (https://www.w3schools.com/js/) nyelveken implementált böngészőben futó, “héj” réteg. A könyv legelején taglalt MVC architektúra (Model – View – Controller) View – nézet rétegéről szól ez a kliens oldali rész. A dinamikus működést biztosító JavaScript, ha AJAX-ról (https://hu.wikipedia.org/wiki/Ajax_(programoz%C3%A1s)) beszélünk, akkor lehet “okos” Controller – vezérló működésű is, sőt bizonyos szempontból még Model – adatszerkezet érintettségről is beszélhetünk, amennyiben XML-formátumban történik az adatátvitel pl. egy kliens oldali űrlap (form) és a szerver oldali Controller között.
A szerveroldali részek azonban a Model és a Controller rétegbe tartoznak. Technikailag ugyan a View is a szerver oldalon van, ámde csupán a böngészőben válik látvánnyá. Ha jól implementáltuk (programoztuk) az alkalmazást, az architektúrális elveknek megfelelően, akkor elválnak egymástól a rétegek. Ettől áttekinthető és karbantartható marad a kódunk. Sokkal kisebb a hibalehetőség, illetve egyszerűbb továbbfejleszteni, módosítani és dokumentálni. Hogy ezt az architektúrális szétválasztást tényleg meg is tehessük, segítségül hívtuk a CodeIgniter (https://codeigniter.com/) keretrendszert (framework-öt).
A CodeIgniter-t (3-as verzió) egész egyszerűen letöltjük (https://codeigniter.com/download), és kicsomagoljuk a webszerverünk gyökér könyvtára alatt létrehozott “apartman” könyvtárba.
Ezek után megnézhetjük a “helyi” webszerverünket, hogy sikerült-e a telepítés. Írjuk be a kedvenc böngészőnkbe (én a Google Chrome-ot használom) a következő címet: http://localhost/apartman/. 🙂
Siker! 🙂 Jól látható, hogy vagy 5 nyelven kell tudnunk kódolni egy átlagos webalkalmazáshoz. 😀 Megjegyzem, hogy ez az esettanulmány ugyan konkrét példát mutat be, azonban általános érvényű az architektúrában és főbb irányelveiben.