Lapozás PHP-ben

Nem is olyan régen írtam egy blogot a dinamikus több sorba tördelésről. Annak folytatásaként fogható fel ez a kis ismertető. Mert hát nagyon hasznos, hogy 500 ezer képet, vagy adatbázisbeli rekordot meg tudsz jeleníteni egy weboldalon több sorban oszlopokba rendezve, csak hát elég valószínűtlen, hogy azt kibírná a böngészőnk, vagy akár a szerver. De elég, ha csak saját emberi tulajdonságunkra gondolunk, a türelmetlenségre. Mi a megoldás? Természetesen a lapozhatóság megvalósítása. Amire többféle megoldás létezik, és sok fórumon, sok blogban foglalkoztak / foglalkoznak a témával. Némelyik igen szép kinézetet is kap. Osztályokat is írnak rá. Ez alól valószínű én sem leszek kivétel, de addig is nézzünk pár primitívebb módszert.

Az első példa egyszerű tömböt fog használni az adattárolásra. Akár csak a sortörésről szóló blogom első példái is.

Oké. Van egy kérdésem. Mi az első, ami eszedbe jut a lapozással kapcsolatban, és szükséges ahhoz, hogy egyáltalán legyen lapozás? Igen igen. Jól gondolod. Hogy hány elemet akarsz megjeleníteni egy oldalon egyszerre. Akkor add is meg gyorsan. Én tízet választok.

$limit = 10; //egy oldalon megjelenő elemek száma

Azt kérded, mi kell még? Ja nem kérded, mondod... Helyes. Valóban szükség van arra, hogy épp melyik oldalt nézed, hiszen anélkül nem lehetne eldönteni, mit kell megjeleníteni, és mi legyen a következő, oldal illetve az előző. Ehhez viszont szükség lesz URL-ben küldött változóra. Amit a $_GET tömbből érünk el. Ezt nem fejtem ki bővebben. A cikknek nem tárgya. De ne késlekedjünk, mert beesteledik. gyorsan folytasd is az előző kódot a következő sorral.

$page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;

Ez a kódrészlet az első oldalt állítja be, ha semmit nem küldtünk url-ben. Egyébként az egy pozitív egész számot. Azt azért még ellenőrizni kell, hogy véletlenül nem egy nem létező oldalszámot próbálsz-e elérni. De ahhoz kell tudni, hogy max mennyi oldal van. Amihez persze kell, hogy összesen hány sorból gazdálkodunk, illetve maga a tömb sem hátrány, ha megvan.

  1. //teszt adatoknak pont jó lesz. Fantáziátlan, de ez van.
  2. $list = array_merge(range("A","Z"),range(300,401),range('a','z'));
  3. $c = count($list); //ennyi eleme van a tömbnek.
  4. $maxpage = ceil($c / $limit); //és ennyi oldal van összesen.
  5.  
  6. //nem lehet 0, vagy negatív az oldal. Bár utóbbit már kiszűrtük.
  7. if ($page <= 0)
  8. {
  9.         $page = 1;
  10. }
  11. //És ne legyen nagyobb se az oldalszám, mint ami van.
  12. else if ($page >= $maxpage)
  13. {
  14.         $page = $maxpage;
  15. }

A ceil függvénnyel felfele kell kerekíteni a kapott eredményt, mivel ha csak 3.2 darab oldalra van szükség, az akkor is 4 oldal. Aki nem hiszi, járjon utána!

Most pedig kell tudni, mettől meddig kellenek a tömbből az elemek. Mi sem egyszerűbb ennél.
Az oldalszám mínusz 1 megszorozva a limittel pont kiadja az első olyan indexet, ahonnan szükség van az elemekre. És hogy meddig? még $limit darab kell.

  1. $offset = ($page-1) * $limit;
  2. $end = $offset+$limit;

Most már nincs más hátra, mint előre. Azaz jöhet a for ciklus.
Azt tudjuk mettől meddig van szükség az elemekre a tömbből. Vagyis majdnem. Oké, hogy nem lehet nagyobb vagy egyenlő az $end változó értékével, de mi van, ha az $end változó nagyobb, mint ahány elem van a tömbben? Akkor gáz van. Egy, kettő, vagy egy halom undefined index hibaüzenet jelenhet meg akár. vagy csak feleslegesen dolgozik a ciklus még pár sorig. Tehát társfeltételnek azt is meg kell adnod, hogy az index kisebb legyen, mint ahány elem van a tömbben. ( Mivel a tömb indexe nullától indul, ez pont megfelelő vizsgálat. )

  1. for ($i=$offset; $i < $c and $i < $end; $i++)
  2. {
  3.         print $list[$i]."<br />";
  4. }

Hát ez nagyon szép. Azért nincs egy kis hiányérzeted? Nincs? Akkor lapozz! Hoppáá.. nincs mivel. Na dobjunk össze egy oldalszám listázót is. Valami primitívet először. Legelső oldaltól a legutolsóig jelenítsük meg a linkeket. És küldjük el az url-ben a page változót is.
Extraként még ki is emelem az aktuális oldalt más stílussal. Így se lesz egy topmodell a kis csúfságunk, de azért a célnak megfelel.

  1. //lapozó linkek
  2. for ($i=1; $i <= $maxpage; $i++)
  3. {
  4.         $style = ($i == $page) ? "color: black;" : "color: blue;";
  5.         print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
  6. }

De milyen vicces lenne 500 oldal linkjét kiírni egyszerre. Vagy inkább rettenetes. Ezért egy jobb megoldást javaslok, amiben még előző és következő link is lesz, és egyszerre csak adott számú oldal linket jelenít meg az aktuális oldalszámhoz képest kicsit előre, kicsit hátra.

Kelleni fog itt is, hogy egyszerre hány linket kell megjeleníteni. Ez lesz $linklimit. És aztán ki kell számolni, hogy hányadik oldalszámtól is kell ezt a listát kiírni.

  1. //lapozó linkek
  2. $linklimit = 10;  //ennyi link lesz kint egyszerre
  3.  
  4. $linklimit2 = $linklimit / 2; //ennyi link legyen az aktuális előtt és után ( vagy amennyi van )
  5. $linkoffset = ($page > $linklimit2) ? $page - $linklimit2 : 0; //átugrott linkek száma
  6. $linkend = $linkoffset+$linklimit; //az utolsó oldalszám.
  7.  
  8. //Ha az utolsó oldalaknál vagyunk, és nincs már $linklimit2 oldal az aktuális után
  9. if ($maxpage - $linklimit2 < $page)
  10. {
  11.         $linkoffset = $maxpage - $linklimit;
  12.         if ($linkoffset < 0)
  13.         {
  14.                 $linkoffset = 0;
  15.         }
  16.         $linkend = $maxpage;
  17. }
  18. //előző oldal
  19. if ($page > 1)
  20. {
  21.         print "<a href='?page=".($page-1)."'>Előző</a>   ";
  22. }
  23. //a linkek megjelenítése
  24. for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
  25. {
  26.         $style = ($i == $page) ? "color: black;" : "color: blue;";
  27.         print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
  28. }
  29. //következő oldal
  30. if ($page < $maxpage)
  31. {
  32.         print "<a href='?page=".($page+1)."'>Következő</a>";
  33. }

Azért a gyakoribb talán mégis az, ha adatbázisban lévő adatokat kell listázni. Erre készítettem egy egyszerű teszt táblát. Aminek az sql kódja:

  1. create table teszt (
  2.      elem varchar(3) not null
  3. );

Itt szeretném megragadni az alkalmat, hogy elmondjam, az auto_increment id igenis jó megoldás. És nem kell aggódni attól, ha esetleg törölve lesz egy sor, és nem lesz folytonos a sorszámozás, mert ez NEM SORSZÁMOZÁS!!! És az előző, következő lekérdezésére is megvan a megfelelő módszer. Ez pedig az idő szerinti lekérdezés, az aktuális elem keletkezésének idejénél korábban, vagy később keletkezett pontosan 1 darab rekord lekérdezése a limit 1 használatával. De auto_increment id esetén használható erre az id is.

Most töltsd fel az adatbázist a következő kódsorral miután kapcsolódtál az adatbázishoz:

  1. $list = array_merge(range("A","Z"),range(300,401),range('a','z'));
  2.  
  3. $insert = "insert into teszt(elem) values  ";
  4. foreach ($list as $value)
  5. {
  6.         $insert .= " ('$value'), ";
  7. }
  8. $insert = rtrim($insert,", ");
  9. mysql_query($insert);

Csak egyszer futtasd le egy fájlban. És jöhet a folytatás, ahol a linklistázót már nem mutatom, mivel az ugyanaz, mint az előző kódban. Hogy akkor mi az, ami változik?
Például másképp kell lekérdezni hány elem van az adatbázisban, mert hogy már adatbázisról beszélünk. Éppen ezért while ciklusban kell lekérdezni a sorokat.

  1. $limit = 10;
  2.  
  3. $sql = "select count(id) from teszt";
  4.  
  5. $maxpage = ceil($c / $limit);
  6.  
  7. $page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;
  8. if ($page <= 0)
  9. {
  10.         $page = 1;
  11. }
  12. else if ($page >= $maxpage)
  13. {
  14.         $page = $maxpage;
  15. }
  16.  
  17. $offset = ($page-1) * $limit;
  18.  
  19. //itt az sql utasítás. Nálam csak simán mindent lekérdez adott intervallumban.
  20. $query = mysql_query("select * from teszt limit $offset, $limit ");
  21. //és maga a ciklus.
  22. while ($row = mysql_fetch_assoc($query))
  23. {
  24.         print $row['elem']."<br />";
  25. }

Most, hogy ilyen szépen elkészült mindkét megoldás, azért egy összegzést még megérdemel, hogy ne csak darabolva legyen.

Tömbös megoldás:

  1. <?php
  2. $limit = 3;
  3. $page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;
  4.  
  5. $list = array_merge(range("A","Z"),range(300,401),range('a','z'));
  6. $c = count($list);
  7. $maxpage = ceil($c / $limit);
  8.  
  9. if ($page <= 0)
  10. {
  11.         $page = 1;
  12. }
  13. else if ($page >= $maxpage)
  14. {
  15.         $page = $maxpage;
  16. }
  17.  
  18. $contents = array_chunk($list, $limit);
  19.  
  20. foreach ($contents[$page-1] as $value)
  21. {
  22.         print "$value<br />";
  23. }
  24.  
  25. //lapozó linkek
  26. $linklimit = 10;
  27.  
  28. $linklimit2 = $linklimit / 2;
  29. $linkoffset = ($page > $linklimit2) ? $page - $linklimit2 : 0;
  30. $linkend = $linkoffset+$linklimit;
  31.  
  32. if ($maxpage - $linklimit2 < $page)
  33. {
  34.         $linkoffset = $maxpage - $linklimit;
  35.         if ($linkoffset < 0)
  36.         {
  37.                 $linkoffset = 0;
  38.         }
  39.         $linkend = $maxpage;
  40. }
  41.  
  42. if ($page > 1)
  43. {
  44.         print "<a href='?page=".($page-1)."'>Előző</a>   ";
  45. }
  46. for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
  47. {
  48.         $style = ($i == $page) ? "color: black;" : "color: blue;";
  49.         print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
  50. }
  51. if ($page < $maxpage)
  52. {
  53.         print "<a href='?page=".($page+1)."'>Következő</a>";
  54. }
  55. ?>

Adatbázisos megoldás:

  1. <?php
  2. //kapcsolódás
  3. mysql_connect("localhost", "felhasználó neved", "jelszavad");
  4. mysql_select_db("teszt");
  5.  
  6. $limit = 10;
  7.  
  8. $sql = "select count(id) from teszt";
  9.  
  10. $maxpage = ceil($c / $limit);
  11.  
  12. $page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;
  13. if ($page <= 0)
  14. {
  15.         $page = 1;
  16. }
  17. else if ($page >= $maxpage)
  18. {
  19.         $page = $maxpage;
  20. }
  21.  
  22. $offset = ($page-1) * $limit;
  23.  
  24. $query = mysql_query("select * from teszt limit $offset, $limit ");
  25.  
  26. while ($row = mysql_fetch_assoc($query))
  27. {
  28.         print $row['elem']."<br />";
  29. }
  30.  
  31. //lapozó linkek
  32. $linklimit = 10;
  33. $linklimit2 = $linklimit / 2;
  34. $linkoffset = ($page > $linklimit2) ? $page - $linklimit / 2 : 0;
  35. $linkend = $linkoffset+$linklimit;
  36.  
  37. if ($maxpage - $linklimit2 < $page)
  38. {
  39.         $linkoffset = $maxpage - $linklimit;
  40.         if ($linkoffset < 0)
  41.         {
  42.                 $linkoffset = 0;
  43.         }
  44.         $linkend = $maxpage;
  45. }
  46.  
  47. if ($page > 1)
  48. {
  49.         print "<a href='?page=".($page-1)."'>Előző</a>   ";
  50. }
  51. for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
  52. {
  53.         $style = ($i == $page) ? "color: black;" : "color: blue;";
  54.         print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
  55. }
  56. if ($page < $maxpage)
  57. {
  58.         print "<a href='?page=".($page+1)."'>Következő</a>";
  59. }
  60. ?>

Ezzel a cikk végére is értem. Ha szeretnéd kombinálni a lapozást a sortördeléssel, rajta. Nem állok az utadba :)
Újra ajánlom ehhez a Lista megjelenítése PHP-ben című blogomat. És adott oldalon belül beépíthető a ciklusba a sortörés megvalósítása is. Sok sikert hozzá! És ha kérdés van, vagy hibát találsz a cikkben, akkor azt tarts maga... Akarom mondani szívesen fogadom kommentben :)

Címkék:

Kategóriák:

Hozzászólások

Tomi képe

Hali, nagyon jó a cikk. Pont az én oldalamra is kellett egy ilyen megoldás és teljesen jó lett.
Szóval csak megszeretném köszönni, hogy van egy ilyen cikk, pont kapóra jött...

:)

Pápai Imre képe

Nagyon szépen köszönöm a forráskódot, igazán sokat tanultam belőle, pont ilyet kerestem..
Örök hála:)

Zsoca képe

Helló Ákos!
Most látom, hogy itt is hozzá szólhatok, nemrég írtam a kapcsolatok menüpont alatt.. :\
Gondoltam írok gyorsan ide is, hogy tudd, melyik témáról van szó!
Köszönjük még egyszer a cikket, nagyon hasznos!
Szóval, az itt megadott lapozáskor a POST-olt adataim “vesznek el”..
Másoljak be kódrészletet, vagy van valamilyen ötleted?
Köszönöm előre is!

Rimelek képe

Szia
Lapozásnál rendszerint az oldalszám url-ben van. A cikkben is így írtam. Ha bármilyen POST -ban küldött adatod van, az csak az űrlap elküldése után közvetlen létezik. Lapozáskor elveszik. Megoldásként használhatsz session-öket. Vagy ami adatra szükséged van minden oldalon, szintén berakhatod url-be az oldalszám mellé. Akkor a $_GET-ben találod majd az értékeket.

ifj.F.Tamás képe

Üdv!
szeretném megkérdezni hogy hogyan lehet azt megcsinálni hogy az aktuális oldal ahol éppen van "más színű" legyen?

  1. for ($i=1+$linkoffset; $i <= $linkend; $i++)
  2. {
  3.         $style = ($i == $page) ? "color: black;" : "color: blue;";
  4.         print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
  5. }

gondolom valami itt kéne módosítani?

Előre is köszönöm a válaszodat.

Tisztelettel:
ifj.F.Tamás

Rimelek képe

Helló,

amit idéztél kódrészletet, az pont ezt teszi. Listázza az oldalszámokat és fekete lesz az, ami aktuális. A többi kék. Csak a megfelelő css részletet kell megváltoztatnod. black -et átírod másra. Akár tetszőleges színkódra.

DoubleW képe

Én ezt a részét a kódnak nem értem
if ($linkoffset < 0)
{
$linkoffset = 0;
}
Szóval ez a if ág : if ($maxpage - $linklimit2 < $page) akkor telyesül csak ha már elértük a 12. oldalt ami rendben is van de az offsett mikor lehet 0?
Ha

Rimelek képe

A $linkoffset azt adja meg, hogy az oldallinkek listája hányadik oldaltól kezdődjön. 0 az offset, ha az elsőtől kell kezdődnie. 1 az offset, ha a 2.-tól. stb... Negatív akkor lehet a változó, amikor a $linklimit értéke nagyobb, mint ahány oldal összesen van. Az előző sorban levő kivonás miatt.

DoubleW képe

Kösz szépen:-)Utána már megértetem én is hogy az miért is van ott.

Bob képe

Köszi!

Rimelek képe

Volt egy elvi hiba a megoldásban. Szégyen, hogy ez ennyi évig fel sem tűnt nekem :) A lapozó linkek kiírásánál azt is figyelembe kell venni, hogy maximum hány oldal van. Tehát ezt a sort:
for ($i=1+$linkoffset; $i <= $linkend; $i++)
így kell írni:
for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
Vagy már a $linkend változót is lehet módosítani a ciklus előtt ennek megfelelően.

Új hozzászólás