paginering vind ik lastig. vooral met OOP. eerlijk gezegd snap ik er geen zak van.
er wordt altijd vertelt: laat het paginate-object de paginering afhandelen, om te zorgen voor flexibiliteit. maar paginering kan je toch eigenlijk niet zien als object? voor mij niets tastbaars. en de gegevens die je altijd moet invoeren, daardoor gaat al die flexibiliteit toch helemaal verloren?
als ik die van Roel hier zie, lijkt die me best netjes. maar het database gebeuren zit in die class, alle links, de hele shit. die flexibiliteit is dan toch weg, en is het meer een verzameling functies?
dus, hoe hoort een paginering in OOP eruit te zien?
Stel we hebben het over een forum met pagination. Dan hebben we 1 object die we Topic noemen die 1 topic vasthoudt. Vervolgens hebben we een TopicMapper klasse die de afhandeling tussen DB en Topic object regelt.
Dan hebben we nog een Pagination klasse die voor de pagination zorgt.
Dat is in elk geval hoe ik het zou doen. In je Topic klasse kun je alles kwijt van berichten totaan users... Met de DataMapper kun je alles makkelijk ophalen en met de Pagination kun je de pagination afhandelen, hoeveel er op 1 pagina mag enz.
Niet alle klassen die je in scripten gebruikt zijn objecten. Je hebt soms ook klassen nodig om je script mooi en flexibel te maken. Als ik zo'n bericht schrijf probeer ik daar altijd verschil in te maken, let maar eens op. Klassen die een echt tastbaar object zijn noem ik altijd object en andere noem ik gewoon klassen.
Een praktisch voorbeeldje hoe ik dit dan zou maken:
<?php
$db = new PDO(...);
$topicMapper = new TopicMapper($db);
// deze functie geeft een array met Topic objecten terug
$topics = $topicMapper->getAllByCategory('oop');
// Maak een pagination object
$pagination = new Pagination();
// een paar instellingen
$pagination->setPageCount(5);
// krijg de eerste pagina (in dit geval met 5 topics)
echo $pagination->getPage(1);
?>
Denk aan:
<?php
interface PaginatorItemInterface
{
pubilc funciton setData(array $data);
public function render();
}
interface PaginatorBackendInterface
{
/**
* @return array[PaginatorItemInterface]
*/
public function getItems($page);
}
interface PaginatorButtonFormInterface
{
public function render($page);
}
class Paginator
{
public function __construct(PaginatorBackendInterface $backend,
PaginatorButtonFormInterface $buttonForm)
{
$this->backend = $backend;
$this->buttonForm = $buttonForm;
}
public function render($page)
{
$items = $this->backend->getItems($page);
$result = '<div>'; // Hier kan je ook nog wel wat mooiers van maken
foreach($items as $item) {
$result .= $item->render();
}
$result .= $this->buttonForm->render($page);
$result .= '</div>';
return $result;
}
}
abstract class AbstractPaginatorPDOBackend implements PaginatorBackendInterface
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
}
class PaginatorLinkItem implements PaginatorItemInterface
{
proteced $name, $link;
public function setData(array $data)
{
$this->name = $data['name'];
$this->link = $data['link'];
}
public function render()
{
return '<a href="'.$this->link.'">'.$this->name.'</a>';
}
}
class PaginatorPDOLinkBackend extends AbstractPaginatorPDOBackend
{
public function getItems($page)
{
$stmt = $this->pdo->prepare($SQL);
$stmt->execute(array(':page'=>$page));
$items = array();
foreach($stmt->fetch() as $row) {
$item = new PaginatorLinkItem();
$item->setData($row);
$items[] = $item;
}
return $items;
}
}
?>
"Quick'n'Dirty", maar ik hoop dat je het idee een beetje snapt.
Een interface is een perfect middel om klassen te groeperen. Je kan er ook nog mooi vaststellen welke functies er verplicht in moeten.
Met implements kun je een klasse aan de interface koppelen.
Bijv:
<?php
/**
* Interface om storage klassen te groeperen
*/
interface Storage
{
/**
* Functie om een value op te slaan
*
* @param string $key De key naar de value
* @param mixed $value De value
*/
public function set($key, $value);
/**
* Functie om een value te krijgen
*
* @param string $key De key naar de value
*
* @return mixed The value
*/
public function get($key);
}
/**
* Class om session storage af te handelen
*/
class SessionStorage implements Storage
{
public function __construct()
{
session_start();
}
/**
* {@inheritdoc}
*/
public function set($key, $value)
{
$_SESSION[$key] = $value;
}
/**
* {@inheritdoc}
*/
public function get($key)
{
if (!isset($_SESSION[$key])) {
throw new InvalidArgumentException($key.' does not exists in SessionStorage');
}
return $_SESSION[$key];
}
}
class CookieStorage implements Storage
{
// ...
}
?>
Jeroen, het kan ook zonder het commentaar. Het voordeel hiervan is dat je makkelijk iets kunt controleren. Bijv. in mijn storage interface. Ik heb een User klasse en die slaat in de session op of een User ingelogd is dan kun je nu makkelijk controleren:
<?php
class User
{
public function __construct(Storage $storage)
{
// het maakt me geen moer uit of dit in een session
// cookie, db of bestand wordt opgeslagen als ik maar
// een klasse heb die een set en get method heeft.
$storage->set('user.loggedin', true);
}
}
?>
ja opzich wel, al ontgaat de logica mij een beetje. regel 74, waar komt $SQL vandaan?
ook uit de render functies kom ik niet zo uit. er zijn er twee, en de tweede returned niets, maar lijkt me van wel?
>> regel 74, waar komt $SQL vandaan?
Pim heeft denk ik dit even snel getypt voor het idee, maar niet om zo te gebruiken. $SQL moet je zelf invullen.
Her en der staan ook wat typos.
Verder zie ik geen 1 render functie die iets niks retourneert?