Commit a9ca7044 authored by guilhermeblanco's avatar guilhermeblanco

added pagination topics

parent 091acf6a
+++ Introduction
+++ Working with pager
+++ Controlling range styles
+++ Advanced layouts with pager
+++ Customizing pager layout
\ No newline at end of file
Until now, we learned how to create paginations and how to retrieve ranges around the current page. To abstract the business logic involving the page links generation, there is a powerful component called {{Doctrine_Pager_Layout}}.
The main idea of this component is to abstract php logic and only leave HTML to be defined by Doctrine developer.
{{Doctrine_Pager_Layout}} accepts 3 obrigatory arguments: a {{Doctrine_Pager}} instance, a {{Doctrine_Pager_Range}} subclass instance and a string which is the URL to be assigned as {%url} mask in templates. As you may see, there are 2 types of "variables" in {{Doctrine_Pager_Layout}}:
++++ Mask
A piece of string that is defined inside template as replacements. They are defined as **{%mask_name}** and are replaced by what you define in options or what is defined internally by {{Doctrine_Pager_Layout}} component. Currently, these are the internal masks available:
* **{%page}** Holds the page number, exactly as page_number, but can be overwritable by {{addMaskReplacement()}} to behavior like another mask or value
* **{%page_number}** Stores the current page number, but cannot be overwritable
* **{%url}** Available only in {{setTemplate()}} and {{setSelectedTemplate()}} methods. Holds the processed URL, which was defined in constructor
++++ Template
As the name explains itself, it is the skeleton of HTML or any other resource that is applied to each page returned by {{Doctrine_Pager_Range::rangeAroundPage()}} subclasses. There are 3 distinct templates that can be defined:
* {{setTemplate()}} Defines the template that can be used in all pages returned by {{Doctrine_Pager_Range::rangeAroundPage()}} subclass call
* {{setSelectedTemplate()}} Template that is applied when it is the page to be processed is the current page you are. If nothing is defined (a blank string or no definition), the template you defined in {{setTemplate()}} is used
* {{setSeparatorTemplate()}} Separator template is the string that is applied between each processed page. It is not included before the first call and after the last one. The defined template of this method is not affected by options and also it cannot process masks
Now we know how to create the {{Doctrine_Pager_Layout}} and the types that are around this component, it is time to view the basic usage:
<code type="php">
// Creating pager layout
$pager_layout = new Doctrine_Pager_Layout(
new Doctrine_Pager(
Doctrine_Query::create()
->from( 'User u' )
->leftJoin( 'u.Group g' )
->orderby( 'u.username ASC' ),
$currentPage,
$resultsPerPage
),
new Doctrine_Pager_Range_Sliding(array(
'chunk' => 5
)),
'http://wwww.domain.com/app/User/list/page,{%page}'
);
// Assigning templates for page links creation
$pager_layout->setTemplate('[<a href="{%url}">{%page}</a>]');
$pager_layout->setSelectedTemplate('[{%page}]');
// Retrieving Doctrine_Pager instance
$pager = $pager_layout->getPager();
// Fetching users
$users = $pager->execute();
// Displaying page links
// Displays: [1][2][3][4][5]
// With links in all pages, except the $currentPage (our example, page 1)
$pager_layout->display();
</code>
Explaining this source, the first part creates the pager layout instance. Second, it defines the templates for all pages and for the current page. The last part, it retrieves the {{Doctrine_Pager}} object and executes the query, returning in variable {{$users}}. The last part calls the displar without any optional mask, which applies the template in all pagfes found by {{Doctrine_Pager_Range::rangeAroundPage()}} subclass call.
As you may see, there is no need to use other masks except the internals ones. Lets suppose we implement a new functionality to search for Users in our existent application, and we need to support this feature in pager layout too. To simplify our case, the search parameter is named "search" and is received through {{$_GET}} superglobal array.
The first change we need to do is tho adjust the {{Doctrine_Query}} object and also the URL, to allow it to be sent to other pages.
<code type="php">
// Creating pager layout
$pager_layout = new Doctrine_Pager_Layout(
new Doctrine_Pager(
Doctrine_Query::create()
->from( 'User u' )
->leftJoin( 'u.Group g' )
->where( 'LOWER(u.username) LIKE LOWER(?)', array( '%'.$_GET['search'].'%' ) )
->orderby( 'u.username ASC' ),
$currentPage,
$resultsPerPage
),
new Doctrine_Pager_Range_Sliding(array(
'chunk' => 5
)),
'http://wwww.domain.com/app/User/list/page,{%page}?search={%search}'
);
</code>
Check out the code and notice we added a new mask, called {{{%search}}}. We'll need to send it to template processment at a later stage.
We then assign the templates, just as defined before, without any change. And also, we do not need to change execution of query.
<code type="php">
// Assigning templates for page links creation
$pager_layout->setTemplate('[<a href="{%url}">{%page}</a>]');
$pager_layout->setSelectedTemplate('[{%page}]');
// Retrieving Doctrine_Pager instance
$pager = $pager_layout->getPager();
// Fetching users
$users = $pager->execute();
</code>
The method {{display()}} is the place where we define the custom mask we created. This method accepts 2 optional arguments: one array of optional masks and if the output should be returned instead of printed on screen.
In our case, we need to define a new mask, the {{{%search}}}, which is the search offset of {{$_GET}} superglobal array. Also, remember that since it'll be sent as URL, it needs to be encoded.
Custom masks are defined in key => value pairs. So all needed code is to define an array with the offset we desire and the value to be replaced:
<code type="php">
// Displaying page links
$pager_layout->display( array(
'search' => urlencode($_GET['search'])
) );
</code>
{{Doctrine_Pager_Layout}} component offers accessors to defined resources. There is not need to define pager and pager range as variables and send to the pager layout. These instances can be retrieved by these accessors:
<code type="php">
// Return the Pager associated to the Pager_Layout
$pager_layout->getPager();
// Return the Pager_Range associated to the Pager_Layout
$pager_layout->getPagerRange();
// Return the URL mask associated to the Pager_Layout
$pager_layout->getUrlMask();
// Return the template associated to the Pager_Layout
$pager_layout->getTemplate();
// Return the current page template associated to the Pager_Layout
$pager_layout->getSelectedTemplate();
// Return the current page template associated to the Pager_Layout
$pager_layout->getSeparatorTemplate();
</code>
There are some cases where simple paginations are not enough. One example situation is when you want to write page links listings.
To enable a more powerful control over pager, there is a small subset of pager package that allows you to create ranges.
Currently, Doctrine implements two types (or styles) of ranges: Sliding ({{Doctrine_Pager_Range_Sliding}}) and Jumping ({{Doctrine_Pager_Range_Jumping}}).
++++ Sliding
Sliding page range style, the page range moves smoothly with the current page. The current page is always in the middle, except in the first and last pages of the range.
Check out how does it work with a chunk length of 5 items:
<code>
Listing 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Page 1: o-------|
Page 2: |-o-----|
Page 3: |---o---|
Page 4: |---o---|
Page 5: |---o---|
Page 6: |---o---|
Page 7: |---o---|
Page 8: |---o---|
</code>
++++ Jumping
In Jumping page range style, the range of page links is always one of a fixed set of "frames": 1-5, 6-10, 11-15, and so on.
<code>
Listing 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Page 1: o-------|
Page 2: |-o-----|
Page 3: |---o---|
Page 4: |-----o-|
Page 5: |-------o
Page 6: o---------|
Page 7: |-o-------|
Page 8: |---o-----|
</code>
Now that we know how the different of styles of pager range works, it's time to learn how to use them:
<code type="php">
$pager_range = new Doctrine_Pager_Range_Sliding(
array(
'chunk' => 5 // Chunk length
),
$pager // Doctrine_Pager object we learned how to create in previous topic
);
</code>
What is the advantage to use this object, instead of the {{Doctrine_Pager}}? Just one; it allows you to retrieve ranges around the current page.
Look at the example:
<code type="php">
// Retrieves the range around the current page
// In our example, we are using sliding style and we are at page 1
$pages = $pager_range->rangeAroundPage();
// Outputs: [1][2][3][4][5]
echo '['. implode('][', $pages) .']';
</code>
If you build your {{Doctrine_Pager}} inside the range object, the API gives you enough power to retrieve information related to {{Doctrine_Pager_Range}} subclass instance:
<code type="php">
// Return the Pager associated to this Pager_Range
$pager_range->getPager();
// Defines a new Doctrine_Pager (automatically call _initialize protected method)
$pager_range->setPager($pager);
// Return the options assigned to the current Pager_Range
$pager_range->getOptions();
// Return the range around the current page (obtained from Doctrine_Pager
// associated to the $pager_range instance)
$pager_range->rangeAroundPage();
</code>
\ No newline at end of file
TBD
Basic customization to generate this page links generation:
« ‹ 1 2 3 4 5 › »
<code type="php">
class PagerLayout extends Doctrine_Pager_Layout
{
public function initialize()
{
}
public function display($options = array(), $return = false)
{
$str = '';
// First page
$options['page_number'] = $this->getPager()->getFirstPage();
$options['page'] = '&laquo;';
$options['url'] = $this->_parseUrl($options);
$str .= $this->_parseTemplate($options);
// Previous page
$options['page_number'] = $this->getPager()->getPreviousPage();
$options['page'] = '&lsaquo;';
$options['url'] = $this->_parseUrl($options);
$str .= $this->_parseTemplate($options);
// Current chunk
// Removing page, page_mask and url
unset($options['page']);
unset($options['page_text']);
unset($options['url']);
$str .= parent::display($options, true);
// Next page
$options['page_number'] = $this->getPager()->getNextPage();
$options['page'] = '&rsaquo;';
$options['url'] = $this->_parseUrl($options);
$str .= $this->_parseTemplate($options);
// Last page
$options['page_number'] = $this->getPager()->getLastPage();
$options['page'] = '&raquo;';
$options['url'] = $this->_parseUrl($options);
$str .= $this->_parseTemplate($options);
// Possible wish to return value instead of print it on screen
if ($return) {
return $str;
}
echo $str;
}
}
</code>
\ No newline at end of file
In real world applications, display content from database tables is a commom task. Also, imagine that this content is a search result containing thousands of items. Undoubtely, it will be a huge listing, memory expensive and hard for users to find the right item. That is where some organization of this content display is needed and pagination comes in rescue.
Doctrine implements a highly flexible pager package, allowing you to not only split listing in pages, but also enabling you to control the layout of page links.
In this chapter, we'll learn how to create pager objects, control pager styles and at the end, overview the pager layout object - a powerful page links displayer of Doctrine.
\ No newline at end of file
Paginating queries is as simple as effectively do the queries itself. {{Doctrine_Pager}} is the responsible to process queries and paginate them. Check out this small piece of code:
<code type="php">
// Defining initial variables
$currentPage = 1;
$resultsPerPage = 50;
// Creating pager object
$pager = new Doctrine_Pager(
Doctrine_Query::create()
->from( 'User u' )
->leftJoin( 'u.Group g' )
->orderby( 'u.username ASC' ),
$currentPage, // Current page of request
$resultsPerPage // (Optional) Number of results per page. Default is 25
);
</code>
Until this place, the source you have is the same as the old {{Doctrine_Query}} object. The only difference is that now you have 2 new arguments. Your old query object plus these 2 arguments are now encapsulated by the {{Doctrine_Pager}} object.
At this stage, {{Doctrine_Pager}} already sent a dummy query to database to collect useful information to allow you to access them before even execute the real query. Let's say for example you want to know how many matches were found:
<code type="php">
echo 'Total of items found: ' . $pager->getNumResults();
</code>
There are a couple of other interesting information that can be retrieved from pager before you execute the query. The API usage is listed at the end of this topic.
To run the query, the process is similar to the current existent {{Doctrine_Query}} execute call. It even allow arguments the way you usually do it. Here is the PHP complete syntax, including the syntax of optional parameters:
<code type="php">
$pager->execute([$args = array() [, $fetchType = Doctrine::FETCH_RECORD]]);
</code>
If you need access the other functionalities that {{Doctrine_Pager}} provides, you can access them through the API:
<code type="php">
// Return the total number of itens found on query search
$pager->getNumResults();
// Return the first page (always 1)
$pager->getFirstPage();
// Return the total number of pages
$pager->getLastPage();
// Return the current page
$pager->getPage();
// Defines a new current page (automatically adjust offsets and values)
$pager->setPage($page);
// Return the next page
$pager->getNextPage();
// Return the previous page
$pager->getPreviousPage();
// Return true if it's necessary to paginate or false if not
$pager->haveToPaginate();
// Return the maximum number of records per page
$pager->getMaxPerPage();
// Defined a new maximum number of records per page (automatically adjust offset and values)
$pager->setMaxPerPage($maxPerPage);
// Return the Doctrine_Query object
$pager->getQuery();
</code>
\ No newline at end of file
+++ Installation
Currently the only way you can install and use the sandbox is by svn. It is available by checking out the trunk of Doctrine. The sandbox comes loaded with generated models, sample schema files, data fixtures and a portable sqlite database to play with.
<code>
svn co http://doctrine.pengus.net/svn/trunk doctrine_trunk
cd doctrine_trunk/tools/sandbox
chmod 0777 cli
./cli
</code>
The above steps should give you a functioning sandbox. Execute the ./cli command without specifying a task will show you an index of all the available cli tasks in Doctrine.
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment