Sunday, October 21, 2007

Try Zend Framework vol.6 "Simple Pager"

When I look back in my old days, the pagination was my first obstacle to understand how it works like my friend introduced here.
PEAR::Pager was so impressive for me to make pagination. :-)
So, I wrote a simple pager for Zend Framework view helper.

Let's say you have data like below. The total record is 1000.
mysql> select * from data limit 10;
+----+---------+
| id | data |
+----+---------+
| 1 | data 1 |
| 2 | data 2 |
| 3 | data 3 |
| 4 | data 4 |
| 5 | data 5 |
| 6 | data 6 |
| 7 | data 7 |
| 8 | data 8 |
| 9 | data 9 |
| 10 | data 10 |
+----+---------+


Here the pager helper.
It basically requires total number of record for pagination.
library/Hoge/View/Helper/Pager.php
<?php
require_once 'Zend/View/Helper/Url.php';

/**
* Pager Helper
*/

class Hoge_View_Helper_Pager
{
/**
* Current page
* @var integer
*/
protected $currentPage = 1;

/**
* Per page
* How many items to display per pages.
* @var integer
*/
protected $perPage = 20;

/**
* Display links
* How many page to display in the pagination links.
* @var integer
*/
protected $pageRange = 10;

/**
* Total number of items
* @var integer
*/
protected $total = 0;

/**
* Start page
* @var integer
*/
protected $startPage = 1;

/**
* End page
* @var integer
*/
protected $endPage = 1;

/**
* Pager format.
*/
const FORMAT_FIRST = '<a href="%s" class="pager_first">&#171;</a>';
const FORMAT_PREVIOUS = '<a href="%s" class="pager_previous">&#139;</a>';
const FORMAT_PREVIOUS_RANGE = '<a href="%s" class="pager_previous_range">...</a>';
const FORMAT_NAVIGATION_LINK = '<a href="%s" class="pager_navigation_link">%d</a>';
const FORMAT_NAVIGATION_CURRENT = '<b class="pager_navigation_current">%d</b>';
const FORMAT_NEXT_RANGE = '<a href="%s" class="pager_next_range">...</a>';
const FORMAT_NEXT = '<a href="%s" class="pager_next">&#155;</a>';
const FORMAT_LAST = '<a href="%s" class="pager_last">&#187;</a>';

/**
* Base link formats
* @var array
*/
protected $baseFormats =
array(
'first' => self::FORMAT_FIRST,
'previous' => self::FORMAT_PREVIOUS,
'previousRange' => self::FORMAT_PREVIOUS_RANGE,
'navigationLink' => self::FORMAT_NAVIGATION_LINK,
'navigationCurrent' => self::FORMAT_NAVIGATION_CURRENT,
'nextRange' => self::FORMAT_NEXT_RANGE,
'next' => self::FORMAT_NEXT,
'last' => self::FORMAT_LAST
);

/**
* Pager instance
* @var object
*/
static protected $pager;

/**
* Pager
*
* @access public
*
* @param integer $total Total records
* @param integer $perPage How many to display per page
*
* @return object pager
*/
public function pager($total = null, $perPage = 20)
{
if(isset(self::$pager)) return self::$pager;

$page = Zend_Controller_Front::getInstance()->getRequest()->getParam('page');

if ($page > 0) {
$this->currentPage = $page;
}

// Number of total data.
$this->total = $total;

// How many rows to display per page.
$this->perPage = $perPage;

// Number of total page
$this->totalPage = ceil($this->total / $this->perPage);

// If total page is smaller than called page.
if ($this->totalPage < $this->currentPage) $this->currentPage = 1;

// Page numnber of core pages.
$corePage = ceil($this->currentPage / $this->pageRange);

// Max page number.
$this->endPage = ($this->pageRange * $corePage);

// Start page number.
$this->startPage = ($this->endPage - $this->pageRange) + 1;

// Set to static instance
self::$pager = $this;

return $this;
}


/**
* Display a simple pagination ex:[<< < ... 1 2 3 ... > >>]
*
* @param array $formats An array of links to use sprintf()
* @return string A link to the desired page page of the pagination
*/
public function paginate(array $formats = array())
{
$formats = array_merge($this->baseFormats, $formats);

$this->pager($this->total, $this->perPage);
$pagination = array();
$pagination[] = $this->pager()->first($formats['first']);
$pagination[] = $this->pager()->previous($formats['previous']);
$pagination[] = $this->pager()->previousRange($formats['previousRange']);
$pagination[] = $this->pager()->navigation($formats['navigationLink'], $formats['navigationCurrent']);
$pagination[] = $this->pager()->nextRange($formats['nextRange']);
$pagination[] = $this->pager()->next($formats['next']);
$pagination[] = $this->pager()->last($formats['last']);

return join('', $pagination);
}

/**
* Display link of the navigation links ex:[1 2 3]
*
* @param string $linkFormat Format string to display page number links to use sprintf()
* @param string $currentFormat Format string to display current page to use sprintf()
* @return string A link to the desired page page of the pagination
*/
public function navigation($linkFormat = self::FORMAT_NAVIGATION_LINK, $currentFormat = self::FORMAT_NAVIGATION_CURRENT)
{
$return = null;
// Display [1,2,3__]
for ($p = $this->startPage; $p <= $this->endPage; $p++) {
// Unlink if the page is selected
if ($p <= $this->totalPage) {
if ($this->currentPage != $p) {
$url = $this->url(array('page' => $p));
$return[] = sprintf($linkFormat, $url, $p);
} else {
$return[] = sprintf($currentFormat, $p);
}
}
}
return join('', $return);
}

/**
* Display link of the next range ex:[...]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the previous range of the pagination
*/
public function previousRange($format = self::FORMAT_PREVIOUS_RANGE)
{
$return = null;
if ($this->pageRange < $this->currentPage) {
$url = $this->url(array('page' => ($this->startPage - 1)));
$return = sprintf($format, $url);
}
return $return;
}

/**
* Display link of the next range ex:[...]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the next range of the pagination
*/
public function nextRange($format = self::FORMAT_NEXT_RANGE)
{
$return = null;
if ($this->endPage < $this->totalPage) {
$url = $this->url(array('page' => ($this->endPage + 1)));
$return = sprintf($format, $url);
}
return $return;
}

/**
* Display link of the previous page ex:[<<]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the first page of the pagination
*/
public function first($format = self::FORMAT_FIRST)
{
$return = null;
if ($this->currentPage != 1) {
$url = $this->url(array('page' => 1));
$return = sprintf($format, $url);
}
return $return;
}

/**
* Display link of the previous page ex:[<]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the previous page of the pagination
*/
public function previous($format = self::FORMAT_PREVIOUS)
{
$return = null;
if ($this->currentPage != 1) {
$url = $this->url(array('page' => ($this->currentPage - 1)));
$return = sprintf($format, $url);
}
return $return;
}

/**
* Display link of the next page ex:[>]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the next page of the pagination
*/
public function next($format = self::FORMAT_NEXT)
{
$return = null;
if($this->currentPage != $this->totalPage) {
$url = $this->url(array('page' => ($this->currentPage + 1)));
$return = sprintf($format, $url);
}
return $return;
}

/**
* Display link of the last page ex:[>>]
*
* @param string $format Format string of link to use sprintf()
* @return string A link to the last page of the pagination
*/
public function last($format = self::FORMAT_LAST)
{
$return = null;
if($this->currentPage != $this->totalPage) {
$url = $this->url(array('page' => $this->totalPage));
$return = sprintf($format, $url);
}
return $return;
}


/**
* Generates an url given the name of a route.
*
* @access public
*
* @see Zend_View_Helper::url()
* @param array $urlOptions Options passed to the assemble method of the Route object.
* @param mixed $name The name of a Route to use. If null it will use the current Route
* @param bool $reset Whether or not to reset the route defaults with those provided
* @return string Url for the link href attribute.
*/
public function url(array $urlOptions = array(), $name = null, $reset = false)
{
$obj = new Zend_View_Helper_Url();
$url = $obj->url($urlOptions, $name, $reset);
return $url;
}

}


Pager needs to know how many total record the object paged has.
In this case, I want to count DB record.
So I added a method to count simple record of Zend_Db_Table object.
library/Hoge/Db/Table/Abstract.php
public function numRows($where = null)
{
$select = $this->_db->select();
$select->from($this->_name, "COUNT(*)", $this->_schema);
// the WHERE clause
$where = (array) $where;
foreach ($where as $key => $val) {
// is $key an int?
if (is_int($key)) {
// $val is the full condition
$select->where($val);
} else {
// $key is the condition with placeholder,
// and $val is quoted into the condition
$select->where($key, $val);
}
}
// return the results
$stmt = $this->_db->query($select);
$data = $stmt->fetchColumn(0);
return $data;
}


To use it.
It is not related to Db model. So you need to calculate the offset and limit.
But it not so difficult, isn't it? :p
application/controllers/HogeController.php
function pagerAction()
{
$this->view->setHelperPath(dirname(dirname(dirname(__FILE__))) . '/library/Hoge/View/Helper', 'Hoge_View_Helper');
$this->view->addHelperPath(dirname(dirname(__FILE__)) . '/View/Helper', 'Hoge_View_Helper');

require_once 'Hoge/Db/Table/Data.php';
$data = new Data();
$perPage = 20; // Should be defined.
$offset = ($this->_getParam('page') > 0) ? $perPage * ($this->_getParam('page') - 1) : 0;
$this->view->data = $data->fetchAll(null, null, $perPage, $offset)->toArray();
$this->view->total = $data->numRows();
}


In the view script. Write like this.
In case you want to handle each parts of pager.
<html>
<head></head>
<body>
<? $this->pager($this->total) ?>
<?= $this->pager()->first() ?>
<?= $this->pager()->previous() ?>
<?= $this->pager()->previousRange() ?>
<?= $this->pager()->navigation() ?>
<?= $this->pager()->nextRange() ?>
<?= $this->pager()->next() ?>
<?= $this->pager()->last() ?>

<ul>
<? foreach($this->data as $k => $v): ?>
<li><?= "{$v['id']} : {$v['data']}" ?></li>
<? endforeach ?>
</ul>
</body>
</html>


In case you want to display simple pager.
<html>
<head></head>
<body>
<?= $this->pager($this->total)->paginate() ?>
<ul>
<? foreach($this->data as $k => $v): ?>
<li><?= "{$v['id']} : {$v['data']}" ?></li>
<? endforeach ?>
</ul>
</body>
</html>

Both of them look like this.
«‹...11121314151617181920...›»

The helper class only have responsibility creating pagination, NOT dataset, so you need to set offset and limit.
If you want to get more sophisticated "Pagination helper" see here :^)
The author is introducing a pagination helper which related to dataset.

(weight 88.6kg BMI 31%)

Labels:

Monday, October 15, 2007

From eight hells

It's about a story that I had been to eight hells.

1st hell "Tatsumaki Jigoku (Tornado hell)".
In the hell, the hot water was blown up every 30~60 minutes.
The water was incredibly hot and the tornado was brew everything off.
People around us was screaming.

2nd hell "Chino Ike Jigoku (Bloody pond hell)".
In the hell, there was a large pond and it looked like full of blood.
People around us were quiet and gazed the pond.

3rd hell "Umi Jigoku (Sea hell)".
In the hell, there was a pond which looked beautiful white blue.
But once you got into it, you would never get out of it.
It opened big mouth to swallow everything.
People around us were just trying to escape from the pond.

4th hell "Bouzu Jigoku (Bouzu hell)".
In the hell, there were many pond consists of clay.
It was boiled violently.
Peopole around us were just trembled frighteningly for fear.

5th hell "Yama Jigoku (Mountain hell)".
In the hell, there were so many creatures.
One of them looked like elephant, one of them looked like condor having sharp claw.
Their eyes gazed us scarly.
People around us was trying not to match eyes.

6th hell "Kamado Jigoku (Steam hell)".
In the hell, it was too hot to be there. A huge hot steam blew to us.
We hardly open our eyes while being through the hell.
People around us were looking for water to enrich the dryness of the throat.

7th hell "Oniyama Jigoku (Devil mountain hell)".
In the hell, so many crocodilian were waiting for prey.
Some of them opened its huge mouth and show us sharp fangs.
People around us were frightened if they called to be prey.

8th hell "Shiraike Jigoku (White pond hell)".
In the hell, there ware beautiful white ponds. We could put our foot into the pond.
It releaved our pain of foot.
People around us were screaming of comfortableness.

We hopefully could come back from eight hells. And really enjoyed hells.
If we can have chance to be back there, we will try again.
(weight 89.0kg BMI 33%)

Labels:

Sunday, October 07, 2007

Over the river

Since my working place has changed, I haven't been able to ride my pretty bike.
These days, I could get a chance to have long vacation for 2 weeks. It's just unbelievable! Thank god !
So I decide to ride my bike while my vacation.

Then my friend suggest me to come to the place he lives. It sounds like a kind of joke, because it's too far to get by bike.
It's so challenging and I really like it. I had been wondering if I could try.

But, it kind of wasting time to think again and again.
My heart told me "No need to think, Just do it !".

So, I left my house at 14PM.
At the moment of leaving my house, I was kind of scared if I can arrive there.
After few minutes, my anxiety has blown out!
It was so comfortable to ride the bike. And so fun! My foot never stop riding!

Around 16PM. I got around "Omiya". I was almost lost my consciousness and I was at a lost there. I asked the way to "Kawa Goe" at the police station.

Around 17PM. I called my friend to ask the way to his house. He was just surprised that I was there. He kindly told me how to get his house.

Around 17:30PM. I arrived his house !! So satisfied !!
Talking for 1 hour. His son came back with mother, This was the first time to meet him, at first site, he was kind of scared :<. Because I have beard and mustache, may be so scarly for him. He was almost crying but he was so cute !! When I left his house, finally he waved his hand to me. I believe we are friend now!

Around 19PM. I left his house. I forget to take a light. I just worried if the police catch me. My friend lent me a small stand light! Wow, useless, man! It's useless!
Anyway I bought some battery to use it, and grab it while riding bike :p.
Sorry I was wrong, It helped me sometimes. You were right, friend.

Around 21PM. I could arrive at my house. Almost dead :<.

Around 21:30PM I went out with my another friend to drink beer!!

BEER! BEER! BEER! BEER! BEER!

After traveling a long distance! The taste of beer is incredibly good! It ran throw my throat!
Anyway, I really enjoyed this small trip with my cute bike!
(weight 87.8kg BMI 30%)

Labels:

Wednesday, October 03, 2007

Try Zend Framework vol.5 "Always absolute url"

Zend Framework has some cute View Helpers.
One of them is Zend_View_Helper_Url which generates url easily.
But, it gives part of url. Sometimes I want to get absolute path. :<
Don't you want? :>

library/Hoge/View/Helper/Url.php
<?php
require_once 'Zend/View/Helper/Url.php';

class Hoge_View_Helper_Url extends Zend_View_Helper_Url
{

public function url(array $urlOptions = array(), $name = null, $reset = false)
{
$server = Zend_Controller_Front::getInstance()->getRequest()->getServer();
$url = parent::url($urlOptions, $name, $reset);

return strtolower(trim(array_shift(split('/', $server['SERVER_PROTOCOL'])))) . '://' . $server['HTTP_HOST'] . $url;
}

}


To use it. Nothing changes from default usage.

application/views/scripts/hoge/url.phtml

<?= $this->url() ?>


I strongly inspired by this entry. So I could write the code above. :p
(weight 86.8kg BMI 29%)

Labels:

Do you like "Sushi" ?

Have you ever tried to make "Sushi" ?
I tried before, but it just a simple one which is called "Maki Sushi".

The owner of this site has outstanding skill.
There are several Sushies which she had made.

Some of them look delicious, but some of them takes more courage to eat. :p
But who cares?!
She said "Every Day is a Sushi Day!"
She is so cool ! She enjoys making Sushi every day!!

I really look up to her.
I also want to have that motivation to create something.
(weight 87.0kg BMI 30%)

Labels: