Laravel5のページング機能に表示件数の可変を実装する方法

この書き方が最善であるとは考えていません。いませんが、私のプログラミング能力が低すぎるのでこれ以外の方法が思いつかず。。いい方法あったら教えてちょ

### 動作環境

* Laravel 5.2.31
* CentOS 6.8
* PHP 5.6.22
* PostgreSQL 9.5.3

### 失敗例

Controller側 「ItemsController.php」

```
<?php // ファイルパス app/Http/Controllers/Admin/ItemsController.php

namespace App\Http\Controllers\Admin; // Controllerファイルのディレクトリが違う場合は修正する

use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;

use App\ItemsModel; // 使用するモデルファイルをここに記述

class ItemsController extends Controller
{
	public function index()
	{
		$items = ItemsModel::paginate(5);
		return view('.index',['items' => $items]);
	}
}

?>
```

Model側 「ItemsModel.php」

```
<?php // ファイルパス app/ItemsModel.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ItemsModel extends Model
{
	//
}

?>
```

View側 「index.blade.php」

```
<html>
<head></head>
<body>
	<div class="index">
		<h4>一覧</h4>
		<div class="pagination">
			{{ $items -> render() }}
		</div>
		<div>
		<p>総件数: {{ $items -> total() }}件</p></div>
		<form action="/admin" method="get">
			表示件数：
			<select id="" name="disp_list" onchange="submit();">
				<option value="">選択してください</option>
				<option value="5">5</option>
				<option value="10">10</option>
				<option value="20">20</option>
				<option value="50">50</option>
				<option value="100">100</option>
			</select>
		</form>
		@foreach ($items as $item)
			{{ $item -> body }}
			<br>
		@endforeach
	</div>
</body>
</html>
```

### 何が問題か

Controllerに上のような感じでかくと確かに簡単にページネーションはできるんですが、表示件数の選択ができない。
ずっと5件ずつ表示。できればここを5,10,20,50,100件から選択したいですよね。
なのでイメージとしてはこんな感じ

```
public function index(Request $disp_list)
{
	$items = ItemsModel::paginate($disp_list);
	return view('.index',['items' => $items]);
}
```

formとかでname="disp_list"のvalueをsubmitして、その値をpaginateに代入するイメージ。

実際それで動くんですが、これだとURLの末尾に「?disp_list=5」とかクエリパラメータがつきます。
問題が発生するのは2ページ目に移動したとき。ページャーの2のボタンをクリックすると、
URLの末尾が「?page=2」になり、disp_listのパラメータが消えます。

それにより、paginateの数値がなくなるので、画面には何も表示されません。おわた。

だからイメージとしては、「?disp_list=5&page=2」ってなるようにしたいですよねえ

### 実装

次のような書き方をします。

Controller側 「ItemsController.php」

```
<?php // ファイルパス app/Http/Controllers/Admin/ItemsController.php

namespace App\Http\Controllers\Admin; // Controllerファイルのディレクトリが違う場合は修正する

use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;

use App\ItemsModel; // 使用するモデルファイルをここに記述

class ItemsController extends Controller
{

    public function index(Request $disp_list)
    {
        $items = ItemsModel::paginate($disp_list);
        return view('.index',['items' => $items]);
    }
}

?>
```

Model側 「ItemsModel.php」

```
<?php // ファイルパス app/ItemsModel.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ItemsModel extends Model
{
	//
}

?>
```

View側 「index.blade.php」

```
<html>
<head></head>
<body>
	<div class="index">
		<h4>一覧</h4>
		<div class="pagination">
			{{ $items -> appends(['disp_list' => $_GET['disp_list']]) -> render() }}
		</div>
		<div>
		<p>総件数: {{ $items -> total() }}件</p></div>
		<form action="/admin" method="get">
			表示件数：
			<select id="" name="disp_list" onchange="submit();">
				<option value="">選択してください</option>
				<option value="5">5</option>
				<option value="10">10</option>
				<option value="20">20</option>
				<option value="50">50</option>
				<option value="100">100</option>
			</select>
		</form>
		@foreach ($items as $item)
			{{ $item -> body }}
			<br>
		@endforeach
	</div>
</body>
</html>
```

### ポイント

Before

```
<div class="pagination">
	{{ $items -> render() }}
</div>
```

After

```
<div class="pagination">
	{{ $items -> appends(['disp_list' => $_GET['disp_list']]) -> render() }}
</div>
```

appendsというものがクエリパラメータの追加を可能にしています。
そして、$_GET['disp_list']により、formの中のname属性のうち、disp_listのもののvalueを格納できます。

これで、2ページ目にいっても表示件数は引き継がれ、期待通りの動きができるようになりました。やったぜ。

### 懸念

ユーザーが送信したvalueをそのまんま受け取るので、セキュリティの観点からこの実装は危険でしょうねえ。
まあそこは追い追い考えるということで。

### 追記

上のやつでひとまず要件は満たせましたが、例外処理がまだでしたね。
テーブルにレコードが20件あったとして、10件表示設定にしていれば、page=2までは表示されますが、
このとき、page=3にアクセスすると中身が表示されませんがページ自体は表示される、つまりレイアウト崩れが発生します。
これは期待動作ではないので、そういう存在しないページにアクセスした場合に404 Not Found ページに飛ばす方法を追記します。

### 404 ページを作る

View 「404.blade.php」 パスはviews/errors/404.blade.php

```

<html>
	<head></head>
	<body>
		<div>
			<p>何かエラーの内容を書く</p>
		</div>
	</body>
</html>

```

### Controller に例外処理を記述

Controller側 「ItemsController.php」

```
<?php // ファイルパス app/Http/Controllers/Admin/ItemsController.php

namespace App\Http\Controllers\Admin; // Controllerファイルのディレクトリが違う場合は修正する

use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;

use App\ItemsModel; // 使用するモデルファイルをここに記述

use Illuminate\Support\Facades\Input; // これを新しく追加

class ItemsController extends Controller
{

    public function index(Request $disp_list)
    {
        $items = ItemsModel::paginate($disp_list);
        
		if ( Input::get('page') > $items -> LastPage() ) { // 入力した page= の値が 要素の最終ページ数より多い場合 例: page=3 > page=2
			abort(404); // resource/views/errors/404.blade.php を表示
		}

        return view('.index',['items' => $items]);
    }
}

?>
```

### さらに追記

デフォルト値の設定を忘れていたとです。
URLに?disp_list= のクエリパラメータがなかった場合は、デフォルトの表示件数を表示するようにします。

```
<?php // ファイルパス app/Http/Controllers/Admin/ItemsController.php

namespace App\Http\Controllers\Admin; // Controllerファイルのディレクトリが違う場合は修正する

use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;

use App\ItemsModel; // 使用するモデルファイルをここに記述

use Illuminate\Support\Facades\Input; // これを新しく追加

class ItemsController extends Controller
{

    public function index(Request $disp_list)
    {

		if(empty($disp_list)) { // ?disp_list= が空値、またはURLになかった場合
			$disp_list = 3; // デフォルトの表示件数をセット
		}

        $items = ItemsModel::paginate($disp_list);

        if ( Input::get('page') > $items -> LastPage() ) { // 入力した ?page= の値が 要素の最終ページ数より多い場合 例: page=3 > page=2
            abort(404); // resource/views/errors/404.blade.php を表示
        }

        return view('.index',['items' => $items]);
    }
}

?>
```