Simpel, alsidig, objekt-orienteret pagination (paging) i PHP

Relaterede dokumenter
Listen over reserverede ord er meget lang, men de væsentligste vil jeg beskrive her i denne artikel:

At klippe en streng over på det mest hensigtsmæssige sted

Database for udviklere. Jan Lund Madsen PBS10107

PHP Pagination. Denne guide er oprindeligt udgivet på Eksperten.dk. Skrevet den 01. May 2011 af dab93 I kategorien Programmering / Andre

Tagwall med Php & MySQL

Anvendelse af metoder - Programmering

Introduktion til SQL queries

Ratingsystem i PHP og MySQL

Løsning af møntproblemet

Database design for begyndere

Gæstebog med validering opbygget med MySQL

Introduktion til funktioner, moduler og scopes i Python

I mit script tager jeg højde for det problem ved, at gemme et unikt tal mellem 0-9 på 6 cifre og derved vil de så blive vist som 2 online.

MySQL C API. Denne artikel beskriver hvordan man bruger MySQL C API. Der er beskrivelse af build med forskellige compilere.

I denne artikel vil vi bruge en User klasse som vi så vil gruppere på forskellige måder.

I denne arktikle går jeg gennem Slet, Ret og Opret data i en MySQL database. der er også en lille del i den hvor den postere datanen ud i en løkke

Loginsystem (med MySQL)

Singleton pattern i C#

Dynamisk PHP design OPDATERET

Indholdsfortegnelse Databaser og PHP... 3 Opgave... 4 Opgave... 5 Opgave... 6 Sidste opgave er en lille gæstebog... 7 Kilder og nyttige links:...

Som sagt kræves der helst lidt viden om OOP hvis man virkelig vil lærer noget, og ikke bare lave copypaste

I denne artikel, vil der blive gennemgået de grundlæggende PHP-funktioner, såsom udskrift til skærmen, tid og dato og if-sætningen.

Arkitektur for begyndere

Hvad er Objekter - Programmering

Bemærk! Et PHP script har kun brug for at forbinde én gang til databaseserveren. Det kan så sagtens udføre flere kommandoer vha. denne forbindelse.

PHP Snippets. De små korte. Skrevet af Daniel Pedersen

Kontrol-strukturer i PHP

Databaseadgang fra Java

DANMARKS TEKNISKE UNIVERSITET

Skriftlig eksamen i Datalogi

Den forudsætter kendskab til C++ og lidt kendskab til SQL og MySQL C API.

PHP 3 UGERS FORLØB PHP, MYSQL & SQL

Eksamen, DSDS, efterår 2007

Samspillet mellem databaser og kort styres af GeoCAD programmet GeoDB.

Skriftlig eksamen i Datalogi

Terese B. Thomsen 1.semester Formidling, projektarbejde og webdesign ITU DMD d. 02/

Prepared Statements. Denne artikel beskriver hvorfor prepared statements er gode. Den forudsætter lidt kendskab til Java og JDBC.

//Udskriver System.out.println("Hej " + ditfornavn + " " + ditefternavn + "."); System.out.println("Du er " + dinalder + " aar gammel!

Løsning af skyline-problemet

dmasark Aflevering - Uge 50

PROJEKT 3. The Design Diaries. LINK TIL BLOG: Af Mikkel Borg Svendsen & Sebastian Frank MUL B

Undtagelseshåndtering i C#

Reeksamen, DSDS, forår 2008

Denne artikel er til dem der ønsker at vide mere om hvad CSS er og hvad CSS kan bruges til hvad angår WWW.

RMI introduktion. Denne artikel beskriver Java RMI (Remtote Method Invocation).

Øvelse 9. Klasser, objekter og sql-tabeller insert code here

Arrays i PHP. Denne guide er oprindeligt udgivet på Eksperten.dk. Skrevet den 04. Feb 2009 af taskmgr I kategorien Programmering / PHP

Databasesystemer. Databaser, efterår Troels Andreasen. Efterår 2002

Log ind med PHP. Denne guide er oprindeligt udgivet på Eksperten.dk. Skrevet den 09. May 2011 af dab93 I kategorien Programmering / Andre

I denne artikel vil jeg gennemgå hvordan en side for RSS "Live Bogmærke" kan se ud.

Dannelse af PDF-dokumenter

Loginsystem med PHP4, klasser, sessions og MySQL database

Målet for disse slides er at diskutere nogle metoder til at gemme og hente data effektivt.

Singleton pattern i Java

SWC Elementer i klassedefinition

Skrevet den 18. Feb 2010 af arne_v I kategorien Programmering / Visual Basic.NET

METODER ARV KLASSER. Grundlæggende programmering Lektion 5

b) Udvid din implementation af forme til at understøtte.equals. To objekter af samme form er ens hvis de har samme værdier i felterne.

Ugeseddel 4 1. marts - 8. marts

Derfor vil jeg bygge dette eksempel på een table hvor der kan tilkyttes personer til ALLE noder og der kan tilføjes et vilkårligt antal niveauer

Lær Python - Dag 4, modul 2 Objektorienteret programmering

Aftenskole i programmering sæson Flere registreringer. Sæson 2 - Lektion 8

Denne artikel gennemgår kort nogle mulighederne for brug af XML i ASP. Det sker ved brug af eksempler. Eksemplerne vil være i VBS.

Umbraco installationsvejledning

Med register_globals = On får du automatisk adgang til en række variabelnavne i dit script.

Uploade billeder eller andre filer ved hjælp af php og mysql

PID2000 Archive Service

HTML, PHP, SQL, webserver, hvad er hvad??

Introduktion til OPC Access

Nintex Workflow UK/DK

Begrænsninger i SQL. Databaser, efterår Troels Andreasen

The Design Diaries. Link til blog

Greenfoot En kort introduktion til Programmering og Objekt-Orientering

Endnu mere om tilfældige tal

Introduction til.net remoting i C#

applikation----x----odbc driver manager----foobar ODBC driver----foobar database

SQL for MySQL-begyndere

POST IT! Cph Business Academy Multimediedesign 2. Semester flow april Kirstine Marie Rasmussen cph-

Dannelse af PDF dokumenter

Parameters. Denne artikel beskriver hvorfor parameters er gode. Den forudsætter lidt kendskab til C# og ADO.NET.

Send fra Java. Denne artikel fortæller hvad man skal bruge og hvordan man skal kode for at sende fra Java.

Introduktion til redigeringsfaciliteterne

Grundlæggende Programmering ITU, Efterår Skriftlig eksamen i Grundlæggende Programmering

JSP, Tomcat. Tutorial lavet af Jákup W. Hansen TSU semester 10.october 2007

DM507 Algoritmer og datastrukturer

Tree klassen fra sidste forelæsning

Videregående Programmering Obligatorisk opgave - 3. semester, efterår 2004

It og informationssøgning Forelæsning oktober 2006 Jakob Grue Simonsen. Klasser

AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007

Klasser og Objekter i Python. Uge 46 Learning Python: kap 15-16,

De vigtigste SQL-sætninger. SQL kap Oprette database. DDL og DML

class subklasse-navn extends superklasse-navn { } NorwaySpruce har superklassen Spruce, som igen har superklassen Tree.

Eksempel på en database: studenter, kurser, eksamener

Forelæsning Uge 2 Torsdag

Algoritmeskabeloner: Sweep- og søgealgoritmer C#-version

Begrynder til at lave log ind system

Introduction til.net remoting i VB.NET

Mozilla Firefox (tidligere Firebird): Fremhæve ord

A11: Last Year s Exam

Lektion 6. Grundlæggende programmering i VR

Transkript:

Denne guide er oprindeligt udgivet på Eksperten.dk Simpel, alsidig, objekt-orienteret pagination (paging) i PHP Der findes masser af bud på, hvordan man kan lave paging (pagination) i PHP. Nogle løsninger er håbløse, andre er for specifikke - og de sidste er som regel for komplekse! I det følgende præsenteres en simpel OO-tilgang, der kan håndtere de fleste behov. Skrevet den 03. Feb 2009 af jensgram I kategorien Programmering / PHP [ Introduktion ] I det følgende behandles emnet "paging", sideopdeling eller "pagination" (jeg benytter sidstnævnte betegnelse): Det vil sige det problem der opstår, når data fra en database skal udtrækkes i mindre portioner og serveres på individuelle sider - som eksempelvis artiklerne her på Eksperten. I og for sig er logikken ikke ret kompleks. Det store problem er derimod, at man ofte finder sig selv i færd med at kopiere store stykker kode fra pagination af gæstebogen til brugerlisten til kommentarerne til... Formålet med denne artikel er at gennemgå tankerne bag en simpel abstrakt PHP-klasse, der kan tage sig af alt det kedelige så du blot skal beskrive, hvordan de enkelte elementer skal renderes. (Kode følger løbende - de nysgerrige kan scrolle til bunden, hvor den samlede klasse forefindes.). Hermed rustes læseren til at tage det sidste skridt og implementere løsningen på site eget site. Artiklen kan ved første øjekast synes lang, hvilket skyldes, at kode og forklaring følges ad. Kan du undvære forklaringen, så skimlæs og hæft dig ved den samlede kode nær artiklens bund. (Det skal i parentes bemærkes, at jeg er klar over, at der på Eksperten findes mindst to andre artikler om dette emne. At dømme ud fra kritikken, lader det imidlertid ikke til at kunne skade med et nyt perspektiv.) [ De overordnede problemstillinger ] Jeg vil for det følgende angribe pagination-problematikken som to sammenhængende udfordringer: 1. Håndtering af dataudtræk fra databasen (heri benyttes MySQL; arbejder du med en anden database vil du sikkert let kunne tilpasse koden), samt opdelingen i sider 2. Problemet, der opstår ved store kollektioner, der skaber mange sider og derved en uoverskuelig navigation. Sidstnævnte er i nogen grad en outsider (ihvertfald i forhold til den gængse artikel om pagination), så for at sikre fællesforståelse med læseren, tillader jeg mig at uddybe: Antag at vi har 1.800 poster i databasen og vil præsentere ti ad gangen. Den simple løsning er at liste 180 links med siderne 1-180. Meget uoverskueligt og derved komplet uanvendeligt. I stedet har det været et krav til den til artiklen udviklede løsning, at sidenavigationen ville se ud à la *1* [2] [3]... [180], hvor asterisk indikerer den aktuelle side, mens kantede parenteser repræsenterer sidelinks. Ellipsen (...) er bogstavelig. Befinder vi os på side 65 (posterne 650-9) skal navigationen antage formen [1]... [63] [64] *65* [66] [67]... [180] etc.

[ En objekt-orienteret tilgang ] Den objekt-orienterede tilgang afspejler den måde, hvorpå mennesket ser verden. Som introduktion til OO et det oplagt (og udbredt) at lave eksempler på nedarvningshierarkier, tilstand og funktionalitet på fænomener og begreber fra hverdagen, e.g. køretøjer, cykler og biler. Nogle gange er vi imidlertid tvunget til at modellere begreber, der ikke kan hentes fra omgivelserne, men som snarere er udtryk for en formaliseringsproces fra udviklerens side. I dette tilfælde eftersøger vi et begreb, der kan konkretiseres til noget, derkan håndtere pagination - resultat: Vi modellerer og udvikler en "Paginator": <?php abstract class Paginator { abstract protected function renderitem(&$row);?> Som omtalt ovenfor, kan stort set hele logikken bag pagination genbruges på tværs af kontekster. Faktisk er det kun et spørgsmål om at implementere en konkret renderingsmekanisme til hver af disse. Det kunne være en kommentar-rendering, gæstebogsindlæg-rendering etc. Som følge af denne betragtning har jeg valgt, at Paginator-klassen skal være abstrakt, idet metoden renderitem() skal realiseres til hver kontekst. Jeg skal senere vende tilbage til denne metode. En Paginator skal i al sin enkelthed kunne håndtere følgende scenarie: 1. Find det totale antal af elementer, der skal gøres til genstand for pagination 2. Udregn det samlede antal sider = total / elementer-per-side 3. Sæt aktuel side som intern tilstand. Er ingen side angivet, antages det, at vi befinder os på første side (som internt er side nul). 4. Tilbyd rendering af den aktuelle sides elementer. 5. Tilbyd rendering af relevante navigationsmuligheder, jf. pkt. 2 i foregående afsnit. Som det altid gør sig gældende, er der behov for tilstand og funktionalitet, så lad os først tilføje de nødvendige metodesignaturer: <?php abstract class Paginator { public function renderitems() {... public function rendernavigation() {... abstract protected function renderitem(&$row);?>

[ Den interne tilstand ] Hvert objekt af typen Paginator skal kende sin interne tilstand. Denne er givet gennem følgende attributter: * tables: En streng med den eller de tabeller (forespørgslen, der udtrækker data fra databasen er ikke begrænset til én tabel), hvorfra data hentes. Denne værdi sættes ved instantiering. * fields: En streng med de felter, der ønskes udtrukket. Denne værdi sættes ved instantiering. * where: En streng med betingelsen for udtrækket (uden LIMIT - den del håndteres internt). Denne værdi sættes ved instantiering. * orderby: En streng med rækkernes sorteringsrækkefølge. Denne værdi sættes ved instantiering. * cntperpage: Antallet af elementer pr. side. Denne værdi sættes ved instantiering. * key: GET-nøgle til at forespørge side. Som udgangspunkt benyttes "page" således at URL'erne i navigationen indeholder "?page=n". Attributten er kun medtaget for at muliggøre simpel tilpasning. Denne værdi kan sættes ved instantiering. * page: Objektets aktuelle side. Sættes internt baseret på GET-værdien for ovennævnte key og vil altid være i intervallet [0; cntpages[. * cntpages: Det samlede antal af sider. Beregnes internt som antallet elementer divideret med elementer pr. side. Disse føjes til skelettet: <?php abstract class Paginator { private $tables; // (string) private $fields; // (string) private $where; // (string) private $orderby; // (string) private $cntperpage; // (int) Items per page private $key; // (string) The query string key for page navigation private $page; // (int) Current page private $cntpages; // (int) Number of pages public function renderitems() {... public function rendernavigation() {... abstract protected function renderitem(&$row);

?> [ Fælles funktionalitet ] Først og fremmest må vi have en konstruktør, hvormed vi kan instantiere et nyt objekt af typen Paginator. (Konkret kan det naturligvis først gøres når renderitem() er blevet realiseret, men det er en anden sag. Vi kommer dertil om et øjeblik.). Denne laver blot lidt simpel argumenthåndtering: public function construct($tables, $fields, $where, $orderby, $cntperpage, $key = 'page') { if (intval($cntperpage) < 1) { die('illegal argument: cntperpage must be greater than zero.'); $this->tables = $tables; $this->fields = $fields; $this->where = empty($where)? '1=1' : $where; $this->orderby = $orderby; $this->cntperpage = intval($cntperpage); $this->key = $key; // Perform calculations to set cntpages and page... $sql = sprintf('select COUNT(*) FROM %s WHERE %s', $this->tables, $this->where); $res = mysql_query($sql); $cnt = mysql_result($res, 0); $this->cntpages = ceil($cnt / $this->cntperpage); $this->page = (isset($_get[$this->key]) && intval($_get[$this->key]) > 0? intval($_get[$this->key]) - 1 : 0); //?page=1 gives $this->page = 0 $this->page = max(0, min($this->page, $this->cntpages - 1)); // Never accept values greater than the number of pages Ingen magi hér! Konstruktøren sørger for, at den initielle tilstand sættes, hvorefter det udregnes, hvor mange sider der skal benyttes, samt hvilken side der forespørges (i.e. der tildeles værdier til cntpages og page). Bemærk, at side 1 repræsenteres som $this->page = 0. renderitem() er erklæret abstrakt, så vi kan være sikre på, at metoden er realiseret (implementeret) inden et konkret Paginator-objekt kan oprettes. Idéen hermed er, at renderitems() (bemærk "s" - der tales hér om den offentlige metoder, der sørger for at renderer den aktuelle sides elementer) blot kan kalde renderitem() for hver række fra databasen, der skal være en del af den aktuelle side: public function renderitems() { $out = '';

$sql = sprintf('select %s FROM %s WHERE %s ORDER BY %s LIMIT %d, %d', $this->fields, $this->tables, $this->where, $this->orderby, $this->page * $this->cntperpage, $this->cntperpage); $res = mysql_query($sql); while ($row = mysql_fetch_assoc($res)) { $out.= $this->renderitem($row); // Calling the concrete implementation for each row return $out; [ Navigationen ] Ovenstående er nu et fungerende skelet, der i sig selv kan håndtere pagination (når man har realiseret renderitem() - eksempel følger i næste afsnit). Det er imidlertid ikke meget ved det, førend man også har mulighed for at navigere mellem siderne. Som allerede omtalt, ønsker jeg at lave et system, der kan håndtere endda meget store kollektioner, i.e. mange sider. Desuden ønsker jeg at kunne styre udseendet meget præcist, så hvert element skal forsynes med relevante CSS-klasser: * seqfirst: Første side * seqlast: Sidste side * seqcurrent: Den aktuelle side - side 1 som udgangspunkt * seqbefore: Sider før den aktuelle * seqafter: Sider efter den aktuelle * seqgap: Udeladte sider (ellipse) Eksemplet fra tidligere - [1]... [63] [64] *65* [66] [67]... [180] - ønskes renderet som følger: <ul class="paginatornavigation"> <li class="seqfirst seqbefore"><a href="">1</a></li> <li class="seqbefore seqgap">...</li> <li class="seqbefore"><a href="?page=63">63</a></li> <li class="seqbefore"><a href="?page=64">64</a></li> <li class="seqcurrent">65</li> <li class="seqafter"><a href="?page=66">66</a></li> <li class="seqafter"><a href="?page=67">67</a></li> <li class="seqafter seqgap">...</li> <li class="seqlast seqafter"><a href="?page=180">180</a></li> </ul> Og så et lille intermezzo: Da ingen sideangivelse svarer til at få serveret side 1, ønsker jeg ikke, at der

bliver genereret et link til "?page=1". Desuden skal Paginator være let at bruge, så jeg må på en måde gøre det muligt for folk at kunne håndtere selve genereringen af sidelinkenes URL'er. Det kan eksempelvis være, at man i ét specifikt tilfælde vil angive sidetallet på det, jeg kalder "mod_rewrite"-vis: http://.../gaestebog/65 i stedet for som GET-parameter. Man kan ikke tage hensyn til alle typer scenarier, så i stedet overlader jeg muligheden til brugeren ved at lave en metode - generatelinkurl() - der kan overskrives, hvis man ønsker mere fleksibilitet end blot at kunne omdøbe GET-parametren (jf. attributten key): protected function generatelinkurl($linkprefix, $key, $page) { $var = (strpos($linkprefix, '?') === false? '?' : '&'). $key. '='. $page; return $linkprefix. ($page > 1? $var : ''); // Don't add page data if link goes to first page Argumenterne turde være selvforklarende. De to sidste kunne have været undværet, da implementøren blot kan tilgå $this->key hhv. $this->page. Personligt foretrækker jeg dog denne afkobling. Jeg vil ikke gå i dybden med hver linie i rendernavigation(), da jeg i nogen grad finder koden selvforklarende. I store træk går det ud på at iterere over alle siderne (1 - cntpages) og påklistre de korrekte klasser, jf. ovenstående liste. Der er imidlertid ingen grund til at gennemgå alle sider, så jeg springer over hvor jeg kan (i.e. hvor sider er udeladt). Metoden er forsynet med to parametre: rendernavigation($linkprefix = '', $cntaround = 2). Det første videresendes til generatelinkurl() og kan benyttes til at angive sidelinkenes udgangspunkt. Andet argument er antallet af sidelinks direkte omkring den aktuelle side. Sættes den til 1 bliver det løbende eksempel til [1]... [64] *65* [66]... [180], sættes den til 0 fås [1]... *65*... [180]. Negative værdier tolkes som et fravalg af udeladte sider, hvilket betyder, at der genereres sidelinks til alle sider. (Det var netop det vi ville undgå, men det kan jo være, at du er af en anden opfattelse, så nu har du muligheden. Jeg gengiver forresten ikke eksemplet hér!) Da denne metode er den sidste, ser jeg ingen grund til at vise den for sig. I stedet præsenterer jeg - tadaaaah... [ Den samlede klasse ] Vejen hertil var lang, men her er det du kom for: <?php abstract class Paginator { private $tables; // (string) private $fields; // (string) private $where; // (string) private $orderby; // (string) private $cntperpage; // (int) Items per page private $key; // (string) The query string key for page navigation

private $page; // (int) Current page private $cntpages; // (int) Number of pages public function construct($tables, $fields, $where, $orderby, $cntperpage, $key = 'page') { if (intval($cntperpage) < 1) { die('illegal argument: cntperpage must be greater than zero.'); $this->tables = $tables; $this->fields = $fields; $this->where = empty($where)? '1=1' : $where; $this->orderby = $orderby; $this->cntperpage = intval($cntperpage); $this->key = $key; // Perform calculations to set cntpages and page... $sql = sprintf('select COUNT(*) FROM %s WHERE %s', $this->tables, $this->where); $res = mysql_query($sql); $cnt = mysql_result($res, 0); $this->cntpages = ceil($cnt / $this->cntperpage); $this->page = (isset($_get[$this->key]) && intval($_get[$this->key]) > 0? intval($_get[$this->key]) - 1 : 0); //?page=1 gives $this->page = 0 $this->page = max(0, min($this->page, $this->cntpages - 1)); // Never accept values greater than the number of pages public function renderitems() { $out = ''; $sql = sprintf('select %s FROM %s WHERE %s ORDER BY %s LIMIT %d, %d', $this->fields, $this->tables, $this->where, $this->orderby, $this->page * $this->cntperpage, $this->cntperpage); $res = mysql_query($sql); while ($row = mysql_fetch_assoc($res)) { $out.= $this->renderitem($row); // Calling the concrete implementation for each row return $out; public function rendernavigation($linkprefix = '', $cntaround = 1) { $out = ''; $isgap = false; $classes = ' '; for ($i = 0; $i < $this->cntpages; $i++) { $isgap = false; $classes = ''; $classes.= ($i == 0? 'seqfirst ' : ''); $classes.= ($i == $this->cntpages - 1? 'seqlast ' : ''); $classes.= ($i == $this->page? 'seqcurrent ' : '');

$classes.= ($i < $this->page? 'seqbefore ' : ''); $classes.= ($i > $this->page? 'seqafter ' : ''); // Are we at a gap? if ($cntaround >= 0 && $i > 0 && $i < $this->cntpages - 1 && abs($i - $this->page) > $cntaround) { $isgap = true; $classes.= 'seqgap '; // Skip to next linked item (or last if we've already run past the current page) $i = ($i < $this->page? $this->page - $cntaround : $this->cntpages - 1) - 1; $lnk = ($isgap? '...' : ($i + 1)); if ($i!= $this->page &&!$isgap) { $lnk = '<a href="'. $this->generatelinkurl($linkprefix, $this->key, $i + 1). '">'. $lnk. '</a>'; $out.= "\t<li class=\"". trim($classes). '">'. $lnk. "</li>\n"; return "<ul class=\"paginatornavigation\">\n". $out. '</ul>'; protected function generatelinkurl($linkprefix, $key, $page) { $var = (strpos($linkprefix, '?') === false? '?' : '&'). $key. '='. $page; return $linkprefix. ($page > 1? $var : ''); // Don't add page data if link goes to first page abstract protected function renderitem(&$row);?> [ Brugseksempel ] Du er nu veludrustet (!) til at realisere din egen Paginator. Jeg har gjort således: class PaginatedGuestbook extends Paginator { protected function renderitem(&$row) { $o = "\nindlæg fra ". $row['navn']. "<br />\n". "Overskrift: <em>". $row['overskrift']. "</em><br />\n"

. "Indhold: ". $row['indhold']. "<br />\n"; // Her kunne man udskrive flere data fra rækken ($row) return $o; $gb = new PaginatedGuestbook('gaestebog', 'tidspunkt, navn, overskrift, indhold', 'hidden=0 AND deleted=0', 'tidspunkt DESC', 5); print $gb->renderitems(); print "<br />\n\n"; print $gb->rendernavigation('minside.php'); Husk, at der næsten ikke er noget overarbejde. Du skulle alligevel have skrevet renderingskode. Eneste forskel er blot, at du nu skal pakke det ind i en på forhånd navngivet metode. Resten får du forærende! Simpelt? Anvendeligt? Jæs! Det er nu du begynder at vinde pengene tilbage! [ Afslutning ] Jeg håber, at du som læser har fået noget ud af artiklen. Den bliver noget længere end planlagt, men jeg håber så, at der måske har sneget sig et par fornuftige ord om objekt-orientering ind. Kopiér koden og se, om ikke den kan bruges i dit unikke tilfælde. Kunne du godt tænke dig at se, hvordan pagination-klassen opfører sig, så kig forbi http://www.jensgram.dk/sjov/gaestebog/ (og skriv gerne en hilsen). Den benyttede CSS finder du på http://www.jensgram.dk/styles/_core.css (der skal ledes lidt). Eventuelle fjel og wrangler bekagles. Med venlig hilsen - Jens Gram, http://www.jensgram.dk/ PS: På http://www.jensgram.dk/web/objects/ kan du finde klassen JgPaginator, der er lige til at gå til uden copy-paste. PPS: Formateringsmulighederne på Eksperten lader stadig noget tilbage at ønske. På http://www.jensgram.dk/web/e-artikler/1265 fremstår artiklen som jeg havde tiltænkt. Change log: 2009/01/13: Første version publiceret 2009/01/14: Beskrivelse af negativ cntaround-argument til rendernavigation 2009/03/07: Reformateret til E5 (ingen rettelser) Kommentar af superb d. 30. Jan 2009 1 nu jeg ret ny i OOP, hvorfor er det du siger $this->?

hvad gør "function construct" og hvad betyder det at noget er "erklæret abstrakt"? Kommentar af zurekk d. 23. Jan 2009 2