Solved with custom pagination: Cpagination Only Works On First Page.


(Jmunoz Dev) #1

Hello all, i am new to Yii and this forum. I am starting a new project and after some researchs i have decided to learning this framework and use it.

Now i am stuck with the pagination. I have a form, sending by POST only 2 variables to an action. This action generates the pagination, and the first page is shown well, but if i try to navigate through the CLinkPager there is no data shown, even the CLinkpager itself has disappeared.

I have 3 days lost yet with this problem. I have read almost every pagination related post without finding a solution, and i have changed my code almost 100 times. First i tried to paginate from a model function directly (that returned a CActiveDataProvider) but i was unable to even show the first page. I have seen all the pagination examples triggered from the controller, i mean always the querys are executed from the actions in the controller (in order to paginate the data).

There is no way to paginate a CActiveDataProvider object directly, for example returned from a model function?

Now the code is this, works for only first page:

Controller:




public function actionIndexSearch() {


        //here some trivial validations and a debug echo 

        if ($_POST['city'] != null && isset($_POST['type'])) {

            //I tried also defining constants for ensure the data is not lost by pagination (maybe recalling this action again without POST data...)

            //define(_CITY, $_POST['city']);

            //define(_TYPE, $_POST['type']);

            $cityId = $_POST['city'];

            $type = $_POST['type'];

            echo "dentro";

        }

        //Another echo for watching the behavior.

        echo "fuera";

        //Creating the data to paginate and its pagination:

        $c = new CDbCriteria();

        $location = Location::model()->tableName();

        $c->compare('t.event_type_id', $type, false);

        $c->join = 'inner join ' . $location . ' l on l.city_id = "' . $cityId . '"';

        $itemcount = LiveEvent::model()->count($c);

        $pages = new CPagination($itemcount);

        $pages->setPageSize(2);

        $pages->applyLimit($c);


        Yii::app()->setTheme('urbandow');

        $this->layout = 'main';

        $this->render('eventsResults', array(

            'data' => LiveEvent::model()->findAll($c),

            'pages' => $pages,

            //'cityId' => $cityId,

            //'type' => $type,

        ));

    }



In my view, for example:





 <?php

                                $this->widget('CLinkPager', array(

                                'pages' => $pages,

                                    ));

?> 


<?php

                    

                    

                    foreach ($data as $data1){

                          echo $data1->title;

                    }

                    

?>  




Ok, and in my model i had this (now it is not used, but i think if we want put all the logic in the models there should be a way to paginate it…):




public static function listByCity($cityId, $type) {


        $c = new CDbCriteria();


        $location = Location::model()->tableName();

        $c->compare('t.event_type_id', $type, false);

        $c->join = 'inner join ' . $location . ' l on l.city_id = "' . $cityId .'"';

        return new CActiveDataProvider($this, array(

            'criteria' => $c,

            'pagination' => 2,

        ));

    }



Thank you very much!


(Ankit Modi) #2

just past the proceeOutput when page is render…

for ex…




$this->render('index',array(),false,true);


(Jmunoz Dev) #3

Hello again.

I have tried it Ankit, if i use TRUE the page is not rendered. If i use FALSE the behavior doesn’t change.

http://www.yiiframework.com/doc/api/1.1/CController#render-detail

If i declare the variables $cityId=1 and $type=1 inside the action method instead of retrieving the values from the post data, the pagination works well in all pages. I mean the data is lost when the pagination buttons are used, because probably the method actionIndexSearch() is called again, and its called without recieving any post data, so the new page have no data and is shown empty (and without paginator).

How is this solved in Yii? How can i use a form to get some data and then paginate it?


(Ankit Modi) #4

Try this jquery.js fals when action was called

[font="Arial"][size="2"]$cs = Yii::app()->clientScript;[/size][/font]

[font="Arial"][size="2"] $cs->reset();[/size][/font]

[font="Arial"][size="2"] $cs->scriptMap = array([/size][/font]

[font=“Arial”][size=“2”] ‘jquery.js’ => false, // prevent produce jquery.js in additional javascript data[/size][/font]

[font="Arial"][size="2"] );[/size][/font]


(Jmunoz Dev) #5

Hello again, thanks for the reply! But it hasn’t solved my problem :(

I tried also disable al jquery in the page without success.

I still searching the way to paginate with Yii :(


(Ankit Modi) #6

Hi,

can you please post the admin view?

Just conform you can use the Clink Pager or not?


(Jmunoz Dev) #7

I attach the file, the line 107 is where i implement the pagination, thanks!


(Ankit Modi) #8

Hi Man,

As a friend I think your code is not correct and it’s not proper way…

please don’t keep in mind…

Please use this proper Yii syntax…

[size=2]http://www.yiiplayground.com/index.php?r=UiModule/dataview/gridView[/size]

[size=2]

[/size]

[size=2]if you have any query please contact me on Skype : amodi06 [/size]

[size=2]if I am available I will help you.[/size]

[size=2]

[/size]

[size=2]Regards,[/size]

[size=2]Ankit Modi[/size]


(Jmunoz Dev) #9

Hello Again.

Finally i didn’t understand how to use the Yii pagination alone (without any other helper like gridview), so i have solved the pagination problem creating it by AJAX from scratch by my self.

Thanks anyway!


(Traprajith) #10

This is easy to implement.

In controller or in the same view

This is Just an example.




                $criteria = new CDbCriteria;

		$criteria->compare('is_deleted',0);  // normal DB field

                $criteria->order = 'first_name ASC';

		

		$total = Students::model()->count($criteria);

		$pages = new CPagination($total);

        $pages->setPageSize(Yii::app()->params['listPerPage']);

        $pages->applyLimit($criteria);  // the trick is here!

		$posts = Students::model()->findAll($criteria);

		

		 

		$this->render('manage',array('model'=>$model,

		'list'=>$posts,

		'pages' => $pages,

		'item_count'=>$total,

		'page_size'=>Yii::app()->params['listPerPage'],)) ;






In view file




<?php 

                              $this->widget('CLinkPager', array(

                              'currentPage'=>$pages->getCurrentPage(),

                              'itemCount'=>$item_count,

                              'pageSize'=>$page_size,

                              'maxButtonCount'=>5,

                              //'nextPageLabel'=>'My text >',

                              'header'=>'',

                            'htmlOptions'=>array('class'=>'pages'),

                            ));?>




(Traprajith) #11

Avoid POST use GET


(Jmunoz Dev) #12

Hi all again. I’m coming to show you how i did it.

I used a jQuery pagination plugin for navigation buttons, making a custom AJAX call to an action in my handmade AjaxController, then the action do the logic part of the pagination and finally it returns to the AJAX a renderpartial of a custom view filled with the paginated data.

The Ajax call trigged:




function realizaProceso(id, level, limit, current){

    

    var parametros = {

            "id" : id,

            "level" : level,

            "current" : current,

            "limit" : limit

    };


    $.ajax({

            data:  parametros,

            url:   "<?php echo Yii::app()->createUrl('ajax/HotelAjaxPagination'); ?>",

            type:  'post',

            beforeSend: function () {

                    //$("#content").html("Procesando, espere por favor...");

            },

            success:  function (response) {

                    $("#content").html(response);

            }

    });




}



The action in the AjaxController. It also has a dynamic tablename selection based on the "level" parameter given.




public function actionHotelAjaxPagination() {


        $id = $_POST['id'];

        $level = $_POST['level'];

        $limit = $_POST['limit'];

        $current = $_POST['current'];

        

        $searchCriteria = new CDbCriteria(); 

        $searchCriteria->select = "t.*";

        $searchCriteria->order = "t.title asc";

        $searchCriteria->offset = ($current*PAGINATION_ITEM_LIMIT);

        $searchCriteria->limit = (PAGINATION_ITEM_LIMIT); // or $limit

        

        $baseTableName = "country";

        

        if ($level > 0){

            for ($i = 0; $i < $level; $i++)

                $baseTableName.="_sub";

            $crossTableName = $baseTableName."_to_hotel";

            $fk = $baseTableName . "_id";

            $searchCriteria->join = "INNER JOIN ". $crossTableName ." ct ON ct.hotel_id = t.id";

            $searchCriteria->addCondition("ct.".$fk." = ".$id);            

            $data = Hotel::model()->findAll($searchCriteria);  

        }else{

            $searchCriteria->join = "INNER JOIN country_sub cs ON t.country_sub_id = cs.id";

            $searchCriteria->addCondition("cs.country_id = ".$id);

            $data = Hotel::model()->findAll($searchCriteria);

        }

         

        $this->renderPartial('hotelSearchPagination', array('data' => $data, 'current' => $current, 'count' => count($data)));


        Yii::app()->end();

    }



And just iterate over results in our hotelSearchPagination view file to show whatever we want.

The jQuery plugin i’m using:





jQuery.fn.pagination = function(maxentries, opts){

    

	opts = jQuery.extend({

		num_display_entries:5,

		num_edge_entries:0,

		link_to:"#",

		prev_text:"First ",

		next_text:" Last",

		ellipse_text:"...",

		prev_show_always:true,

		next_show_always:true,

		callback:function(){return false;}

	},opts||{});

	

	return this.each(function() {

		/**

		 * Calculate the maximum number of pages

		 */

		function numPages() {

			return Math.ceil(maxentries/opts.items_per_page);

		}

		

		/**

		 * Calculate start and end point of pagination links depending on 

		 * current_page and num_display_entries.

		 * @return {Array}

		 */

		function getInterval()  {

			var ne_half = Math.ceil(opts.num_display_entries/2);

			var np = numPages();

			var upper_limit = np-opts.num_display_entries;

			var start = current_page>ne_half?Math.max(Math.min(current_page-ne_half, upper_limit), 0):0;

			var end = current_page>ne_half?Math.min(current_page+ne_half, np):Math.min(opts.num_display_entries, np);

			return [start,end];

		}

		

		/**

		 * This is the event handling function for the pagination links. 

		 * @param {int} page_id The new page number

		 */

		function pageSelected(page_id, evt){

			current_page = page_id;

			drawLinks();

			var continuePropagation = opts.callback(page_id, panel);

			if (!continuePropagation) {

				if (evt.stopPropagation) {

					evt.stopPropagation();

				}

				else {

					evt.cancelBubble = true;

				}

			}


                        ////////////////////// MY ACTION:

                        updateContent(current_page);

                        

			return continuePropagation;

		}

		

		/**

		 * This function inserts the pagination links into the container element

		 */

		function drawLinks() {

			panel.empty();

			var interval = getInterval();

			var np = numPages();

			// This helper function returns a handler function that calls pageSelected with the right page_id

			var getClickHandler = function(page_id) {

				return function(evt){ return pageSelected(page_id,evt); }

			}

			// Helper function for generating a single link (or a span tag if it's the current page)

			var appendItem = function(page_id, appendopts){

                            

                                    page_id = page_id<0?0:(page_id<np?page_id:np-1); // Normalize page id to sane value

                                    appendopts = jQuery.extend({text:page_id+1, classes:""}, appendopts||{});

                                    if(page_id == current_page){

                                            if (isNaN(appendopts.text))

                                                var lnk = jQuery("<a class='enlace_paginacion' href='#'> "+(appendopts.text)+"</span>");

                                            else

                                                var lnk = jQuery("<a class='enlace_paginacion_activo' href='#'> "+(appendopts.text)+"</span>");

                                    }

                                    else

                                    {

                                            var lnk = jQuery("<a class='enlace_paginacion'>"+(appendopts.text)+"</a>")

                                                    .bind("click", getClickHandler(page_id))

                                                    .attr('href', opts.link_to.replace(/__id__/,page_id));

                                    }

				

				if(appendopts.classes){lnk.addClass(appendopts.classes);}

				panel.append(lnk);

			}

			// Generate "Previous"-Link

			if(opts.prev_text && (current_page > 0 || opts.prev_show_always)){

				appendItem(0,{text:opts.prev_text, classes:"prev"});

			}

//			// Generate starting points

			if (interval[0] > 0 && opts.num_edge_entries > 0)

			{

				var end = Math.min(opts.num_edge_entries, interval[0]);

				for(var i=0; i<end; i++) {

					appendItem(i);

				}

				if(opts.num_edge_entries < interval[0] && opts.ellipse_text)

				{

					jQuery("<span>"+opts.ellipse_text+"</span>").appendTo(panel);

				}

			}

			// Generate interval links

			for(var i=interval[0]; i<interval[1]; i++) {

				appendItem(i);

			}

			// Generate ending points

			if (interval[1] < np && opts.num_edge_entries > 0)

			{

				if(np-opts.num_edge_entries > interval[1]&& opts.ellipse_text)

				{

					jQuery("<span>"+opts.ellipse_text+"</span>").appendTo(panel);

				}

				var begin = Math.max(np-opts.num_edge_entries, interval[1]);

				for(var i=begin; i<np; i++) {

					appendItem(i);

				}

				

			}

			// Generate "Next"-Link

			if(opts.next_text && (current_page < np-1 || opts.next_show_always)){

				appendItem(numPages(),{text:opts.next_text, classes:"next"});

			}

		}

		

		// Extract current_page from options

		var current_page = opts.current_page;

		// Create a sane value for maxentries and items_per_page

		maxentries = (!maxentries || maxentries < 0)?1:maxentries;

		opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0)?1:opts.items_per_page;

		// Store DOM element for easy access from all inner functions

		var panel = jQuery(this);

		// Attach control functions to the DOM element 

		this.selectPage = function(page_id){ pageSelected(page_id);}

		// When all initialisation is done, draw the links

		drawLinks();

        // call callback function

        opts.callback(current_page, this);

	});

}



Thank you all for your answers.


(Traprajith) #13

:) :)