Page 4 of 5

Posted: Mon Feb 20, 2006 11:39 pm
by Christopher
I have not worked on this for a while, but got the ADODB and MySQL Pageable classes working so I thought I would bundle up the code. It is available here:

http://ap3.sourceforge.net/pager-0.2.0.zip

One other thing I wanted to add, because I use it, is to allow the pager to remember where it was and resume. This is very helpful in CRUD type code because the paged list contains a link for each item in the list. When you are done on the sub page for that list item, it is much nicer to return to the list where you left off. Sessions are needed to do that without requiring the sub pages to remember values and pass them back. Unfortunately the logic is messy and the code I wrote just seems terrible to me (and has a terrible name). Maybe someone can find a way to clean up this code:

Code: Select all

class PagerSessionRequest {
	var $pager;
	var $session_name = 'Pager';
	var $page_resume = 'resume';
	var $last_row_recalc = 'recalc';

	function PagerSessionRequest(&$pager) {
		$this->pager = &$pager;
	}

	function process() {
		session_start();
		$pager = &$this->pager;
		$resume = false;

// code to set the page number

// no 'page=' parameter so initialize pager
		if (! isset($_GET[$pager->page_param])) {
			$pager->current_page = $pager->first_page;
			unset($_SESSION[$this->session_name]);		// clear any previous values
			$_SESSION[$this->session_name]['page'] = $pager->current_page;
		} else {
// 'page=resume' and page number in session so resume
			if (($_GET[$pager->page_param] === $this->page_resume) && (isset($_SESSION[$this->session_name]['page']))) {
				$pager->current_page = $_SESSION[$this->session_name]['page'];
				$resume = true;
			} else {
// use 'page=' parameter instead
				$pager->current_page = intval($_GET[$pager->page_param]);
				$_SESSION[$this->session_name]['page'] = $pager->current_page;
			}
		}
		
// code to set the last row value

// no 'last_row=' param and not resuming OR 'last_row=recalc' to force the datasource to recalculate value
		if ((! isset($_GET[$pager->last_row_param]) && (! $resume)) 
					|| ((isset($_GET[$pager->last_row_param]) && ($_GET[$pager->last_row_param] === $this->last_row_recalc)))) {
			$pager->last_row = $pager->datasource->getNumRows();
			$_SESSION[$this->session_name]['last_row'] = $pager->last_row;
		} elseif (isset($_SESSION[$this->session_name]['last_row'])) {
// last_row value is in session so use that
			$pager->last_row = $_SESSION[$this->session_name]['last_row'];
		} else {
// set the value in the session to the value in the pager object
			$_SESSION[$this->session_name]['last_row'] = $pager->last_row;
		}
 
// code to set the page size value

// 'page_size=' parameter passed so save in session
		if (isset($_GET[$pager->page_size_param])) {
			$pager->page_size = intval($_GET[$pager->page_size_param]);
			$_SESSION[$this->session_name]['page_size'] = $pager->page_size;
		} elseif (isset($_SESSION[$this->session_name]['page_size'])) {
// page size value is in session so use that value
			$pager->page_size = $_SESSION[$this->session_name]['page_size'];
		} else {
// set the value in the session to the value in the pager object
			$_SESSION[$this->session_name]['page_size'] = $pager->page_size;
		}

		$pager->setCurrentPage($pager->current_page);

	}

}

Posted: Wed Feb 22, 2006 11:22 am
by timvw
Don't have the time to review all the code, but the following is one that might bite you ;)


I'd change the process function a little to:

Code: Select all

<?php
function process() {
  if (!isset($_SESSION)) session_start();
  ...
}
?>

Posted: Wed Feb 22, 2006 12:21 pm
by Christopher
Thanks Tim, I'll change that. Can you think of a better name than "PagerSessionRequest" (ugh!). Maybe PagerRequestWithSession ?

I think the only other thing I want to add is some support for changing the sort order by clicking on links (column heads usually). Then a refactoring pass and clean up my unit tests.

Posted: Fri Feb 24, 2006 4:21 am
by Stripe-man
Interesting guys...
Im interested in seeing where this might go...

Posted: Fri Feb 24, 2006 10:20 am
by timvw
arborint wrote: I think the only other thing I want to add is some support for changing the sort order by clicking on links (column heads usually). Then a refactoring pass and clean up my unit tests.
Well, the ordering of data is one of the first things that i'd factor out of a Pager..

Sorting usually requires you can do the following on your data:
- void Sort($options);

Most implementation that i know simply have $options defined as following, an array with "column" -> "order" elements... eg:
$options = array( array('col1' => 'desc'), array('col2' => 'asc'), ... );

And now the story for a Sorter starts like the Pager story.. (A pattern in patterns... We're at the next level :p)

Posted: Fri Feb 24, 2006 11:04 am
by Christopher
I was thinking of supporting the URL generation and request processing part to maintain and reverse the current sort direction. That's the useful part as it relates to the Pager.

I don't know the specifics, but assumed that you would initialize sorting by providing some kind of array of the fields you want to be possibly sorted on (like you presented). The database side is easier because "ORDER BY" is standard -- unlike generating record sets.

Posted: Fri Feb 24, 2006 2:50 pm
by timvw
I've never said that afterwards they can easily be combined.. When i decided to stop passing arrays with data, but instead "pageable data (whatever that may be) i had the following in mind...

1-) You start with a SortableAndPageableSQLQueryDataSource (atleast it's clear what it is :p)
2-) Order the data by using the methods of the Sortable interface (add an ORDER BY clause (or whatever that is needed))
3-) Page the data by using the methods of the Pageable interface (add a LIMIT clause (or whatever that is needed))
4-) Then you "render/display" the data.. A SortablePageWriter should know how to generate links for sorting and paging.
5-) Time for a couple of beers...

Posted: Fri Feb 24, 2006 7:40 pm
by Christopher
I was thinking:

1) Time for a couple of beers...
2) Add the ability to register fields to sort on
3) Figure out how to maintain sort direction internally
4) Add a method to create URLs for changing the sort order
5) Add code to get sorting parameters from the request
6) Maybe add an OrderBy() method to the Pagable classes

Posted: Fri Feb 24, 2006 8:01 pm
by AlecH
Wow, this is a very nice class and I do believe that I will be needing this soon. If its allowed to be used as open source code that is.

Posted: Fri Feb 24, 2006 9:33 pm
by Christopher
AlecH wrote:If its allowed to be used as open source code that is.
I assume that you are free to use the code posted here. Should it have some sort of license or disclaimer?

Posted: Sat Feb 25, 2006 3:20 am
by matthijs
Maybe a free license in the sense of free beer (for the developers) :) ? They seem very thirsty..

Adding onclick?

Posted: Sun Aug 13, 2006 11:05 pm
by DragonI
Hi,

I just came across your great class. I would like to you it for Ajax based pagination. How would I go about adding something like this:

onclick="ajaxPaging($var1, $var2, $page);return false;"

Thanks!

Posted: Sun Aug 13, 2006 11:42 pm
by Christopher
That's a great idea. There are probably a couple of ways to do it. The application specific way would be to create one wrapper that accepted calls and another that put the page data into XML/JSON/whatever as a response. But it would be easier to have the library handle that as well because the whole point of a Pager class is to deal with all the annoying details of pagination. It seems like the XML response should contain the page data, plus info for the paging display. Then on the Javascript side we could have code to parse the XML and return the data as an array and set the paging variables at the same time.

What did you see the code looking like? You showed:

Code: Select all

onclick="ajaxPaging($var1, $var2, $page);return false;"
I guess we would need similar calls in Javascript to the current PHP interface, such as ajaxPage, ajaxPageNext, ajaxPagePrev, ajaxFirst, ajaxLast, ajaxRange, etc.

I would be interested to know what you (and others) think the Javascript interfaceshould look like.

FYI - I have continued to develop that code and may have a newer version with more features than the one you are looking at.

Posted: Thu Aug 17, 2006 1:54 am
by DragonI
Hi arborint,

I'm currently using a pagination class called PaginateIt, http://www.bradyvercher.com/, that only produces the pagination links. Therefore the data is displayed independently. However, I had to make some mod's to pass in the javascript function and parms as a string. Not the nicest solution but it works. I'm also using xajax, so I won't require the Json/XML. But I'm just being selfish! People who are using the Pager class may want it

Here's an example of the link would look like:

Code: Select all

<a href="/products/?cat=1&pid=1&page=2" title="goto page 2" onclick="xajax_pageNavigate(1,1,2);return false;">2</a>
I have the href stuff just in case users don't have javascript enabled.

Sorry, it's late - hopefully this makes sense

Posted: Thu Aug 17, 2006 5:28 am
by Jenk
With regards to including functionality such as ordering and so forth, should that not be a seperate entity/class to the pager class?

I can just see this pager class, as awesome as it will be/is, being blob like with lots of functionality that is only rarely used, weighing down the objects. So would it be better to seperate the classes out as per functionality? You could also argue that the ordering should be done by the datasource/dataobject before being passed to the pager?

Likewise for the iteration of pageable data, shouldn't the pager class return the pages data in bulk, and a seperate iterator (be it a template system's iterator such as smarty {section}) handle the.. iteration?