CodeIgniter Forums
Pagination: Auto split Article/Post into multiple pages - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forum-20.html)
+--- Forum: Archived Libraries & Helpers (https://forum.codeigniter.com/forum-22.html)
+--- Thread: Pagination: Auto split Article/Post into multiple pages (/thread-31988.html)



Pagination: Auto split Article/Post into multiple pages - El Forum - 07-09-2010

[eluser]PhireGuys[/eluser]
I'm old to EE but new to CI but so far I'm loving it. I needed to create a pagination but for articles that are over a certain length. For example, if an article was more than 12,000 characters, I wanted to split it into multiple pages. Since the site had a backlog of articles, we didn't want to insert an identifier in the actual articles to say where to break the page, so I needed CI to do it for me.

I came up with this solution, hopefully it will inspire others to further develop this if needed. It just goes to show you can do anything you want if you spend some time on it (took me a day and a half).

Here is my code:

Pagination.php

I used a modified version of Pagination.php (http://codeigniter.com/wiki/Pagination_Mod/) library to fulfill my needs. You should use this modified version, and note that I added a function that lets me know if the current page is the last one. Just paste it into the above paginate version:

Code:
// --------------------------------------------------------------------

/**
* Returns if this is the last page or not
*
* @access    public
* @return    boolean
*/    
function last_page()
{
    $CI =& get_instance();
    if ($CI->uri->segment($this->uri_segment) != 0)
    {
        $cur_page = $CI->uri->segment($this->uri_segment);
        
        // Prep the current page - no funny business!
        $cur_page = (int) $cur_page;
    }
    else
        $cur_page = 1;
        
    $num_pages = ceil($this->total_rows / $this->per_page);
    

    if ($cur_page == $num_pages)
        return true;
    else
        return false;
}

Controller Functions

I needed some custom functions in my controller to take care of knowing when to split an article and to make sure I'm not destroying the HTML or readability.

This function takes care of cutting the string with HTML and paragraph awareness. It looks at where you want to cut, then finds the nearest <p> tag after that and then cuts from there. This way, you won't cut in the middle of a paragraph.

Code:
/**
* word-sensitive substring function with html tags awareness
* @param text The text to cut
* @param len The maximum length of the cut string
* @param start Where to begin cutting in the string
* @returns string
**/
function substrws( $text, $len, $start )
{
    
    $raw_text_length = strlen($text);
            
    //remaining text is less then max length
    if ($raw_text_length < $len + $start)
        $len = $raw_text_length;
        
        
    //check if white space before
    if ($start - $len >= 0 || $len == $raw_text_length)
        $prev_whitespace_pos = strpos($text,"<p>", $start);
    else
        $prev_whitespace_pos = 0;
    
    //check if white space after
    if ($len + $start > $raw_text_length)
        $next_whitespace_pos = $raw_text_length;
    else
        $next_whitespace_pos = strpos($text, "<p>", $len + $start);
    if ($next_whitespace_pos === FALSE)
        $next_whitespace_pos = $len + $start;
    
    
    $text = substr($text, $prev_whitespace_pos, $next_whitespace_pos - $prev_whitespace_pos);
    

    // close unclosed html tags
    if (preg_match_all("|<([a-zA-Z]+)>|",$text,$aBuffer))
    {
        if (!empty($aBuffer[1]))
        {
            preg_match_all("|</([a-zA-Z]+)>|",$text,$aBuffer2);

            if (count($aBuffer[1]) != count($aBuffer2[1]))
            {
                foreach ($aBuffer[1] as $index => $tag)
                {
                    if( empty($aBuffer2[1][$index]) || $aBuffer2[1][$index] != $tag)
                        $text .= '</'.$tag.'>';
                }
            }
        }
    }
    return $text;
}

This next function takes care of giving me a remainder, it's needed to determine how long I want my pages to be.

Code:
function getremainder($x, $y)
{
    $value = ($x / $y);
    return $value - intval($value);
}

The Controller

Lastly, I had to actually put my code into the controller to activate the paginate and all that good stuff.

Code:
//setup paginate for long articles
$this->load->library('pagination');
if (strlen($data['post']->article) > 12000)
{
    $config['base_url'] = site_url('articles/'.$data['category_title_url'].'/'.$data['post']->title_url);
    $config['total_rows'] = strlen($data['post']->article);
    $config['num_links'] = 14;
    $config['uri_segment'] = 4;
    $config['full_tag_open'] = '<div class="pagination">Multipage Article:&nbsp;&nbsp;&nbsp;';
    $config['full_tag_close'] = '</div>';
    
    //do some math to stop tiny leftover pages
    if ($this->getremainder(strlen($data['post']->article), 8000) >= .2)
        $config['per_page'] = '8000';
    else if ($this->getremainder(strlen($data['post']->article), 7000) >= .2)
        $config['per_page'] = '7000';
    else
        $config['per_page'] = '10000';
    
    
    $this->pagination->initialize($config);
    
    //pagination: modify data output for paginate
    if ($this->uri->segment(4) == '')
    {
        $data['post']->article = $this->substrws($data['post']->article, $config['per_page'], 0);
    }
    else
    {
        $range = ($this->uri->segment(4)-1) * $config['per_page'];    //gets the offset
        $data['post']->article = $this->substrws($data['post']->article, $config['per_page'], $range);
    }
    
    //add ... continued on next page
    if (!$this->pagination->last_page())
        $data['post']->article.= '<p><strong>Continue reading on next page...</strong></p>';

}

Now, please note I have some custom stuff still in there. So if a variable is being called without you knowing what the value is, it is because I defined it earlier but didn't post it here. But $data['post']->article held my article, that is what is most important to know.


Pagination: Auto split Article/Post into multiple pages - El Forum - 07-09-2010

[eluser]PhireGuys[/eluser]
So running you through the above code, you'll see I called the standard paginate library, but then I made sure my library was more than a certain length (12000 characters in my case).

Code:
if (strlen($data['post']->article) > 12000)

If it was over that length, I wanted to do pagination, if it wasn't, no pagination was done.

The next thing was to make sure an article didn't end up with a last page that had just a few sentences on it. To do that, you'll see I used my getremainder function and you can see it in the above code. You can change the tested lengths to whatever makes sense for your site. I just tested 8000, 7000 and 10000 characters, you can test more or change these values. I wanted them >= .2 because .2 was a decent length for an individual page, in terms of characters.

The last thing that was important was to cut up my article to only show what was supposed to be shown for the given page. That is the last chunk of the code and uses my functiion substrws.

For visual, I added in final code to use my last_page function in pagination.php to put text indicating multiple pages on all pages except the last one.

I hope this helps anything looking to try this out. This seems like a lot of code, but it is pretty simple. You can copy and page everything except the last block which you'll need to customize slightly.

Take care Smile

Edit: Since everyone likes pictures, this is it implemented on a site I'm developing. Of course I made my pagination links pretty, you'll need to do that yourself.


Pagination: Auto split Article/Post into multiple pages - El Forum - 07-12-2010

[eluser]devastator[/eluser]
Thank you very much for this, That is what i need. I have laws with more than 30-40,000 characters and this will be very useful for me.


Pagination: Auto split Article/Post into multiple pages - El Forum - 07-12-2010

[eluser]PhireGuys[/eluser]
Glad someone found it useful Smile.

One important note: I made it so my page 1's url was not like /myarticle/1, but instead like /myarticle/. I changed this in my pagination.php file because I thought it silly that /myarticle/ and /myarticle/1 were both used for the first page of the article. I don't know if using /myarticle/1 will cause any problems, although I think it might not.