Delphi - Internet áruház készítése

Internet áruház 7. rész

forráskód letöltése
Internetes áruházunk készítésének e heti részében működőképessé tesszük a kosarat. Ettől kezdve a felhasználó már tetszőleges termékeket válogathat össze, melyeket meg kíván rendelni tőlünk. A mellékelt példaprogram megnyitása előtt a következő teendőket kell elvégezni, hogy a példa működőképes legyen:
  • A HTMLProducer.pas-ban lévő komponenst telepítenie kell a Delphi alá. (Ezt a komponenst a múlt heti cikknél hoztuk létre, forráskódja ott található!)
  • Kérjük, hozza létre az ASWEBSHOP07 alias-t. Ez mutasson a mellékelt példaprogram Data könyvtárára, az adatbázis elérés végett.
  • A programban talál egy HTMDIR konstanst. Ez szintén a Data könyvtár elérési útvonalát kell hogy tartalmazza, ha Ön ettől eltérő helyre másolta a példát, akkor fordítás előtt kérjük módosítsa ezt.
  • A lefordított WS07.EXE-t helyezze a web szerver scripts könyvtárába, ahonnan az futtatható.
  • A mellékelt HTM könyvtár tartalmát pedig helyezze el a web szerveren úgy, hogy az egy böngészőn keresztül elérhető legyen. Például: C:\Inetpub\wwwroot\webshop\07\. Ekkor a böngészőben a www.animare.hu/webshop/07/ cím beírásakor meg kell hogy jelenjen a kezdő oldal. Persze a web szerver neve mindenkinél más és más lesz. A mellékelt példában a webshop\07\ könyvtárra több hivatkozás is van az egyes web lapokon, így kérjük ettől Ön se térjen el.

A jelenlegi cikk elolvasása előtt javasoljuk a sorozat előző részeinek megismerését, mivel a mostani ismeretek csupán kiegészítői az előzőeknek, így azok ismerete nélkül e konkrét cikk tartalma sem érthető meg teljes egészében.


Ebben részben jó példa lesz a múltkori THTMLProducer komponens felhasználására, hogy miért is kellett ennyire megbonyolítanunk a komponensben lévő feldolgozó algoritmust. Alábbiakban majd láthatjuk, hogy ennek révén most igen gyorsan tudjuk a programunkat továbbfejleszteni.

A kosárba helyezett termékeket a web lap jobb oldalán kell kilistáznunk. A jelenlegi példaprogram csak annyit tűz ki feladatul, hogy a kosárba lehessen helyezni az egyes termékeket. Későbbiek folyamán még meg kell majd oldanunk, hogy ne csak egyesével lehessen egy-egy terméket berakni, hanem akár egyszerre tetszőleges darabszámban lehessen vásárolni az egyes termékekből. További feladat marad még, hogy a kosárból kivenni is lehessen terméket a rendelés előtt, és ne csak berakni.

Hogy a kosárnak mi az aktuális tartalma erről nem ártana tudnia a programunknak. Mivel a felhasználó egyesével rakja bele a termékeket, így állandóan látnia is kell az aktuális állapotot. Ráadásul az áruháznak egyidejűleg több vevőt is kell szolgálnia. További probléma, hogy jelenleg még nem is tudjuk azonosítani a vevőket, így a sok-sok kosár tartalma nehezen kezelhető külön-külön. Így nem lenne egyszerű megoldás, hogy létrehozunk egy adatbázist, melyben tárolnánk az egyes vevők aktuális kosarának tartalmát. Ez azért sem jó, mert ha a felhasználó egyszerűen elhagyja web oldalainkat, akkor marad egy telerakott kosarunk, melyet már soha senki nem fog megrendelni, törölni, stb.

Ezek megoldására az alábbit találtuk ki: minden felhasználó számára egy olyan web lapot szolgáltat a programunk, melyben eltároljuk a kosár aktuális tartalmát. Így azt nyugodtan bővítheti, törölhet belőle. Megrendelés esetén pedig rendelkezésünkre fog állni az összes kosárba helyezett termék.

Ennek tényleges megvalósítása az alábbiak szerint történik: amikor a web lapunkon hivatkozunk a ws07.exe-re, akkor itt tetszőleges paramétereket is megadhatunk. Mivel minden terméknek a Product.dbf táblában egyedi azonosítót adtunk, így itt paraméterként felsorolhatjuk az éppen a kosárban lévő termékeket.

Például a második terméket az alábbiak szerint jelöljük:
http://www.animare.hu/scripts/ws07.exe?b0=2

A b0 a paraméter neve, a 2-es szám pedig a Product.dbf tábla ID mezőjének értéke. Ezek után már minden adatot kiolvashatunk az adatbázisból, amikor a web lapot készítjük.

Ha több termék van a kosárban, akkor ezeket egymás után felsoroljuk majd:
http://www.animare.hu/scripts/ws07.exe?b0=2&b1=5&b2=12

Amint az meg is figyelhető, a paraméterek elnevezésében lévő szám egymásután növekszik egyesével, hogy minden egyes paraméternek egyedi elnevezése legyen.

Nézzük most a programot, hogy ezt miként is valósíthatjuk meg.

Amikor a programunk alapértelmezett akciója fut, ott kell ellenőriznünk, hogy kaptunk-e olyan paramétereket, melyek a kosár tartalmára hivatkoznak. Ehhez létrehoztunk egy GetBox nevű eljárást, melyet itt meghívunk.
procedure TWebModule1.WebModule1WebActionItem1Action
  (Sender: TObject; Request: TWebRequest; Response: TWebResponse; 
   var Handled: Boolean);
begin
  try
    GetBox;
    Response.Content:=HTMLProducer1.Content(HTMDIR+'main.htm');
  except
    on e: exception do Response.Content:=e.message;
  end;
end;

Ez a GetBox nevű eljárás végignézi a paramétereket egyesével és ha olyat talál, mely kis b betűvel kezdődik, akkor a paraméterhez tartozó számot, vagyis a termék kódját eltárolja egy globálisan deklarált FBoxList nevű TStringList típusú változóba.

Egyúttal egy másik globális változónak is értéket adunk. Ez az FBoxLink nevű sztring típusú változó lesz. A későbbiekben még visszatérünk pontos szerepére.
procedure TWebModule1.GetBox;
var
  i: integer;
begin
  with Request.QueryFields do begin
    FBoxList.Clear;
    FBoxLink:='';
    for i:=0 to Count-1 do begin
      if Names[i][1]='b' then begin
        FBoxList.Add(Values[Names[i]]);
        FBoxLink:=FBoxLink+Request.QueryFields[i]+'&';
      end;
    end;
    if FBoxLink<>'' then begin
      System.Delete(FBoxLink, Length(FBoxLink), 1);
      FBoxLink:='&'+FBoxLink;
    end;
  end;
end;
Az FBoxList listájában most már rendelkezésünkre áll a kosárban lévő termékek azonosító kódja. Ezek után már listázhatjuk a web lapra a termékek nevét és árát.

Ehhez most módosítjuk a box.dat nevű állományt, melyben a kosár web lap részletének HTML kódja található. Eddig csak annyit írtunk ki, hogy "A kosár tartalma jelenleg üres.", de most már ez módosulhat, így ide most egy címkét helyezünk el, hogy ezt majd programból dinamikusan tudjuk előállítani. Ez a címke a <#boxitems> lesz.

Elhelyezünk itt egy másik címkét is <#allprice> néven. Itt jelezzük majd a felhasználónak, hogy a kosár tartalmának mennyi a teljes összege.

Nézzük most ezen címkéknek a feldolgozását.

A <#boxitems> címkét felvesszük a HTMLProducer1 komponens CustomTagList property-ének listájába.

Az OnCustomHTMLTag eseménynél adhatunk értéket ennek a címkének:
    4: begin//boxitems
      if FBoxList.Count=0 then begin
        ReplaceText:='A kosár tartalma jelenleg üres.';
      end else begin
        ReplaceText:=HTMLProducer1.Content(HTMDIR+'boxitems.dat');
      end;
    end;
Ha az FBoxList nem tartalmaz egyetlen termékkódot sem, akkor a kosár üres, így ezt írjuk ki a felhasználó számára.

Ha vannak termék kódok, akkor egy táblázatot kell generálnunk, melyben soronként felsoroljuk az egyes termékeket és a hozzájuk tartozó adatokat.

Itt látható példa a HTMLProducer1 komponens egyik nagy előnyére a TPageProducer komponenssel szemben. Mivel itt most egy másik web lap adatait kell feldolgoznunk, miközben az előző lap feldolgozása közben vagyunk, így ehhez egy új TPageProducer komponensre lenne szükségünk és a program vége felé már mást nem is látnánk a képernyőn, mint TPageProducer komponenseket.

E helyett a THTMLProducer komponens esetében, akár a feldolgozás közben is elvégeztethetünk egy újabb web lap feldolgozását. Így a teendőnk csak annyi, hogy meghívjuk ismét a Content függvényt, paraméterként átadva a most feldolgozandó web lap állományt, mely most a boxitems.dat lesz. Ennek tartalma az alábbi:
<table cellspacing=0 cellpadding=0 border=0 width="90%">
<tr>
<td class=f8b style="text-align: center">
Termék
</td>
<td class=f8b style="text-align: center">
Ár
</td>
</tr>
<#include file=boxitem.dat repeat=yes>
</table>
Látható, hogy itt egy táblázat lesz két oszloppal (terméknév, ár). Elhelyezünk benne egy címkét is, melyet a THTMLProducer komponens automatikusan fog feldolgozni. Az <#include file=boxitem.dat repeat=yes> címke arra utasítja a komponenst, hogy a boxitem.dat állományt illessze a címke helyére annyiszor egymás után, amíg csak az OnMoreData eseménynél ezt le nem állítjuk. Így lehetőségünk van arra, hogy annyi sort készítsünk a web lap táblázatába, ahány termék van a kosárban.

A boxitem.dat állomány egyetlen táblázat sort tartalmaz:
<tr>
<td class=f8>
<#productname>
</td>
<td class=f8 style="text-align: right">
<#productprice>
</td>
</tr>
Ezt a HTML kódrészletet kell ismételgetni az egyes termékek listázásához. Látható, hogy itt újabb címkék lettek elhelyezve. A <#productname> esetén cserélnünk kell a kosárban lévő aktuális termék nevére, a <#productprice> esetén pedig annak az árára.

Az adatbázis megnyitásáról és lezárásáról is gondoskodnunk kell mielőtt fenti műveletek megtörténnének. Megnyitáshoz az OnBeforeProcessInclude eseményt használjuk fel. Ez akkor jön létre, ha a BeforeProcessFiles property-ben megadjuk annak az állománynak a nevét, melyet a komponens feldolgoz. Itt most megadtuk a box.dat állományt.
procedure TWebModule1.HTMLProducer1BeforeProcessInclude
  (Index: Integer; var Enabled: Boolean);
begin
  case Index of
    0: tProduct.Open;
    1: begin
      tProduct.Open;
      FI:=0;
      FI2:=0;
    end;
  end;
end;
A megnyitáson kívül még két integer típusú változót is lenullázunk (FI, FI2), szerepükről a későbbiekben szólunk.

Az adatbázis lezárásához az OnAfterProcessInclude eseményt használhatjuk, mely az AfterProcessFiles property-ben megadott állomány feldolgozása után jön létre. Itt egyszerűen csak zárjuk a táblát.
procedure TWebModule1.HTMLProducer1AfterProcessInclude
   (Index: Integer);
begin
  case Index of
    0, 1: tProduct.Close;
  end;
end;
Amikor elindul a boxitems.dat feldolgozása, akkor a komponens automatikusan beilleszti a boxitem.dat-ot is. Az OnMoreData eseménynél szabályoznunk kell, hogy ezt hányszor tegye meg. A szükséges szám a kosárban lévő termékek számával egyezik, ezért az FBoxList változó Count property-ét felhasználhatjuk, valamint a THTMLProducer komponens Counter számlálóját, melynek értéke megegyezik azzal a számmal, hogy eddig hányszor került ismételten beillesztésre az adott állomány.
procedure TWebModule1.HTMLProducer1MoreData
   (Index: Integer; var MoreData: Boolean);
begin
  case Index of
     ...
    1: MoreData:=HTMLProducer1.Counter<FBoxList.Count;
     ...
  end;
end;

Ahhoz hogy ez az esemény létrejöjjön, a MoreDataFiles property-ben fel kell sorolnunk a boxitem.dat állomány nevet is.

Mielőtt a táblázat egy sora beillesztésre kerül, vagyis a kosár egy terméke kiírásra kerülne a web lapra, feltétlenül szükséges, hogy a tProduct táblában az aktuális rekordot a megfelelő termékre állítsuk. Ehhez a komponens OnBeforeRepeatInclude eseményét használhatjuk fel. Miután a BeforeRepeatFiles property-ben megadtuk a boxitem.dat állomány nevet, ez az esemény minden olyan esetben meghívásra kerül, amikor a boxitem.dat kerülne beillesztésre egy címke helyett, de ez még nem történt meg.

Itt az aktuális termék azonosító kódja alapján előkeressük a hozzá tartozó rekordot az adatbázisból.

Hogy melyik az aktuális termék, azt a FI globális változó mondja meg, melyet a művelet kezdetén nullára állítottunk. Az aktuális rekordra való ugrást a Locate függvénnyel tesszük meg, melyben az ID mezőre keresünk, az FBoxList változóban található termékkódok közül a soron következőre.

Ha megtaláltuk ezt a rekordot, akkor a FI2 globális változóban egyúttal a termék értékét is összegezzük, hogy a táblázat végén a kosár összértékét is kiírhassuk.
procedure TWebModule1.HTMLProducer1BeforeRepeatInclude
    (Index: Integer; var Enabled: Boolean);
begin
  case Index of
    0: begin
      Enabled:=tProduct.Locate('ID', StrToInt(FBoxList[FI]), []);
      inc(FI);
      if Enabled then begin
        inc(FI2, Round(tProductPRICE.Value*
           (tProductTAX.Value/100+1)));
      end;
    end;
  end;
end;

Ezek után már nagyon egyszerű a helyzetünk, amikor a boxitem.dat címkéit kell cserélnünk. Itt találjuk a productname címkét és a productprice címkét. Amikor ideér a feldolgozás, akkor a tProduct tábla pontosan azon a rekordon áll, amelyiket éppen ki kell írnunk, így nincs más dolgunk, mint a megfelelő mező értékét átadni:
procedure TWebModule1.HTMLProducer1CustomHTMLTag
    (Index: Integer; TagParams: TStrings; var ReplaceText: String);
begin
  case Index of
    ...
    6: begin//productname
      ReplaceText:=tProductNAME.Value;
    end;
    7: begin//productprice
      ReplaceText:=IntToStr(Round(tProductPRICE.Value
        *(tProductTAX.Value/100+1)));
    end;
    ...
  end;
end;
Ezek után már csak két teendőnk marad a helyes működés érdekében. Az egyik, hogy minden egyes a web lapon lévő linkhez még be kell írnunk, hogy mi az aktuális kosár tartalma, a másik pedig az, hogy mi az adott linkhez tartozó termék kódja.

Amikor a web lap bal oldalán lévő termékeket listázzuk, akkor kerül feldolgozásra a productitem.dat állomány. Ez az állomány tartalmazza a táblázat egy-egy sorát, melyben az egyes termékek adatai jelennek meg. Itt található egy link is, mely a "Tegyük a kosárba >>" nevet viseli. Ide most két címkét kell elhelyeznünk:
...
<a class=f8 href="/scripts/ws07.exe?<#productidlink><#boxlink>">
Tegyük a kosárba >>
</a>
...
A feldolgozáskor a <#productidlink> címkét az adott termék kódjára kell cserélnünk:
procedure TWebModule1.HTMLProducer1CustomHTMLTag
  (Index: Integer; TagParams: TStrings; var ReplaceText: String);
begin
  case Index of
    ...
    9: begin//productidlink
      ReplaceText:='b'+IntToStr(FBoxList.Count)+'='+
        tProductID.AsString;
    end;
  end;
end;
A <#boxlink> esetén pedig a kosár aktuális tartalmának termékkódjait kell behelyettesítenünk. Ez a sztring az FBoxLink globális változóban található.
procedure TWebModule1.HTMLProducer1CustomHTMLTag
  (Index: Integer;TagParams: TStrings; var ReplaceText: String);
begin
  case Index of
    ...
    8: begin//boxlink
      ReplaceText:=FBoxLink;
    end;
    ...
  end;
end;
Az FBoxLink változónak a GetBox eljárás ad értéket, melyet a cikk elején már tárgyaltunk. Amikor meghatározásra kerül a kosár aktuális tartalma, akkor rögtön előállítjuk a szükséges sztringet, mely kódolva tartalmazza a kosár tartalmát.
procedure TWebModule1.GetBox;
var
  i: integer;
begin
  with Request.QueryFields do begin
    FBoxList.Clear;
    FBoxLink:='';
    for i:=0 to Count-1 do begin
      if Names[i][1]='b' then begin
        FBoxList.Add(Values[Names[i]]);
        FBoxLink:=FBoxLink+Request.QueryFields[i]+'&';
      end;
    end;
    if FBoxLink<>'' then begin
      System.Delete(FBoxLink, Length(FBoxLink), 1);
      FBoxLink:='&'+FBoxLink;
    end;
  end;
end;
Ezek után, ha például a web lapon megnézzük, hogy a "Tegyük a kosárba >>" szöveghez milyen linkek tartoznak, amikor mondjuk az 1-es és a 3-as kódú termék már a kosárban áll, akkor egyértelmű lesz a fenti művelet sor:
Az 1-es terméknél a http://as1/scripts/ws07.exe?
   b2=1&b1=3&b0=1 link található.
A 2-es terméknél a http://as1/scripts/ws07.exe?
   b2=2&b1=3&b0=1 link található. 
A 3-as terméknél a http://as1/scripts/ws07.exe?
   b2=3&b1=3&b0=1 link található.
Megfigyelhető, hogy minden linknél három paraméter jön létre. Az első paraméter b2 névvel az aktuális termék kódját tartalmazza. Látható, hogy ez termékenként különböző érték. Ez után jön a b1 és b0 paraméter, mely a 3-as és 1-es termék kódját tartalmazza.

Ezek után, ha a linkre kattintunk, akkor az új termék is bekerül a kosárba és linkek úgy módosulnak, hogy most már négy paraméterük lesz: az első az adott termék kódja, a további három pedig a kosár tartalma.

Ettől kezdve már tetszőleges számú termék kezelhető a rendszerrel. Csupán a Product.dbf táblát kell feltölteni a megfelelő rekordokkal.

Internet áruház cikksorozat