Using zend-paginator in your Album Module

TODO

Update to:

  • follow the changes in the user-guide
  • use SQLite-compatible SQL syntax, and provide a script for inserting the data

In this tutorial, we will use the zend-paginator component to add a handy pagination controller to the bottom of the album list.

Currently, we only have a handful of albums to display, so showing everything on one page is not a problem. However, how will the album list look when we have 100 albums or more in our database? The standard solution to this problem is to split the data up into a number of pages, and allow the user to navigate around these pages using a pagination control. Just type "Zend Framework" into Google, and you can see their pagination control at the bottom of the page:

Preparation

In order for us to have lots of albums in our database, you'll need to run the following SQL insert statement to insert the current 150 top iTunes albums (at the time of writing!):

INSERT INTO "album" ("artist", "title")
VALUES
("David Bowie", "The Next Day (Deluxe Version)"),
("Bastille", "Bad Blood"),
("Bruno Mars", "Unorthodox Jukebox"),
("Emeli Sandé", "Our Version of Events (Special Edition)"),
("Bon Jovi", "What About Now (Deluxe Version)"),
("Justin Timberlake", "The 20/20 Experience (Deluxe Version)"),
("Bastille", "Bad Blood (The Extended Cut)"),
("P!nk", "The Truth About Love"),
("Sound City - Real to Reel", "Sound City - Real to Reel"),
("Jake Bugg", "Jake Bugg"),
("Various Artists", "The Trevor Nelson Collection"),
("David Bowie", "The Next Day"),
("Mumford & Sons", "Babel"),
("The Lumineers", "The Lumineers"),
("Various Artists", "Get Ur Freak On - R&B Anthems"),
("The 1975", "Music For Cars EP"),
("Various Artists", "Saturday Night Club Classics - Ministry of Sound"),
("Hurts", "Exile (Deluxe)"),
("Various Artists", "Mixmag - The Greatest Dance Tracks of All Time"),
("Ben Howard", "Every Kingdom"),
("Stereophonics", "Graffiti On the Train"),
("The Script", "#3"),
("Stornoway", "Tales from Terra Firma"),
("David Bowie", "Hunky Dory (Remastered)"),
("Worship Central", "Let It Be Known (Live)"),
("Ellie Goulding", "Halcyon"),
("Various Artists", "Dermot O'Leary Presents the Saturday Sessions 2013"),
("Stereophonics", "Graffiti On the Train (Deluxe Version)"),
("Dido", "Girl Who Got Away (Deluxe)"),
("Hurts", "Exile"),
("Bruno Mars", "Doo-Wops & Hooligans"),
("Calvin Harris", "18 Months"),
("Olly Murs", "Right Place Right Time"),
("Alt-J (?)", "An Awesome Wave"),
("One Direction", "Take Me Home"),
("Various Artists", "Pop Stars"),
("Various Artists", "Now That's What I Call Music! 83"),
("John Grant", "Pale Green Ghosts"),
("Paloma Faith", "Fall to Grace"),
("Laura Mvula", "Sing To the Moon (Deluxe)"),
("Duke Dumont", "Need U (100%) [feat. A*M*E] - EP"),
("Watsky", "Cardboard Castles"),
("Blondie", "Blondie: Greatest Hits"),
("Foals", "Holy Fire"),
("Maroon 5", "Overexposed"),
("Bastille", "Pompeii (Remixes) - EP"),
("Imagine Dragons", "Hear Me - EP"),
("Various Artists", "100 Hits: 80s Classics"),
("Various Artists", "Les Misérables (Highlights From the Motion Picture Soundtrack)"),
("Mumford & Sons", "Sigh No More"),
("Frank Ocean", "Channel ORANGE"),
("Bon Jovi", "What About Now"),
("Various Artists", "BRIT Awards 2013"),
("Taylor Swift", "Red"),
("Fleetwood Mac", "Fleetwood Mac: Greatest Hits"),
("David Guetta", "Nothing But the Beat Ultimate"),
("Various Artists", "Clubbers Guide 2013 (Mixed By Danny Howard) - Ministry of Sound"),
("David Bowie", "Best of Bowie"),
("Laura Mvula", "Sing To the Moon"),
("ADELE", "21"),
("Of Monsters and Men", "My Head Is an Animal"),
("Rihanna", "Unapologetic"),
("Various Artists", "BBC Radio 1's Live Lounge - 2012"),
("Avicii & Nicky Romero", "I Could Be the One (Avicii vs. Nicky Romero)"),
("The Streets", "A Grand Don't Come for Free"),
("Tim McGraw", "Two Lanes of Freedom"),
("Foo Fighters", "Foo Fighters: Greatest Hits"),
("Various Artists", "Now That's What I Call Running!"),
("Swedish House Mafia", "Until Now"),
("The xx", "Coexist"),
("Five", "Five: Greatest Hits"),
("Jimi Hendrix", "People, Hell & Angels"),
("Biffy Clyro", "Opposites (Deluxe)"),
("The Smiths", "The Sound of the Smiths"),
("The Saturdays", "What About Us - EP"),
("Fleetwood Mac", "Rumours"),
("Various Artists", "The Big Reunion"),
("Various Artists", "Anthems 90s - Ministry of Sound"),
("The Vaccines", "Come of Age"),
("Nicole Scherzinger", "Boomerang (Remixes) - EP"),
("Bob Marley", "Legend (Bonus Track Version)"),
("Josh Groban", "All That Echoes"),
("Blue", "Best of Blue"),
("Ed Sheeran", "+"),
("Olly Murs", "In Case You Didn't Know (Deluxe Edition)"),
("Macklemore & Ryan Lewis", "The Heist (Deluxe Edition)"),
("Various Artists", "Defected Presents Most Rated Miami 2013"),
("Gorgon City", "Real EP"),
("Mumford & Sons", "Babel (Deluxe Version)"),
("Various Artists", "The Music of Nashville: Season 1, Vol. 1 (Original Soundtrack)"),
("Various Artists", "The Twilight Saga: Breaking Dawn, Pt. 2 (Original Motion Picture Soundtrack)"),
("Various Artists", "Mum - The Ultimate Mothers Day Collection"),
("One Direction", "Up All Night"),
("Bon Jovi", "Bon Jovi Greatest Hits"),
("Agnetha Fältskog", "A"),
("Fun.", "Some Nights"),
("Justin Bieber", "Believe Acoustic"),
("Atoms for Peace", "Amok"),
("Justin Timberlake", "Justified"),
("Passenger", "All the Little Lights"),
("Kodaline", "The High Hopes EP"),
("Lana Del Rey", "Born to Die"),
("JAY Z & Kanye West", "Watch the Throne (Deluxe Version)"),
("Biffy Clyro", "Opposites"),
("Various Artists", "Return of the 90s"),
("Gabrielle Aplin", "Please Don't Say You Love Me - EP"),
("Various Artists", "100 Hits - Driving Rock"),
("Jimi Hendrix", "Experience Hendrix - The Best of Jimi Hendrix"),
("Various Artists", "The Workout Mix 2013"),
("The 1975", "Sex"),
("Chase & Status", "No More Idols"),
("Rihanna", "Unapologetic (Deluxe Version)"),
("The Killers", "Battle Born"),
("Olly Murs", "Right Place Right Time (Deluxe Edition)"),
("A$AP Rocky", "LONG.LIVE.A$AP (Deluxe Version)"),
("Various Artists", "Cooking Songs"),
("Haim", "Forever - EP"),
("Lianne La Havas", "Is Your Love Big Enough?"),
("Michael Bublé", "To Be Loved"),
("Daughter", "If You Leave"),
("The xx", "xx"),
("Eminem", "Curtain Call"),
("Kendrick Lamar", "good kid, m.A.A.d city (Deluxe)"),
("Disclosure", "The Face - EP"),
("Palma Violets", "180"),
("Cody Simpson", "Paradise"),
("Ed Sheeran", "+ (Deluxe Version)"),
("Michael Bublé", "Crazy Love (Hollywood Edition)"),
("Bon Jovi", "Bon Jovi Greatest Hits - The Ultimate Collection"),
("Rita Ora", "Ora"),
("g33k", "Spabby"),
("Various Artists", "Annie Mac Presents 2012"),
("David Bowie", "The Platinum Collection"),
("Bridgit Mendler", "Ready or Not (Remixes) - EP"),
("Dido", "Girl Who Got Away"),
("Various Artists", "Now That's What I Call Disney"),
("The 1975", "Facedown - EP"),
("Kodaline", "The Kodaline - EP"),
("Various Artists", "100 Hits: Super 70s"),
("Fred V & Grafix", "Goggles - EP"),
("Biffy Clyro", "Only Revolutions (Deluxe Version)"),
("Train", "California 37"),
("Ben Howard", "Every Kingdom (Deluxe Edition)"),
("Various Artists", "Motown Anthems"),
("Courteeners", "ANNA"),
("Johnny Marr", "The Messenger"),
("Rodriguez", "Searching for Sugar Man"),
("Jessie Ware", "Devotion"),
("Bruno Mars", "Unorthodox Jukebox"),
("Various Artists", "Call the Midwife (Music From the TV Series)"
);

Populating the database

You have a number of options for populating the database, based on your operating system and installed applications.

First, copy the above into the file data/bulk_albums.sql.

Next, if you have the sqlite or sqlite3 command installed, run:

$ sqlite data/zftutorial.db < data/bulk_albums.sql

(If you have sqlite3 on your system, use that, as sqlite typically refers to the SQLite v2 binary.)

If you do not, you can use PHP. Create a new file,data/load_bulk_albums.php, with the following contents:

<?php
$db = new PDO('sqlite:' . realpath(__DIR__) . '/zftutorial.db');
$sql = file_get_contents(__DIR__ . '/bulk_albums.sql');
$db->exec($sql);

Then, execute it with:

$ php data/load_bulk_albums.php

This gives us a handy extra 150 rows to play with. If you now visit your album list at /album, you'll see a huge long list of 150+ albums; it's ugly.

Install zend-paginator

zend-paginator is not installed or configured by default, so we will need to do that. Run the following from the application root:

$ composer require zendframework/zend-paginator

Assuming you followed the Getting Started tutorial, you will be prompted by the zend-component-installer plugin to inject Zend\Paginator; be sure to select the option for either config/application.config.php orconfig/modules.config.php; since it is the only package you are installing, you can answer either "y" or "n" to the "Remember this option for other packages of the same type" prompt.

Manual configuration

If you are not using zend-component-installer, you will need to setup configuration manually. You can do this in one of two ways:

  • Register the Zend\Paginator module in eitherconfig/application.config.php or config/modules.config.php. Make sure you put it towards the top of the module list, before any modules you have defined or third party modules you are using.
  • Alternately, add a new file, config/autoload/paginator.global.php, with the following contents:
<?php
use Zend\Paginator\ConfigProvider; return [
'service_manager' => (new ConfigProvider())->getDependencyConfig(),
];

Once installed, our application is now aware of zend-paginator, and even has some default factories in place, which we will now make use of.

Modifying the AlbumTable

In order to let zend-paginator handle our database queries automatically for us, we will be using the DbSelect pagination adapter This will automatically manipulate and run a Zend\Db\Sql\Select object to include the correct LIMITand WHERE clauses so that it returns only the configured amount of data for the given page. Let's modify the fetchAll method of the AlbumTable model, so that it can optionally return a paginator object:

// in module/Album/src/Model/AlbumTable.php:
namespace Album\Model; use RuntimeException;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\Sql\Select;
use Zend\Db\TableGateway\TableGateway;
use Zend\Paginator\Adapter\DbSelect;
use Zend\Paginator\Paginator; class AlbumTable
{
/* ... */ public function fetchAll($paginated = false)
{
if ($paginated) {
return $this->fetchPaginatedResults();
} return $this->tableGateway->select();
} private function fetchPaginatedResults()
{
// Create a new Select object for the table:
$select = new Select($this->tableGateway->getTable()); // Create a new result set based on the Album entity:
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album()); // Create a new pagination adapter object:
$paginatorAdapter = new DbSelect(
// our configured select object:
$select,
// the adapter to run it against:
$this->tableGateway->getAdapter(),
// the result set to hydrate:
$resultSetPrototype
); $paginator = new Paginator($paginatorAdapter);
return $paginator;
} /* ... */
}

This will return a fully configured Paginator instance. We've already told theDbSelect adapter to use our created Select object, to use the adapter that theTableGateway object uses, and also how to hydrate the result into a Album entity in the same fashion as the TableGateway does. This means that our executed and returned paginator results will return Album objects in exactly the same fashion as the non-paginated results.

Modifying the AlbumController

Next, we need to tell the album controller to provide the view with aPagination object instead of a ResultSet. Both these objects can by iterated over to return hydrated Album objects, so we won't need to make many changes to the view script:

// in module/Album/src/Controller/AlbumController.php:

/* ... */

public function indexAction()
{
// Grab the paginator from the AlbumTable:
$paginator = $this->table->fetchAll(true); // Set the current page to what has been passed in query string,
// or to 1 if none is set, or the page is invalid:
$page = (int) $this->params()->fromQuery('page', 1);
$page = ($page < 1) ? 1 : $page;
$paginator->setCurrentPageNumber($page); // Set the number of items per page to 10:
$paginator->setItemCountPerPage(10); return new ViewModel(['paginator' => $paginator]);
} /* ... */

Here we are getting the configured Paginator object from the AlbumTable, and then telling it to use the page that is optionally passed in the querystring pageparameter (after first validating it). We are also telling the paginator we want to display 10 albums per page.

Updating the View Script

Now, tell the view script to iterate over the pagination view variable, rather than the albums variable:

<?php // in module/Album/view/album/album/index.phtml: ?>
<table class="table">
<tr>
<th>Title</th>
<th>Artist</th>
<th>&nbsp;</th>
</tr>
<?php foreach ($this->paginator as $album) : // <-- change here! ?>
<tr>
<td><?= $this->escapeHtml($album->title) ?></td>
<td><?= $this->escapeHtml($album->artist) ?></td>
<td>
<a href="<?= $this->url('album', ['action' => 'edit', 'id' => $album->id)] ?>">Edit</a>
<a href="<?= $this->url('album', ['action' => 'delete', 'id' => $album->id]) ?>">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</table>

Checking the /album route on your website should now give you a list of just 10 albums, but with no method to navigate through the pages. Let's correct that now.

Creating the Pagination Control Partial

Much like we created a custom breadcrumbs partial to render our breadcrumb in the navigation tutorial, we need to create a custom pagination control partial to render our pagination control just the way we want it. Again, because we are using Bootstrap, this will primarily involve outputting correctly formatted HTML. Let's create the partial in the module/Application/view/partial/ folder, so that we can use the control in all our modules:

<?php // in module/Application/view/partial/paginator.phtml: ?>
<?php if ($this->pageCount): ?>
<div>
<ul class="pagination">
<!-- Previous page link -->
<?php if (isset($this->previous)): ?>
<li>
<a href="<?= $this->url($this->route, [], ['query' => ['page' => $this->previous]]) ?>">
&lt;&lt;
</a>
</li>
<?php else: ?>
<li class="disabled">
<a href="#">
&lt;&lt;
</a>
</li>
<?php endif ?> <!-- Numbered page links -->
<?php foreach ($this->pagesInRange as $page): ?>
<?php if ($page !== $this->current): ?>
<li>
<a href="<?= $this->url($this->route, [], ['query' => ['page' => $page]]) ?>">
<?= $page; ?>
</a>
</li>
<?php else: ?>
<li class="active">
<a href="#"><?= $page; ?></a>
</li>
<?php endif ?>
<?php endforeach ?> <!-- Next page link -->
<?php if (isset($this->next)): ?>
<li>
<a href="<?= $this->url($this->route, [], ['query' => ['page' => $this->next]]) ?>">
&gt;&gt;
</a>
</li>
<?php else: ?>
<li class="disabled">
<a href="#">
&gt;&gt;
</a>
</li>
<?php endif ?>
</ul>
</div>
<?php endif ?>

This partial creates a pagination control with links to the correct pages (if there is more than one page in the pagination object). It will render a previous page link (and mark it disabled if you are at the first page), then render a list of intermediate pages (that are passed to the partial based on the rendering style; we'll pass that to the view helper in the next step). Finally, it will create a next page link (and disable it if you're at the end). Notice how we pass the page number via the page querystring parameter which we have already told our controller to use to display the current page.

Using the PaginationControl View Helper

To page through the albums, we need to invoke the paginationControl view helper to display our pagination control:

<?php
// In module/Album/view/album/album/index.phtml.
// Add at the end of the file after the table:
?>
<?= $this->paginationControl(
// The paginator object:
$this->paginator,
// The scrolling style:
'sliding',
// The partial to use to render the control:
'partial/paginator',
// The route to link to when a user clicks a control link:
[ 'route' => 'album' ]
) ?>

The above echoes the paginationControl helper, and tells it to use our paginator instance, the sliding scrolling style, our paginator partial, and which route to use for generating links. Refreshing your application now should give you Bootstrap-styled pagination controls!

Using zend-paginator in your Album Module的更多相关文章

  1. Using zend-navigation in your Album Module

    Using zend-navigation in your Album Module In this tutorial we will use the zend-navigation componen ...

  2. Introducing the Blog Module

    Introducing the Blog Module Now that we know about the basics of the zend-mvc skeleton application, ...

  3. Zend框架2入门(二) (转)

    Zend框架2使用一个模块系统,和你组织内每个你的主应用程序特定代码模块.骨架提供的应用程序模块是用于提供引导,错误和路由配置到整个应用程序.它通常是用来提供应用水平控制器,比如说,应用程序的主页,但 ...

  4. ZendFramework-2.4 源代码 - 关于Module - 模块入口文件

    <?php // /data/www/www.domain.com/www/module/Album/Module.php namespace Album; use Zend\ModuleMan ...

  5. Zend Framework 2中如何使用Service Manager

    end Framework 2 使用ServiceManager(简称SM)来实现控制反转(IoC).有很多资料介绍了service managers的背景,我推荐大家看看this blog post ...

  6. zendframework 2 链接数据库

    相对于zf1,来说,zf2让我们对于数据库这方面的操作我的个人感觉是对于字段起别名简单了,但是对数据库的操作虽然配置写好的就基本不需要动了,但是还是比1的配置要繁琐, 还是那句话,大家可以去看看源码. ...

  7. Unit Testing a zend-mvc application

    Unit Testing a zend-mvc application A solid unit test suite is essential for ongoing development in ...

  8. Forms and actions

    Forms and actions Adding new albums We can now code up the functionality to add new albums. There ar ...

  9. Database and models

    Database and models The database Now that we have the Album module set up with controller action met ...

随机推荐

  1. 建立自己的bin目录,在当前路径运行shell脚本

    Shell脚本nusers cat nusers #! /bin/sh - who | wc -l 如果你要编写自己的脚本,最好准备自己的bin目录来存放它们,并且让Shell能够自动找到它们.这不难 ...

  2. 8、四大组件之三-ContentProvider

    课程目标: 理解ContentProvider的作用及好处 认清ContentProvider与数据存储的关系 掌握ContentProvider对外提供的数据模型形式 能够编写ContentReso ...

  3. 【Tcpcopy】离线回放功能

    最近因调试问题,需要一直进行tcpcopy,拿有问题的包进行测试.决定使用tcpcopy对录制脚本进行回放,以下为我操作的具体步骤.主要是三块 1 下载安装具有离线回放功能的tcpcopy 2 使用t ...

  4. POJ 3107-Godfather(树形dp)

    题意: 有n个节点的树,删除一个点,得到的最大联通分支最小,求这样点的集合 分析: dp[i]表示删除i所得最大联通分支,遍历一遍节点即可,该题用vector会超时 #include <map& ...

  5. LightOJ 1422 Halloween Costumes 区间dp

    题意:给你n天需要穿的衣服的样式,每次可以套着穿衣服,脱掉的衣服就不能再穿了,问至少要带多少条衣服才能参加所有宴会 思路:dp[i][j]代表i-j天最少要带的衣服 从后向前dp 区间从大到小 更新d ...

  6. MMU(what,how,todo)

    出处:http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=11580&fromuid=5490 正文黑色,代码蓝色,重点标红. ...

  7. 使用SVN服务器管理源码

    最近在学习使用SVN管理自己的项目文件,正好有好的文章就拿来标记一下,正所谓: 站在巨人的肩膀上   天下知识为我所用 转载两篇关于使用SVN管理源码的文章. 使用SVN进行源码管理(上):http: ...

  8. 【noip2011】Mayan游戏

    题解: 刷了一天的noip啊 做了10题! 突然找回了做马拉松的感觉- - 我中午竟然放弃治疗去看视频 做到晚上累得都快挂了 用电脑放一些rock 把音乐当咖啡硬撑下来 但是还是没能刷3届 唉 显然速 ...

  9. TCP恋爱史:三次握手和四次分手

    TCP协议非常重要,这里把它的连接和释放整理一下. 首先是三次握手: 1.  客户端发起,像服务器发送的报文SYN=1,ACK=0,然后选择了一个初始序号:seq=x. SYN是干什么用的? 在链接的 ...

  10. 如何计算ModBus超时时间?

    波特率:每秒钟通过信道传输的信息量称为位传输速率,也就是每秒钟传送的二进制位数,简称比特率.比特率表示有效数据的传输速率,用b/s .bit/s.比特/秒,读作:比特每秒. 如9600b/s:指总线上 ...