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.

//teszt adatoknak pont jó lesz. Fantáziátlan, de ez van.
$list = array_merge(range("A","Z"),range(300,401),range('a','z'));
$c = count($list); //ennyi eleme van a tömbnek.
$maxpage = ceil($c / $limit); //és ennyi oldal van összesen.

//nem lehet 0, vagy negatív az oldal. Bár utóbbit már kiszűrtük.
if ($page <= 0)
{
        $page = 1;
}
//És ne legyen nagyobb se az oldalszám, mint ami van.
else if ($page >= $maxpage)
{
        $page = $maxpage;
}

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.

$offset = ($page-1) * $limit;
$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. )

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

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.

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

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.

//lapozó linkek
$linklimit = 10;  //ennyi link lesz kint egyszerre

$linklimit2 = $linklimit / 2; //ennyi link legyen az aktuális előtt és után ( vagy amennyi van )
$linkoffset = ($page > $linklimit2) ? $page - $linklimit2 : 0; //átugrott linkek száma
$linkend = $linkoffset+$linklimit; //az utolsó oldalszám.

//Ha az utolsó oldalaknál vagyunk, és nincs már $linklimit2 oldal az aktuális után
if ($maxpage - $linklimit2 < $page)
{
        $linkoffset = $maxpage - $linklimit;
        if ($linkoffset < 0)
        {
                $linkoffset = 0;
        }
        $linkend = $maxpage;
}
//előző oldal
if ($page > 1)
{
        print "<a href='?page=".($page-1)."'>Előző</a>   ";
}
//a linkek megjelenítése
for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
{
        $style = ($i == $page) ? "color: black;" : "color: blue;";
        print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
}
//következő oldal
if ($page < $maxpage)
{
        print "<a href='?page=".($page+1)."'>Következő</a>";
}

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:

create table teszt (
     id int not null primary key auto_increment,
     elem varchar(3) not null
);

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:

$list = array_merge(range("A","Z"),range(300,401),range('a','z'));

$insert = "insert into teszt(elem) values  ";
foreach ($list as $value)
{
        $insert .= " ('$value'), ";
}
$insert = rtrim($insert,", ");
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.

$limit = 10;

$sql = "select count(id) from teszt";
$c = array_shift(mysql_fetch_row(mysql_query($sql)));

$maxpage = ceil($c / $limit);

$page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;
if ($page <= 0)
{
        $page = 1;
}
else if ($page >= $maxpage)
{
        $page = $maxpage;
}

$offset = ($page-1) * $limit;

//itt az sql utasítás. Nálam csak simán mindent lekérdez adott intervallumban.
$query = mysql_query("select * from teszt limit $offset, $limit ");
//és maga a ciklus.
while ($row = mysql_fetch_assoc($query))
{
        print $row['elem']."<br />";
}

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:

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

$list = array_merge(range("A","Z"),range(300,401),range('a','z'));
$c = count($list);
$maxpage = ceil($c / $limit);

if ($page <= 0)
{
        $page = 1;
}
else if ($page >= $maxpage)
{
        $page = $maxpage;
}

$contents = array_chunk($list, $limit);

foreach ($contents[$page-1] as $value)
{
        print "$value<br />";
}

//lapozó linkek
$linklimit = 10;

$linklimit2 = $linklimit / 2;
$linkoffset = ($page > $linklimit2) ? $page - $linklimit2 : 0;
$linkend = $linkoffset+$linklimit;

if ($maxpage - $linklimit2 < $page)
{
        $linkoffset = $maxpage - $linklimit;
        if ($linkoffset < 0)
        {
                $linkoffset = 0;
        }
        $linkend = $maxpage;
}

if ($page > 1)
{
        print "<a href='?page=".($page-1)."'>Előző</a>   ";
}
for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
{
        $style = ($i == $page) ? "color: black;" : "color: blue;";
        print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
}
if ($page < $maxpage)
{
        print "<a href='?page=".($page+1)."'>Következő</a>";
}
?>

Adatbázisos megoldás:

<?php
//kapcsolódás
mysql_connect("localhost", "felhasználó neved", "jelszavad");
mysql_select_db("teszt");

$limit = 10;

$sql = "select count(id) from teszt";
$c = array_shift(mysql_fetch_row(mysql_query($sql)));

$maxpage = ceil($c / $limit);

$page = isset($_GET['page']) ? abs((int)$_GET['page']) : 1;
if ($page <= 0)
{
        $page = 1;
}
else if ($page >= $maxpage)
{
        $page = $maxpage;
}

$offset = ($page-1) * $limit;

$query = mysql_query("select * from teszt limit $offset, $limit ");

while ($row = mysql_fetch_assoc($query))
{
        print $row['elem']."<br />";
}

//lapozó linkek
$linklimit = 10;
$linklimit2 = $linklimit / 2;
$linkoffset = ($page > $linklimit2) ? $page - $linklimit / 2 : 0;
$linkend = $linkoffset+$linklimit;

if ($maxpage - $linklimit2 < $page)
{
        $linkoffset = $maxpage - $linklimit;
        if ($linkoffset < 0)
        {
                $linkoffset = 0;
        }
        $linkend = $maxpage;
}

if ($page > 1)
{
        print "<a href='?page=".($page-1)."'>Előző</a>   ";
}
for ($i=1+$linkoffset; $i <= $linkend and $i <= $maxpage; $i++)
{
        $style = ($i == $page) ? "color: black;" : "color: blue;";
        print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";
}
if ($page < $maxpage)
{
        print "<a href='?page=".($page+1)."'>Következő</a>";
}
?>

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 :)

Kategóriák: 
Megosztás/Mentés

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?

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

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.

patyex képe

én ugyan ezt szeretném beépíteni a relogin-v2.0.3- ban
ha nekem tudnál segíteni annak nagyon örülnék

Rimelek képe

Van benne ennél okosabb lapozó is. Este, ha el nem felejtem, írok róla bővebben majd. Ha ez a fajta lapozó érdekel, akkor tedd fel a kérdésed, hogy mit nem értesz! Én pedig megpróbálok válaszolni.

Rimelek képe

Na tehát lapozás. Van egy ilyen: R.E. Pagination

Ennek van leírása is a használatra. Nincs beépítve a loginba azért, mert ott egy ugyanilyen elven működő van. Így ha a beépített megoldásokkal kérdezel le dolgokat, akkor már adva lesz a lapozó megoldás. Egyéb esetben a fent linkelt R.E. Pagination -t használhatod. Ha a lapozás módszere érdekel, akkor jó ez a cikk.

Bővebben azért nem írok mégsem a login lapozójáról, mert nem tudom, mi a célod. Lehet, nem is lenne egyszerűbb azt használnod. Nézd meg, amit linkeltem. Ha tetszik, használd! Ha nem boldogulsz, kérdezz ott! Ha nem tetszik, és neked a loginé kell, vagy mondjuk felhasználólistát akarsz lapozhatóvá tenni ( bár az eleve az ), akkor írj a loginhoz ez ügyben. LINK

Laczkó képe

Szia
Van ez a kódrészlet:

    $list = array_merge(range("A","Z"),range(300,401),range('a','z'));
     
    $insert = "insert into teszt(elem) values  ";
    foreach ($list as $value)
    {
            $insert .= " ('$value'), ";
    }
    $insert = rtrim($insert,", ");
    mysql_query($insert);

Ezt fel kell tölteni az adatbázisba?
Mert nekem nem engedi, hibát ír ki:

Rimelek képe

Szia

Fent írtam a bejegyzésben, milyen sql-lel kell létrehozni a teszt táblát, hogy ezt kipróbálhasd. Anélkül nem fog menni. Ha más lenne a probléma, ennél többet nem tudok segíteni, mivel a hibaüzenetet, ami igen sokat számít egy hibánál, nem írtad meg.

Laczkó képe

Első látásra tűnt csak bonyolultnak. Többször átolvasva már megértettem.
Ez nagyon hasznos leírás
Köszönöm

Csakany97 képe

Üdv!

Nekem az a problémám, hogy berakom a kódot, amit írtál és kiírja a nagybetűket, számokat 300-tól 401-ig, valamint kisbetűket, amiket megadtunk
$list = array_merge(range("A","Z"),range(300,401),range('a','z')); az alábbi kódban.

Egy képkatalógus lapozójának szeretném használni, de nem tudom, hogy tudnám beállítani, hogy rendezze a képeket a megadott betűk helyett.

Rimelek képe

Szia. Ehhez nem tudok hozzászólni. Az a betűkiíró csak egy nagyon alap minta volt a lapozás megértésére. De van ott még több is. Mysql-es például. Fogalmam sincs, hogy működik a te képkatalógusod. A megfelelő módszert kell hozzá választanod. Ha nincs mysql adatbázisod, csak mappák és fájlok, akkor azokat kell beolvasnod egy tömbbe. Ennek mikéntje nem témája ennek a cikknek. Ha van egy tömböd a mappákról vagy képekről, akkor azt már tudod lapoztatni. scandir() függvény segíthet. És még van több módszer is. Pl.: DirectoryIterator

Csakany97 képe

A scandir()-hez mit kel beírni?

Van olyan módszer, hogy mondjuk beírom a kép name-hez, hogy pl.: kep, és aztán az adjam meg, mint az oldal tartalma?

Rimelek képe

Ha programozni szeretnél, ennyi önállóságra szükséged lesz. Beírod google keresőbe, hogy "php scandir". Szinte biztos, hogy az első találat lesz a php manual. És abból megfejtheted. Sajnos ilyen szintű alapok leírására nincs időm és energiám.

Ezen kívül a magyar nyelv helyesebb használatával több esélyed van arra is, hogy megértsenek, akiktől kérdezel. A második mondatodat sehogy sem tudom értelmezni.

Még gyúrnod kell a php alapokra! Sok sikert!

Pal képe

Helo!
Ezt a lapozót szeretném hozzáfűzni egy AJAX válaszba, melynél ha a feltételeket változtatjuk akkor a találatok száma is változik, az oldal újratöltődése nélkül.

A legnagyobb gondom vele, hogy az oldal újratöltése nélkül szeretném átadni az AJAX küldéssel a 'page' számát.
Nem tudom, hogy tudnál-e ebben segíteni. Válaszod előre is köszönöm!

Rimelek képe

Üdv. És mi okozza a nehézséget? Maga az AJAX az, amit még nem értesz, vagy van egyedi igény is?

Berakod az egészet az ajax-szal meghívandó fájlodba. És a tartalommal együtt a lapozót is ajax-szal jeleníted meg. A linkeket pedig az AJAX-nak megfelelően állítod össze. Hogy azt te hogy szereted, azt te tudod. onclick esemény közvetlenül a linkben, vagy osztályok alapján iratkozol fel az onclick-re.

Így persze egyáltalán nem lesz tartalom ajax nélkül. De ha okosabb megoldást szeretnél, az egy kicsit bővebb téma. Főleg, hogy ne tudom a pontos célt és az elakadás okát.

Pal képe

Köszönöm, a megoldás a második bekezdésedben volt :)
Köszönöm a segítséget!

u.i: Nagyon profi amúgy a lapozó kód :)

geld77 képe

Tisztelt Rimelek!

Szeretnék segítséget kérni hogy az alábbi listázó kódot ilyen módon szeretném megjeleníteni:

1 - 10 l 11 - 20 l 21 - 30

Stb. stb.

Hogy lehet ezt megvalósítani?

Rimelek képe

Kedves geld77!

Csak ezt a sort kell módosítani:

print "<a href='?page=$i' style='$style'>[$i. oldal]</a>   ";

erre:

$end = $i*$limit;
$start = $end-$limit+1;
print "<a href='?page=$i' style='$style'>$start - $end</a> | ";

Illetve a ciklus elé lehet még tenni egy | karaktert, hogy az "Előző" link után is legyen.

geld77 képe

És azt, hogy lehet megoldani még ha pl csak 33 oldal van akkor az utolsó 31-33 jelenjen meg? Ne pedig 31 - 40 legyen?

Rimelek képe

Legegyszerűbben így:

$end = $i*$limit;
$start = $end-$limit+1;
$end = min($end, $c);

$c az összes elem száma. Az utolsó elem sorszáma az oldalon pedig kisebb vagy egyenlő lehet az összes elem számával. A min függvény pedig épp azt adja vissza.

geld77 képe

Nagyon szépen köszönöm a segítséget!

Tökéletes lett!

Anonymous képe

Szia,
Azt hogyan lehet megcsinálni, hogy különböző oldalaknak más-más a tartalmuk?
A MySQL-es megoldást használva?

Rimelek képe

Üdv. Ez a leírás egy lista lapozásáról szól. Neked az alábbi cikket javaslom:

Szép URL-ek htaccess-szel

Persze ez többről szól, mint ami neked kell. Már ha jól értettem a kérdést. Ha nem, akkor pontosítsd kérlek.

Anonymous képe

Szia,
Letöltöttem egy ingyenes hírrendszert, és ha híreket teszek közzé, akkor az mind egymás alá megy, de én azt akarom elérni, hogy pl 5 hírenként a következő oldalra írja híreket, ha ott is meglesz az 5, akkor a 3. oldalra és így tovább.

Rimelek képe

Biztos voltam benne, hogy erre már válaszoltam.

A lényeg, hogy nem ismerem a rendszert.Magának a lapozásnak a mysql-es megoldását viszont leírtam.

Yanoman képe

Köszi, nagyon jó a script:)