on Oct 23rd, 2008Paging kombinerat med Data Mapper Pattern
Jag och några andra programmerare disskuterade ett tag sen hur man bör implementera paging mot en arkitektur där man tillämpar Data mappers.
Lösningen vikom fram till presenterar jag nedan. Kort sagt fungerar det så att en Pager-klass tar ett Mapper-objekt, en limitering och ett startvärde på pagingen i konstruktorn. Pager-objektet anropar sedan mapper-objektet med de vart man vill börja hämta raderna och vart man vill börja.
Koden:
Interface IMapper
{
public function __construct($table, $primary_key);
public function fetchAll($offset, $limit);
}
Abstract Class Mapper Implements IMapper
{
public $table;
public $primary_key;
public function __construct($table, $primary_key)
{
$this->table = $table;
$this->primary_key = $primary_key;
}
}
/* pager.php */
/* Simple pager interface */
Interface IPager
{
public function fetchAll($page);
public function countRows();
public function printLinks($page);
}
/* The pager base-class */
Abstract Class Pager Implements IPager
{
public $mapper;
public $limit;
public function __construct(IMapper $mapper, $limit)
{
$this->mapper = $mapper;
$this->limit = $limit;
}
public function countRows()
{
$db = DB::getInstance();
$stmt = $db->prepare(‘SELECT COUNT(‘.$this->mapper->primary_key.‘) FROM ‘ . $this->mapper->table . ‘ LIMIT 1′);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result[‘COUNT(‘.$this->mapper->primary_key.‘)’];
}
public function fetchAll($page)
{
return $this->mapper->fetchAll(($page – 1) * $this->limit, $this->limit);
}
}
/* handler.php */
require_once "pager.php";
require_once "mapper.php";
Class User
{
public $id;
public $username;
}
Class UserMapper Extends Mapper
{
public function __construct($table, $primary_key)
{
parent::__construct($table, $primary_key);
}
public function fetchAll($offset, $limit)
{
$db = DB::getInstance();
$users = array();
$stmt = $db->prepare(‘SELECT * FROM ‘ . $this->table . ‘ ORDER BY ‘ . $this->primary_key . ‘ DESC’ . ‘ LIMIT ‘ . $offset . ‘,’ . $limit);
$stmt->execute();
foreach($stmt->fetchAll() as $row)
{
$user = new User;
$user->id = $row[‘user_id’];
$user->username = $row[‘user_name’];
$users[] = $user;
}
return $users;
}
}
Class UserPager Extends Pager
{
public function countRows()
{
return parent::countRows();
}
public function printLinks($page)
{
$links = $this->countRows();
$retval = ”;
for($i = 0; $i < $links / $this->limit; $i++)
{
$retval .= ‘<a href="?page=’.($i+1).‘"><li>’ . ($i + 1) . ‘</li></a>’;
}
return $retval;
}
}
$page = $_GET[‘page’];
$uMapper = new UserMapper(‘users’, ‘user_id’);
$uPager = new UserPager($uMapper, 4);
/* template */
<html>
<head>
</head>
<body>
<ul>
<?php echo $uPager->printLinks($page); ?>
</ul>
<table border=1>
<tr><th>Username</th></tr>
<?php foreach($uPager->fetchAll($page) as $user): ?>
<tr>
<td>
<?php echo $user->username; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>
Jag har tyvärr inte tid att gå in mer på djupet om hur det fungerar just nu, skulle det dock vara någon som vill det är det inget problem.
Värt att notera är även att alla inte var helt 100% med på den här lösningen, och som alltid finns det spridda meningar om hur man bör kombinera och använda olika patterns.
Hej!
Det ser mycket intressant ut, skulle du vilja lägga till lite kommentarer så man förstår lite bättre, hur det fungerar osv?
Annars så ser det jättebra ut!