Building Example Pager Class

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

feyd wrote:my post was directed at end goal points, not just first pass. Yes, my end goal would resemble, if not be, a framework of sorts. Obviously a complete framework is outside the boundries of this excersize, but one can push towards it. ;) It probably does go over the heads of a newcomer, which is why it's version 3 or 4 not 1. :)
I will keep in mind that we are headed toward the "DevNetwork Framework." ;) Certainly designing a Pager will bring out many of the issues that a framework would need to deal with.
feyd wrote:That's roughly what is in my head for the initiation. :) Although I might put that into a function to simplify and make sure the sequence in performed in proper order.. ;)
Great. Let's get Jcart's input (and anyone else interested) on what we have discussed and then we can start to nail down some specific requirements.
(#10850)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I'll take those comments as a yes ;) so I hacked something up quick to show some running code for what I proposed above:

Code: Select all

class PagerArrayDatasource {
	var $data = array();
	
	function PagerArrayDatasource($data) {
		if (is_array($data)) {
			$this->data = $data;
		}
	}

	function getNumRows() {
		return count($this->data);
	}
}

class Pager {
	var $datasource = null;
	var $page_size = 10;
	var $current_page = 0;
	var $max_page = 0;
	var $max_row = 0;
	var $start_row = 0;
	var $end_row = 0;
	var $page_parameter = 'page';

	function Pager(&$datasource) {
		$this->datasource = &$datasource;
	}

	function setPageSize($n) {
		if ($n > 0) {
			$this->page_size = $n;
		}
	}

	function setCurrentPage($n) {
		$this->max_row = $this->datasource->getNumRows();
		if ($this->max_row > 0) {
			$this->max_page = ceil($this->max_row / $this->page_size);
			if ($n < 1) {
				$n = 1;
			}
			if ($n > $this->max_page) {
				$n = $this->max_page;
			}
			$this->current_page = $n;
			$this->start_row = ($n - 1) * $this->page_size + 1;
			$this->end_row = $this->start_row + $this->page_size - 1;
			if ($this->end_row > $this->max_row) {
				$this->end_row = $this->max_row;
			}
		} else {
			$this->max_page = 0;
			$this->current_page = 0;
			$this->start_row = 0;
			$this->end_row = 0;
		}
	}
}

class PagerHTMLOutputter {
	var $pager = null;

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

	function debug() {
		return "
page_size={$this->pager->page_size}, 
current_page={$this->pager->current_page}, 
max_page={$this->pager->max_page}, 
max_row={$this->pager->max_row}, 
start_row={$this->pager->start_row}, 
end_row={$this->pager->end_row}, 
";
	}
}

// initialize an array for testing
for ($i=0; $i<=75; ++$i) {
	$myarray[$i] = 'This is row ' . $i;
}
// create a data object that has the interface needed by the Pager object
$datasource = & new PagerArrayDatasource($myarray);

// create pager using values from datasource and request parameters
$pager = & new Pager($datasource);
// create Outputter that gets its values from Pager object
$output =  & new PagerHTMLOutputter($pager);

// initialize an array to testing
for ($n=-1; $n<=10; ++$n) {
	$pager->setCurrentPage($n);
	echo "<p>page=$n: " . $output->debug() . '</p>';
}
This code should work in PHP4 and PHP5 (only checked in PHP 5 though). I created debug data with 75 rows which should be 8 pages with the default of 10 rows per page. I then check it with page values from -1 .. 10 (to check bounds as well). The output looks like this:
page=-1: page_size=10, current_page=1, max_page=8, max_row=76, start_row=1, end_row=10,

page=0: page_size=10, current_page=1, max_page=8, max_row=76, start_row=1, end_row=10,

page=1: page_size=10, current_page=1, max_page=8, max_row=76, start_row=1, end_row=10,

page=2: page_size=10, current_page=2, max_page=8, max_row=76, start_row=11, end_row=20,

page=3: page_size=10, current_page=3, max_page=8, max_row=76, start_row=21, end_row=30,

page=4: page_size=10, current_page=4, max_page=8, max_row=76, start_row=31, end_row=40,

page=5: page_size=10, current_page=5, max_page=8, max_row=76, start_row=41, end_row=50,

page=6: page_size=10, current_page=6, max_page=8, max_row=76, start_row=51, end_row=60,

page=7: page_size=10, current_page=7, max_page=8, max_row=76, start_row=61, end_row=70,

page=8: page_size=10, current_page=8, max_page=8, max_row=76, start_row=71, end_row=76,

page=9: page_size=10, current_page=8, max_page=8, max_row=76, start_row=71, end_row=76,

page=10: page_size=10, current_page=8, max_page=8, max_row=76, start_row=71, end_row=76,
Last edited by Christopher on Wed Feb 08, 2006 12:20 am, edited 1 time in total.
(#10850)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Hopefully I can sit down tommorow and give you some insight on the discussion, but I've got a ton a work ahead of me.

Quick notes from your code:
1) I would suggest we port it to PHP5, so we can focus more on OOP
2) Perhaps we should provide Unit Tests.. never hurts ;)

I'm still learning much about Unit Testing every day, but I see this as an excellent opportunity to sharpen your testing skills.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I wanted to know a little about the possible Use Cases, so I modified Jcart's and Jshpro2's code (found in the Code Snippets forum) to work with the code I posted above. My hack is really terrible, but it showed me some a few things. One is the question of how to deal with the composition of the Pager object in the Outputter. In Jcart's code I always use the $this->pager->var form to get to values, while in Jshpro2's I copy from the Pager object to local vars. It would probably make sense to create methods to get the various values from the Pager and copy to locals where it makes sense.

There was a difference in how the two dealt with URL generation so I added the ability for the programmer to set what the name of the parameter is. I would probably make sense to centralize the URL generation into a method in the base Outputter class to reduce code and add other features like adding other parameters to all URLs (e.g. FrontController action param). And I can hear feyd saying that we should split out the URL code into a separate class as part of the framework. ;)

Hacking these to classes has given me a sense of what methods the Pager class might need to provide and what common methods that Outputter might use. As ever, variable and method naming is slightly different programmer to programmer. I probably made the naming worse from borrowing from all directions.

Here is Jcart's:

Code: Select all

/*
   Based on:
    
   Pagination 1.1.0

   Made by John Cartwright (2005)
   Tested and modified by members at http://forums.devnetwork.net
*/

class PagerHTMLOutputterJcart extends PagerHTMLOutputter {
	var $delimeter = '>';
	var $newLimit = 0;
	var $viewing = '';
	var $BoundaryMin;
	var $BoundaryMax;
	
	function PagerHTMLOutputterJcart(&$pager) {
		$this->PagerHTMLOutputter($pager);
	}

    function createViewing() {
        $this->newLimit = $this->pager->current_page * $this->pager->page_size;
        $this->viewing  = 'Viewing: [ ';
        
        if ($this->pager->page_size != 1) {
            $this->viewing .= (($this->newLimit - $this->pager->page_size) + 1) .' - '.  ($this->newLimit > $this->pager->max_row ? ($this->pager->max_row) : $this->newLimit);
        }
        else {
            $this->viewing .= $this->pager->current_page;
        }
        
        $this->viewing .= ' ] of '. $this->pager->max_row;
        return $this->viewing;    
    }

    function balanceOffset() {        
        if ($this->BoundaryMin < 1) {
            $this->BoundaryMin = 1;            
            
            if ($this->pager->current_page < $this->maxDivided) {
                $difference = $this->maxDivided - $this->pager->current_page;
            }

            $this->BoundaryMax = $this->BoundaryMax + $difference + 1;
        }
        
        if ($this->BoundaryMax > $this->pager->max_page) {
            $difference = ($this->BoundaryMax - $this->pager->max_page);
            $this->BoundaryMin = ($this->BoundaryMin - $difference);
            $this->BoundaryMax = ($this->BoundaryMax - $difference);
        }            
    }

    function truncateOffset() {
        if ($this->BoundaryMin < 1) {
            $this->BoundaryMin = 1;
        }
        if ($this->BoundaryMax > $this->pager->max_page) {
            $this->BoundaryMax = $this->pager->max_page;
        }
    }
    
    function getBoundaries() {
        switch ($this->pager->current_page) : 
            case ($this->pager->max_page) :
                $this->BoundaryMax = $this->pager->max_page;
                $this->BoundaryMin = (($this->BoundaryMax - $this->pager->max_page));                    
            break;
            case (1) : 
                $this->BoundaryMin = 1;            
                $this->BoundaryMax = ($this->pager->current_page + $this->pager->max_page);
            break;
            default: 
                $this->maxDivided  = ceil($this->pager->max_page / 2);
                $this->BoundaryMin = ($this->pager->current_page - $this->maxDivided);
                $this->BoundaryMax = ($this->pager->current_page + $this->maxDivided);                                        
                $this->balanceOffset();
                $this->truncateOffset();
                break;  
        endswitch;
        
        $this->truncateOffset();            
    }

    function buildNumbers($string = '') {
        for ($x = $this->BoundaryMin; $x <= $this->BoundaryMax; $x++) {
            if ($x == $this->BoundaryMin && $this->BoundaryMin != 1) {
                $string .= '... '.$this->delimeter.' ';
            }                
            if ($this->pager->current_page == $x) {
                $string .= '<span style="font-weight: bold">'.$x.'</span> '.$this->delimeter.' ';
            }
            else {
                $string .= '<a href="?' . $this->pager->page_parameter . '='.$x.'">'.$x.'</a> '.$this->delimeter.' ';
            }    
            if (($x == $this->BoundaryMax) && ($this->BoundaryMax < $this->pager->max_page)) {
                $string .= '... '.$this->delimeter.' ';
            }                        
        }
        
        $this->numbers = $string;
    }

    function createPagination($string = '') {
    	$this->getBoundaries();
		$this->buildNumbers($string);
    	
        $output  = ($this->pager->current_page == 1 ? 'Prev '.$this->delimeter.' ' : '<a href="?' . $this->pager->page_parameter . '='.($this->pager->current_page - 1).'">Prev</a> '.$this->delimeter.' ');
        $output .= '<a href="?' . $this->pager->page_parameter . '=1">First</a> '.$this->delimeter.' ';        
        $output .= $this->numbers;
        $output .= ($this->pager->current_page == ($this->pager->max_page) ? 'Next '.$this->delimeter.' ' : '<a href="?' . $this->pager->page_parameter . '='.($this->pager->current_page + 1).'" >Next</a> '.$this->delimeter.' ');            
        $output .= '<a href="?' . $this->pager->page_parameter . '='.($this->pager->max_page).'">Last</a>';    
        return $output;
    }

}

/*
// example usage
$output =  & new PagerHTMLOutputterJcart($pager);
echo $output->createViewing();
echo '<br/>';
echo $output->createPagination();
*/
And here is Jshpro2's:

Code: Select all

/*
 *  Based on the create_pagination() function by jshpro2 at http://forums.devnetwork.net
 */

class PagerHTMLOutputterJshpro2 extends PagerHTMLOutputter {
	
	function PagerHTMLOutputterJshpro2(&$pager) {
		$this->PagerHTMLOutputter($pager);
	}

	function createPagination() {  
	    // if there is no need for pagination return the stuff empty  
	    $maxPage = $this->pager->max_page;  
	    if($maxPage == 1) {  
	        return ' <b>1</b> ';  
	    }  
	    $pagination = '';  
	    $page = $this->pager->current_page;  
	    $current_page = $page;  
	    //if there are 10 or more links create the special pagination  
	    if ( $maxPage >= 10 ) {  
	        //show the first 3 links  
	        if($current_page <= 5) {  
	            //this is to prevent that if page 1 is selected only 2 links are shown instead of 3.  
	            if($current_page == 1) {  
	                $maximum = $current_page + 2;  
	            } else {  
	                $maximum = $current_page + 1;  
	            }  
	            for($i = 1; $i <= $maximum; $i++) {    
	                $pagination .= ($i == $current_page) ? '<strong>'.$i.'</strong>' : '<a href="?' . $this->pager->page_parameter . '=' .$i. '">' .$i. '</a>';  
	                if($i < $maximum) {  
	                    $pagination .= ', ';  
	                }  
	            }  
	        } else {
	            //if the page is not in the first row of links show the the first 3 links.  
	            $maximum = 3;  
	            for($i = 1; $i <= $maximum; $i++) {    
	                $pagination .= ($i == $page) ? '<strong>'.$i.'</strong>' : '<a href="?' . $this->pager->page_parameter . '=' .$i. '">' .$i. '</a>';  
	                if($i < $maximum) {  
	                    $pagination .= ', ';  
	                }  
	            }  
	        }  
	        if($current_page > 5) {  
	            //show the first dots  
	            $pagination .= ' ... ';  
	            //and show the link in front and behind $current_page  
	            if($current_page <= $maxPage) {  
	                 //this creates the links if page is higher then 5.  
	                 if($current_page == $maxPage) {  
	                      $start_num = $current_page - 2;  
	                 } else {  
	                      $start_num = $current_page - 1;  
	                 }  
	                 $max_num = 3;  
	                 for($i = 1; $i <= $max_num; $i++) {  
	                   $pagination .= ($start_num == $current_page) ? '<strong>'.$start_num.'</strong>' : '<a href="?' . $this->pager->page_parameter . '=' .$start_num. '">' .$start_num. '</a>';  
	                   if($i < $maximum) {  
	                        $pagination .= ', ';  
	                   }  
	                   $start_num++;  
	                }  
	            }  
	            //see if there have to be dots at the end.                 
	        }  
	        if($current_page <= ($maxPage - 5)) {  
	            //its smaller so we have to show the dots.  
	            $pagination .= ' ... ';  
	        }  
	        //see how many links we should put at the end of the links row.  
	        //if the current page is is the last or the one before the last we display no links.  
	        if($current_page == $maxPage) {  
	            $max_num = 0;  
	         }  
	         if($current_page == ($maxPage -1)) {  
	            $max_num = 0;  
	         }  
	         if($current_page == ($maxPage -2)) {  
	            $max_num = 1;  
	         }  
	         if($current_page == ($maxPage -3)) {  
	            $max_num = 2;  
	         }  
	         if($current_page <= ($maxPage -4)) {  
	            $max_num = 3;  
	         }  
	         //little thing to check the output of the above if functions.  
	         if($max_num !== 0) {  
	            $start_num = $maxPage - ($max_num - 1);  
	            if($current_page >= ($maxPage - 4))  {  
	                $pagination .= ', ';  
	            }  
	            for($i = 1; $i <= $max_num; $i++) {  
	                $pagination .= '<a href="?' . $this->pager->page_parameter . '=' .$start_num. '">' .$start_num. '</a>';  
	                if($start_num < $maxPage) {  
	                    $pagination .= ', ';  
	                }  
	                $start_num++;  
	            }  
	        }  
	    } else {
	        //if there are 9 links or less create a link string  
	        $nextLink = array();    
	        for($i = 1; $i <= $maxPage; $i++) {    
	            $nextLink[] = ($i == $page) ? '<strong>'.$i.'</strong>' : '<a href="?' . $this->pager->page_parameter . '=' .$i. '">' .$i. '</a>';  
	        }  
	        $pagination .= implode(', ', $nextLink);    
	    }  
	    return $pagination;  
	}

}
/*
// example usage
$output =  & new PagerHTMLOutputterJshpro2($pager);
echo $output->createPagination();
*/
(#10850)
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

Code: Select all

class PagerArrayDatasource {
	var $data = array();
	
	function PagerArrayDatasource($data) {
		if (is_array($data)) {
			$this->data = $data;
		}
	}

	function getNumRows() {
		return count($this->data);
	}
}
Imho this needs some refactoring.. I don't find it a good idea to pass *all* the rows to the pager before it can start paging... Thus i suppose we change the interface a little to: (Do we want PHP4 or PHP5?)

(Haven't run the code yet.. Thus don't shoot me if there are logic/type errors)

Code: Select all

<?php
interface Pageable {
  function getNumRows(); // returns the total number of available rows
  function getRows($begin, $end); // returns all the rows from begin to end
}
?>
An array that can be paged implementation would look like: (Notice how simple it becomes to add different Pageable implementations: MySQL specific, DB Abstraction classes, ...)

Code: Select all

<?php
class PageableArray implements Pageable{
  private $data;

  function PageableArray($data) {
    $this->data = $data;
  }

  function getNumRows() {
    return count($this->data);
  }

  function getRows($begin, $end) {
    if ($begin < 0 || $end >= $this->getNumRows()) {
      trigger_error('array out of bounds');
      return null;
    }
    $rows = array_slice($this->data, $begin, $end - $begin);
    return $rows;
  }
}
?>
Meaby a bit of testing too:

Code: Select all

<?php
<?php
require('pageablearray.php');

class TestOfPageableArray extends UnitTestCase {
  private $data = array();
  private $data2 = array(array('foo', 'bar'), array('foo2', 'bar2'));
  
  function testGetNumRows() {
    $pr1 = new PageableArray($this->data);
    $this->assertEquals($pr1->getNumRows(), $this->data);
    
    $pr2 = new PageableArray($this->data2);
    $this->assertEquals($pr2->getNumRows(), $this->data2);
  }

  function testGetRows() {
    $pr1 = new PageableArray($this->data);
    $this->assertNull($pr1->getNumRows(0, 10));
    $this->assertErrorPattern("#array out of bounds#");

    $pr2 = new PageableArray($this->data2);
    $this->assertEqual($pr2->getNumRows(0, 2), array_slice($this->data2, 0, 2 - 0));
    $this->assertEqual($pr2->getNumRows(0, 1), array_slice($this->data2, 0, 1 - 0));
    $this->assertNull($pr2->getNumRows(0, 3), array_slice($this->data2, 0, 3 - 0));
    $this->assertErrorPattern("#array out of bounds#");
  }
}
?>
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

timvw wrote: Imho this needs some refactoring.. I don't find it a good idea to pass *all* the rows to the pager before it can start paging... Thus i suppose we change the interface a little to:

An array that can be paged implementation would look like: (Notice how simple it becomes to add different Pageable implementations: MySQL specific, DB Abstraction classes, ...)
Hey Tim, great! Thanks. I actually hadn't reached the point of implementing any data access in the datasource. I was just going for the minimum. But you are exactly right and your implementation is exactly what it should be. Excellent!

It should be clear from your code that adding a PagerMySQLDatasource or PagerXMLDatasource will be very straightforward. That polymorphism is the main point of this design (as with the Outputters too). I will add your getRows() method to the code.

FYI - My plan was to do a team programming session, but everyone is busy at the moment so I just plowed ahead. It was supposed to be done step by step, which is why this is only a partial implemention -- but I went far enough to confuse. Thanks for your input.
timvw wrote:(Do we want PHP4 or PHP5?)

Code: Select all

<?php
interface Pageable {
  function getNumRows(); // returns the total number of available rows
  function getRows($begin, $end); // returns all the rows from begin to end
}
?>
This is really two questions, a small and a big one. The smaller question is "Do we want PHP4 or PHP5?" to which the answer needs to be both practical and strategic. Based on my statement in post #4 -- the goal was to have code that was fairly easily usable by a wide group of programmers -- which is why I went with "practical" PHP4 compatable. The strategic question I would ask the DevNetwork people like feyd is if they want example code to be PHP4 compatable or do they want to push PHP5 only code? (this forum is running on PHP4 I think, though Netcraft reports that it is running Microsoft IIS on Linux !!!). I am also not sure if PHP5 gets us any useful features for this implementation.

The bigger question is whether implementing PHP interfaces is a good idea or not. I am not wild about them and I get the sense that as they are used more they are liked less. I certainly think that classes should conform to interfaces, but whether to use the actual PHP "interface" syntax is still a question for me. At this point in PHP they seem to give some error reporting benefits and might be useful in doing some tricks like Dependency Injection, but they don't seem generally useful to me currently.
(#10850)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Since I haven't gotten much feeback on the code I think I will just finish it up. I can implement a MySQL class using the interface that timvw outlined (and maybe PDO, Postgres and SQLite versions too). I think my last question is how or whether to format the paging links. Jcart had posted this as his target:
Jcart wrote:I've updated to output it as follows

Code: Select all

Prev | First | 1 | 2 | 3 | 4 | 5 | 6 | ... | Next | Last
or

Code: Select all

Prev | First | ... | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ... | Next | Last
or

Code: Select all

Prev | First | ... | 7 | 8 | 9 | 10 | 11 | 12 | Next | Last
Execept that I think Prev and First should be swapped to mirror Next and Last that looks reasonable. Since we can have multiple links Outputters, what are the standard layouts that would be good to have examples of?
(#10850)
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Sorry, my noninvolvement simply means I have nothing to comment, therefore I don't disagree or think the design is off, or some such. ;)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

feyd wrote:Sorry, my noninvolvement simply means I have nothing to comment, therefore I don't disagree or think the design is off, or some such. ;)
Wow ... I guess I am so used to getting my code critiqued to shreds. ;)

One of the things I like about breaking a design into multiple object is the opportunity for reuse. I have been playing around some more because I noticed someone mention that "jump boxes" are an option with Pagers. Since I have the composite pager object in the Outputter it was pretty easy to create a select with all the pages that submits itself onChange.

You would use this in combination with another Outputter object for the Prev/Next links. Here is my quick hack of the code:

Code: Select all

class PagerHTMLOutputterJump extends PagerHTMLOutputter {
	
	function PagerHTMLOutputterJump(&$pager) {
		$this->PagerHTMLOutputter($pager);
	}

    function createPagination($form_attr='', $select_attr='', $option_attr='') {

		$output = "<form action="" method="GET" $form_attr>\n";
		$output .= '<select name="'. $this->pager->page_parameter . "" onChange="this.form.submit()" $select_attr>\n";
		for ($i=1; $i<=$this->pager->max_page; ++$i) {
			if ($i == $this->pager->current_page) {
				$output .= "<option value="$i" selected="selected"  $option_attr>Current Page $i</option>\n";
			} else {
				$output .= "<option value="$i" $option_attr>Page $i of {$this->pager->max_page}</option>\n";
			}
		}
		$output .= "</select>\n</form>\n";
        return $output;
    }

}
I allow you to pass more attributes for the form, select and option so you could add id, class or styles. But I am still not happy with the hard coding of the text for "Current Page $i" and "Page $i of {$this->pager->max_page}". Maybe someone has an idea about how to allow all this text to be customizable?
(#10850)
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

sprintf() or a basic template string from a language object?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

feyd wrote:sprintf() or a basic template string from a language object?
That's what I was thinking too. Which one looks better:

"Page %d of %d"

or

"Page {current_page} of {max_page}"


If we're going to build that framework you wanted feyd, we need to start standardizing now! ;)
(#10850)
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I prefer templated, as then a user can bind values in from ~anywhere.

[edit]Some added thoughts: templating will also give more contextual meaning to each variable, as well as allowing for altering orders of usage since not all languages are left-to-right.. :)
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

So something like this?

Code: Select all

class PagerHTMLOutputterJump extends PagerHTMLOutputter {
	var $current_page_template = 'Current Page {current_page}';
	var $page_template = 'Page {current_page} of {max_page}';
	
	function PagerHTMLOutputterJump(&$pager) {
		$this->PagerHTMLOutputter($pager);
	}

	function setCurrentPageTemplate($template) {
		$this->current_page_template = $template;
	}

	function setPageTemplate($template) {
		$this->page_template = $template;
	}

    function createPagination($form_attr='', $submit_attr='', $option_attr='') {

		$output = "<form action=\"\" method=\"GET\" $form_attr>\n";
		$output .= '<select name="'. $this->pager->page_parameter . "\" onChange=\"this.form.submit()\" $submit_attr>\n";
		for ($i=1; $i<=$this->pager->max_page; ++$i) {
			if ($i == $this->pager->current_page) {
				$template = str_replace('{current_page}', $i, $this->current_page_template);
				$output .= "<option value=\"$i\" selected=\"selected\"  $option_attr>$template</option>\n";
			} else {
				$template = str_replace(array('{current_page}', '{max_page}'), array($i, $this->pager->max_page), $this->page_template);
				$output .= "<option value=\"$i\" $option_attr>$template</option>\n";
			}
		}
		$output .= "</select>\n</form>\n";
        return $output;
    }

}
You can test it with code like this:

Code: Select all

for ($i=0; $i<=75; ++$i) {
	$myarray[$i] = 'This is row ' . $i;
}
$datasource = & new PagerArrayDatasource($myarray);
$pager = & new Pager($datasource);

$pagen = (isset($_GET[$pager->page_parameter]) ? intval($_GET[$pager->page_parameter]) : 1);
$pager->setCurrentPage($pagen);

$output =  & new PagerHTMLOutputterJump($pager);
echo $output->createPagination();
(#10850)
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Seems okay for now. Personally, I'd like to have the template strings stored in a central file that's loaded by a templating object to handle the actual replacement, just so there's a unified way of doing it... but that may be for the framework. :)
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Post by josh »

You can still retain the simplicity of sprintf and avoid the overhead of preg_replace by using something like

example 1:

Code: Select all

page %1\$d of %2\$d
example 2:

Code: Select all

Total pages %2\$d, current page: %1\$d
Post Reply