46

I am trying to create a page where I can see all the people in my database and create edits on them. I made a form where I fill in the data from the database of certain fields.

I would like to navigate trough them by a Next and Previous button.

For generating the next step I have to take the ID larger than the current one to load the next profile.

For generating the previous step I have to take the ID smaller than the current one to load the previous profile.

My route:

Route::get('users/{id}','UserController@show');

Controller:

public function show($id)
    {

        $input = User::find($id);

        // If a user clicks next this one should be executed.
        $input = User::where('id', '>', $id)->firstOrFail();



        echo '<pre>';

        dd($input);

        echo '</pre>';

        return View::make('hello')->with('input', $input);
    }

View: The buttons:

<a href="{{ URL::to( 'users/' . $input->id ) }}">Next</a>

What is the best approach to get the current ID and increment it?

Duikboot
  • 1,093
  • 1
  • 12
  • 24
  • I can't answer the question unfortunately (though I'd probably also use your approach and then wonder if there's a better way as you are), but I think `firstOrFail` could end up being an issue: when the user gets to view the last model, rather than being able to see it they get a 404 page, just because there's no 'next' model available. – alexrussell Feb 20 '14 at 13:51

13 Answers13

114

Below are your updated controller and view files derived from @ridecar2 link,

Controller:

public function show($id)
{

    // get the current user
    $user = User::find($id);

    // get previous user id
    $previous = User::where('id', '<', $user->id)->max('id');

    // get next user id
    $next = User::where('id', '>', $user->id)->min('id');

    return View::make('users.show')->with('previous', $previous)->with('next', $next);
}

View:

<a href="{{ URL::to( 'users/' . $previous ) }}">Previous</a>
<a href="{{ URL::to( 'users/' . $next ) }}">Next</a>
57
// in your model file
public function next(){
    // get next user
    return User::where('id', '>', $this->id)->orderBy('id','asc')->first();

}
public  function previous(){
    // get previous  user
    return User::where('id', '<', $this->id)->orderBy('id','desc')->first();

}
// in your controller file
$user = User::find(5); 
// a clean object that can be used anywhere
$user->next();
$user->previous();
Alireza
  • 1,706
  • 16
  • 23
8

In your App\Models\User.php

...
protected $appends = ['next', 'previous'];

public function getNextAttribute()
{
    return $this->where('id', '>', $this->id)->orderBy('id','asc')->first();
}

public function getPreviousAttribute()
{
    return $this->where('id', '<', $this->id)->orderBy('id','asc')->first();
}

In your Controller you can simply do this:

public function show(User $user)
{
    return View::make('users.show')
    ->with('user', $user)
    ->with('previous', $user->previous)
    ->with('next', $user->next);
}
Shreyansh Panchal
  • 827
  • 12
  • 22
4

I understand the approach being taken here by user2581096 but I am not sure it is efficient (by any standards). We are calling the database 3 times for really no good reason. I suggest an alternative that will be way more efficient and scalable.

Do not pass the previous and next IDs to the view. This eliminates 2 unnecessary database calls.

Create the following routes:

users/{id}/next

users/{id}/previous

These routes should be used in the href attributes of the anchor tags

Add methods in the controller to handle each of the new routes you have created. For example:

 public  function getPrevious(){
        // get previous  user
        $user = User::where('id', '<', $this->id)->orderBy('id','desc')->first();
        return $this->show($user->id);
    }

This function will only be called when you actually click on the button. Therefore, the database call is only made when you need to actually look up the user.

Nikhil Agarwal
  • 410
  • 4
  • 15
  • this is a good tip, but what if you want to show the prev & next records in the view of the current id ? ur current approach work if we need the prev & next as a simple pagination btns – ctf0 Oct 08 '16 at 21:40
1

in-case you want to retrieve the prev/next records along with their data, you can try

$id   = 7; // for example

$prev = DB::table('posts')->where('id', '<', $id)->orderBy('id','desc')->limit(1);
$next = DB::table('posts')->where('id', '>', $id)->limit(1);

$res = DB::table('posts')
        ->where('id', '=', $id)
        ->unionAll($prev)
        ->unionAll($next)
        ->get();

// now $res is an array of 3 objects
// main, prev, next
dd($res);

1- the query builder is usually much faster than eloquent.

2- with union we are now only hitting the db once instead of 3.

ctf0
  • 6,991
  • 5
  • 37
  • 46
1

To get next and previous post we can use max and min functions on Model id in laravel. here is an example to get this https://usingphp.com/post/get-next-and-previous-post-link-in-laravel The Controller:

public function post($id)
{
    $post = Post::find($id);
    $previous = Post::where('id', '<', $post->id)->max('id');
    $next = Post::where('id', '>', $post->id)->min('id');
    return view( 'post', compact( 'post', 'next', 'previous' ));
}

The View:

@if($next)
   <a href="{{ route( 'blog.show', $next->id ) }}">{{$next->title}}</a>
@endif
@if($previous)
   <a href="{{ route( 'blog.show', $previous->id ) }}">{{$previous->title}}</a>
@endif
Abilogos
  • 4,777
  • 2
  • 19
  • 39
0

Here's a link I found that should help: http://maxoffsky.com/code-blog/laravel-quick-tip-get-previous-next-records/

It looks like for next you want to use: $next = User::where('id', '>', $id)->min('id'); and have the view as: <a href="{{ URL::to( 'users/' . $next->id ) }}">Next</a>

Also don't forget to pass $next to the view.

ridecar2
  • 1,968
  • 16
  • 34
0

Simplest approach

// User.php
public static function findNext($id)
{
    return static::where('id', '>', $id)->first();
}

// UserController.php
$nextUser = User::findNext($id);

// view
<a href="{{ URL::to( 'users/' . $nextUser->id ) }}">Next</a>

Lazy approach :

// view
<a href="{{ URL::to( 'users/' . $input->id . '/next') }}">Next</a>

// routes.php (should be optimized, this is just to show the idea)
Route::get('users/{user}/next', function($id) {
    $nextUser = User::findNext($id);
    return Redirect::to('user/' . $id);
});
Alexandre Butynski
  • 6,625
  • 2
  • 31
  • 44
0
// yourModel.php
public function previous()
{
   return $this->find(--$this->id);
}

public function next()
{
   return $this->find(++$this->id);
}

Works like magic, you can chain it:

$prevprev = Model::find($id)->previous()->previous();
$nextnext = Model::find($id)->next()->next();
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
  • 5
    and what if we have deleted the previous or next id? i think other answers are better than this – Matthew Jul 31 '20 at 08:46
  • 2
    this will only work if no rows are ever deleted from that table. Otherwise it's not going to work as expected. – Duilio Aug 03 '20 at 14:58
0

First, get a record out of the database.

    $post = Post::where('slug', $slug)->first();

With a database record, we can get the previous record where the record id is less than the id stored inside $post order by the id in descending order and use first() to get a single record back.

    $previous = Post::where('id', '<', $post->id)->orderBy('id','desc')->first();

To get the next record it's almost the same query, this time get the record where the id is more than the id stored in $post.

    $next = Post::where('id', '>', $post->id)->orderBy('id')->first();
Tumelo Mapheto
  • 495
  • 8
  • 10
0

Controller:

public function show($id)
{
    // get the current user
    $user = User::find($id);

    // get previous user id
    $previous = User::offset($user->id-2)->first();

    // get next user id
    $next = User::offset($user->id)->first();

    return View::make('users.show')->with('previous', $previous)->with('next', $next);
}
Yusef Maali
  • 2,201
  • 2
  • 23
  • 29
0

I tried to get next and previous sorted by text, not by ID, eg. next user name, next movie alphabetically. And faced in Laravel 9-10 some difficulties and problems when a record in the database is first or last (doesn't exist next or previous link). The easiest way for me or many examples returns empty object or find something wrong, strange record if it found nothing or throws an error in view, or it was a collection with one record and not an object.

Example in the controller that finally works for me:

I have a current user object loaded...$user;

$previous_value = User::where('user', '<', $user->user)->max('user'); //get value of previous user name
$next_value = User::where('user', '>', $user->user)->min('user'); //get value of next user name
    
$previous = ($previous_value) ? User::where('user', $previous_value)->first() : ""; //get object of previous user based on previous search when you need it or empty value passed to view.
$next = ($next_value) ? User::where('user', $next_value)->first() : "";

In view, I have just

@if($previous) ... do something <a href="route..etc." Title="$previous->desc">{{$previous->name}}</a>@endif //condition I must use because, in the first and last record, it is empty and throws an error.

I don't care about a number of 4 simple SQL queries for this and don't want another nice solution with new links and searching for the next record after clicking on it; the page is cached and links directly to the next record page.

Petr Novotny
  • 198
  • 2
  • 6
-1

i developed the code.

it work all times, even if we don't have any next or prev post

public function nextPost($table, $id)
{
    $next = DB::table($table)->where('id', '>', $id)->orderBy('id','asc')->first();
    if(!$next)
        $next = DB::table($table)->orderBy('id','asc')->first();

    return $next;
}

public function prevPost($table, $id)
{
    $prev = DB::table($table)->where('id', '<', $id)->orderBy('id','desc')->first();
    if(!$prev)
        $prev = DB::table($table)->orderBy('id','desc')->first();
    return $prev;
}
mostafaznv
  • 958
  • 14
  • 24
  • This is a bad example. If there is no result, you just return a result. The user will think that there are more items left, which is not the case. – Douwe de Haan Apr 03 '19 at 09:28