If requesting for a page say page number 3 in paginated items of a table in cakephp,
which is having only 2 pages, how do we show the last page ie page number 2?
I found out the solution here:-
cakephp paginator helper shows error when delete the last record from the last page
public function index() {
try {
$paginatedData = $this->Paginator->paginate();
} catch (NotFoundException $e) {
//get current page
$page = $this->request->params['named']['page'];
if( $page > 1 ){
//redirect to previous page
$this->redirect( array( "page" => $page-1 ) );
}else{
$paginatedData = array(); //no data to paginate so use empty array()
//you will have to check for this in the view and no longer display the pagination links, since they will NOT be defined
}
}
}
Related
I allow the user to delete items from paginated content. After deleting the item the user requests i redirect them back. I however have noticed this is buggy because if the page contains only one item and the user deletes it they are redirected to the same page which now has no records.I have 11 items on each page and use the default Laravel pagination. How can i enforce a mechanism that redirects user to the previous page if the current page is blank after deletion of the last or only item?
You can try to do simple check in controller and redirect users manually. The code just shows the idea:
$result = Model::paginate(10);
if (count($result) === 0) {
$lastPage = $result->lastPage(); // Get last page with results.
$url = route('my-route').'?page='.$lastPage; // Manually build URL.
return redirect($url);
}
return redirect()->back();
You can check the no. of results, if its less than 1 then you can redirect to the previousPageUrl as:
if ($results->count()) {
if (! is_null($results->previousPageUrl())) {
return redirect()->to($results->previousPageUrl());
}
}
I solved it by doing a Redirect::back() on the delete function. This led to the paginator function where l did the following:
//if an item was deleted by the user and it was the only on that page the number of
//items will be zero due the page argument on the url. To solve this we need to
//redirect to the previous page. This is an internal redirect so we just get the
//current arguments and reduce the page number by 1. If the page argument is not
//available it means that we are on the first page. This way we either land on the
//first page or a previous page that has items
$input = Input::all();
if( (count($pages) == 0) && ( array_key_exists('page', $input) ) ){
if($input['page'] < 2){
//we are headed for the first page
unset($input['page']);
return redirect()->action('MyController#showItems', $input);
}
else{
//we are headed for the previous page -- recursive
$input['page'] -= 1;
return redirect()->action('MyController#showItems', $input);
}
}
To implement such a logic in the controller method, destroy(), we need to know the current page number, and the items count (before we delete this item).
Pass these two pieces of information through the DELETE link, and retrieve them using the Request input method.
In your view:
<form action="{{'/items/'.$item->id.'?current_page='.$items->currentPage().'&items_count='.$items->count()}}" method="POST">
#method('DELETE')
#csrf
<button>Delete</button>
</form>
In your controller:
public function destroy(Item $item, Request $request)
{
$item->delete();
$itemsCount = $request->input('items_count');
$currentPage = $request->input('current_page');
$url = $itemsCount > 1 ? '/items/?page='.$currentPage : '/items/?page='.($currentPage - 1);
return redirect($url);
}
If it's the last page (1), then the page number becomes 0, which will still return the correct response (empty items list).
For the routes, implement a route with the two parameters, with the controller's destroy method as the callback.
If you have a resource controller, then this route has to be before the latter's route.
web.php
Route::DELETE('/items/{item_id}?current_page={number}&items_count={count}', 'ItemController#destroy');
Route::resource('/items', 'ItemController');
I am working on a website written in Yii framework (version 1.1.14) that allows uploading and displaying news. The admin of the site can select three news to promote to homepage and specify the order in which they are displayed. I am using Mysql database. The news table has two fields: isChecked (0 or 1) and homepagePos (integer) in addition to the other fields. The isChecked field determines whether the news is selected for displaying in homepage and the homepagePos field determines the order in which the news are displayed. I have used jquery's sortable plugin to sort the news. When the user selects which news to display and clicks save button, the news ids are sent to php via ajax.
The javascript portion to send the values to news controller is as follows:
$(document).on('click', '#saveToHomepage', function()
{
var url = ajaxRequestSendUrl; //ajaxRequestSendUrl contains url to news controller's promote to homepage method.
$.ajax({
method: "GET",
url: url,
data: {
contentIds: contentIds, //contentIds contains an array of news Ids in certain order
},
success: function() {
// Show success message
},
error: function() {
alert('Some error occured. Please reload the page and try again.');
}
});
});
Here's the promote to homepage method in news controller:
public function actionHomepage()
{
$allNews = News::model()->findAll();
$value = $_GET['contentIds'];
foreach ($allNews as $news) {
if($news->id == $value[0] ||$news->id == $value[1] ||$news->id == $value[2])
{
$news->isChecked = 1;
$news->homepagePos = array_search($news->id, $value); //Assign index of the array as the position
$news->save();
}
else
{
$news->isChecked = 0;
$news->homepagePos = -1;
$news->save();
}
}
}
My problem is that the news table I have has over 2k data. So the ajax call takes really long time (over a minute) to complete. Is there any way I can optimize the code or is there other way I can approach this to reduce the time taken to complete this operation?
Thanks in advance
Three queries: One first to set the whole table to not checked status, and the rest to set the checked status only in the row each selected id
public function actionHomepage()
{
$values = $_GET['contentIds'];
$sql = "UPDATE news SET idChecked=0,homepagePos = -1";
Yii::app()->db
->createCommand($sql)
->execute();
for($ii = 0; $ii < 3; $ii++) {
$sql = "UPDATE news SET idChecked = 1,homepagePos = ':homepagePos' WHERE id=:id";
Yii::app()->db
->createCommand($sql)
->bindValues(array(':homepagePos' => array_search($ii, $values), ':id' => $values[$ii]))
->execute();
}
}
i am not sure, but why don't you get the filtered records from the database itself by sending the id's of selected news. I don't know your backend language. Seems, you are getting all the records and applying filtering. which consumes time. Instead get the filtered news from the database.
Hope this helps!
Its taking a long time because you're fetching all the records and then updating those three id. You could try this:
$criteria = new CDbCriteria;
$criteria->addInCondition( "id" , $value ) ; // $value = array ( 1, 2, 3 );
News::model()->updateAll(array('isChecked'=>'1','homepagePos'=>'val2'), $criteria);
You could do an update before hand to reset all the homepagePos and then update only the ids you need.
In my index (/players) view I have a carousel with images of a bunch of players. When I click on an Image I go to the view screen of the clicked player (/players/view/1).
Now I want to only have an index screen with that carousel of player images and after I click on an Image I want the information of that player on that same index screen.
How would I best combine the index and view so that I can click on a player image and retrieve his information on the same page? The goal is to have one view file and one action in the controller. So how can I put find('all') and find('first') in one action?
Now I have /players and /players/view/1.
I want to have for instance /players/1 so it loads on the same page. However this will still give a page load I think.
Eventually I don't want a page load, but only a content change.
index action of PlayersController (gives me all the players) :
public function index() {
$this->layout = 'default_front_players';
$this->Player->recursive = 0;
//$this->Player->find('all');
$this->set('players', $this->Paginator->paginate());
}
view action of PlayersController (gives me the player that has been clicked) :
public function view($id = null) {
$this->layout = 'default_front_players';
if (!$this->Player->exists($id)) {
throw new NotFoundException(__('Invalid player'));
}
$options = array('conditions' => array('Player.' . $this->Player->primaryKey => $id));
$this->set('player', $this->Player->find('first', $options));
}
UPDATE
First case
This gives me all the players and gives me also one player (atm player 143, the last one in the database), problem is that when I click on a players image the player stays on player 143. The url changes to players/index/{number of clicked player}
public function index($id = null) {
$this->layout = 'default_front_players';
$this->Player->recursive = 0;
$players = $this->Player->find('all');
$this->set(compact('players'));
$options = array('conditions' => array('Player.' . $this->Player->primaryKey => $id));
$player = $this->set('player', $this->Player->find('first', $options));
}
Second case
This doesn't shows me the player images, but when I change the url, it gives me the content of the player which id I give in the URL.
public function index($id = null) {
$this->layout = 'default_front_players';
$this->Player->recursive = 0;
//$players = $this->Player->find('all');
//$this->Paginator->settings = $this->paginator_players;
$this->set('players');
$options = array('conditions' => array('Player.' . $this->Player->primaryKey => $id));
$player = $this->set('player', $this->Player->find('first', $options));
}
If I go to /players or players/index I don't get any values.
How can I combine these 2?
Have you tried ajax and routes to achieve a content refresh ( essentially creating your own api call to internal resources )? I use cakephp for my own side project, and needed a way to circumvent the whole controller, view load mechanism for a faster UI experience.
The gist is including jquery in your template or layout file. When a certain route or url is called ( this requires a route to be setup in your routes.php file in app/config relatively from your apps root folder ) , fire the ajax call to fetch the content and dynamically update the dom without having to reload the page.
This suggestion might open up a whole new can of worms for you, but its achievable I think.
i implement the Yii infinite scroll extension. its working fine. its scrolling 10 by 10 from the database dynamically at last 10 values are repeated one more time.
i simply printing text values only but at last row ie last 10 values are repeated once.
i added my code here :
site controller
$criteria = new CDbCriteria;
$total = Recipe::model()->count();
$pages = new CPagination($total);
$pages->pageSize = 10;
$pages->applyLimit($criteria);
$posts = Recipe::model()->findAll($criteria);
$this->render('index', array(
'posts' => $posts,
'pages' => $pages,
));
my index page here:
<div id="posts">
<?php foreach($posts as $rec): ?>
<?php $rec_id=$rec['recipe_id']; $name=$rec['name']; ?>
<?php echo $name=$rec['name']; ?>
then i added my extension below
Infinite scroll ( and yii-infinite scroll) stops querying the server when it it hits a 404 error. on the next page. Yii framework keeps returning the last results for page numbers greater than what the result has.
This is why you keep seeing duplicated rows at the end of the results.. to prevent this add this piece of code to your corresponding action in your controller.
if(isset($_GET['Model_page'])){
if($model->search()->pagination->pageCount < $_GET['Model_page']+1){
throw new CHttpException(404,'No More results');
}
}
Where Model_page is to be substituted with Post_page etc depending on your model name and $model->search() is your dataProvider, if you have some other dataProvider please use that directly after it has been declared.
I want to paginate query results in CodeIgniter to look like this:
Problem 1 :
The Pagination class always outputs numeric links. I just want to show next and back links.
Problem 2 :
$data['links'] = $this->pagination->create_links(); returns all of the links as a string. How can I separate the next and back links and put next to the right and back to the left ?
suppose url is: http://localhost/controller/method/
do following in your controller function
...
function method($page_num)
{
...
$data['next_link'] = $page_num + 1;
$data['prev_link'] = $page_num;
...
$this->load->view('<veiw_name>', $data);
}
do this in your view
...
Prev
Next
....