Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Laravel の paginate は一体何をやっているのか

1,156 views

Published on

2018-06-27 開催の「第127回 PHP勉強会@東京」におけるLT資料です

https://phpstudy.doorkeeper.jp/events/76011

Published in: Software
  • 個人的にEloquentを広範囲に跨らせるのが嫌でインフラ層に閉じ込めていた関係で、Paginatorを自前実装をしていました。その時、僕もなぜcurrentPageを取得できるのか疑問に思ったのですが、このスライドで理解できました。ありがとうございます!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Laravel の paginate は一体何をやっているのか

  1. 1. Laravel の paginate は 一体何をやっているのか 第127回 PHP勉強会@東京
  2. 2. 岡田 正平(おかだ しょうへい)@okashoi • 株式会社ウィルゲート 2015年新卒入社 • 開発室 ソリューションユニット 所属 • PHP, Laravel, Vue.js 2 自己紹介 Slides:
  3. 3. ① Laravel の paginate は何をやっているのか • タイトル通り ② フレームワークのソースコードの追うときの思考プロセス 3 このスライドでつたえたい
  4. 4. https://laravel.com/docs/5.6/pagination
  5. 5. // Controller にて $users = App¥User::paginate(15); {{-- blade template にて --}} <div class="container"> @foreach ($users as $user) {{ $user->name }} @endforeach </div> {{ $users->links() }}
  6. 6. // Controller にて $users = App¥User::paginate(15); {{-- blade template にて --}} <div class="container"> @foreach ($users as $user) {{ $user->name }} @endforeach </div> {{ $users->links() }}
  7. 7. routing を触らずに ページネーション用の URL (?page=x) が生まれる • 勝手に routing が生成される? だけでいい感じにページネーションのリンクが生まれる 8 なんか気持ち悪い…… $users->links()
  8. 8. ……
  9. 9. // Controller にて $users = App¥User::paginate(15); {{-- blade template にて --}} <div class="container"> @foreach ($users as $user) {{ $user->name }} @endforeach </div> {{ $users->links() }}
  10. 10. // Controller にて $users = App¥User::paginate(15); {{-- blade template にて --}} <div class="container"> @foreach ($users as $user) {{ $user->name }} @endforeach </div> {{ $users->links() }}
  11. 11. • の戻り値 • ページネーションに必要な情報を持っている • もこのクラスに生えている 12 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); ¥Illuminate¥Database¥Query¥Builder::paginate() links()
  12. 12. • の戻り値 • ページネーションに必要な情報を持っている • もこのクラスに生えている 13 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); ¥Illuminate¥Database¥Query¥Builder::paginate() links()
  13. 13. 14 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15);
  14. 14. これらの情報から、ページネーション部分の HTML を生成することはできる(わかる)
  15. 15. 16 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15);
  16. 16. 17 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる)
  17. 17. 18 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる) 引数でもらう (わかる)
  18. 18. 19 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる) 引数でもらう (わかる) 算出できる (わかる)
  19. 19. 20 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる) 引数でもらう (わかる) 算出できる (わかる) ??(わからない)
  20. 20. ここからは勘と執念の戦い (なのでちょっと駆け足)
  21. 21. • の戻り値 • ページネーションに必要な情報を持っている • もこのクラスに生えている 22 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); ¥Illuminate¥Database¥Query¥Builder::paginate() links()
  22. 22. 23 ¥Illuminate¥Database¥Query¥Builder::paginate() public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null) { $page = $page ?: Paginator::resolveCurrentPage($pageName); $total = $this->getCountForPagination($columns); $results = $total ? $this->forPage($page, $perPage)->get($columns) : collect(); return $this->paginator($results, $total, $perPage, $page, [ 'path' => Paginator::resolveCurrentPath(), 'pageName' => $pageName, ]); }
  23. 23. 24 ¥Illuminate¥Database¥Query¥Builder::paginate() public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null) { $page = $page ?: Paginator::resolveCurrentPage($pageName); $total = $this->getCountForPagination($columns); $results = $total ? $this->forPage($page, $perPage)->get($columns) : collect(); return $this->paginator($results, $total, $perPage, $page, [ 'path' => Paginator::resolveCurrentPath(), 'pageName' => $pageName, ]); } currentPage を解決してそう
  24. 24. 25 ¥Illuminate¥Pagination¥AbstractPaginator public static function resolveCurrentPage($pageName = 'page', $default = 1) { if (isset(static::$currentPageResolver)) { return call_user_func(static::$currentPageResolver, $pageName); } return $default; } /** * Set the current page resolver callback. * * @param ¥Closure $resolver * @return void */ public static function currentPageResolver(Closure $resolver) { static::$currentPageResolver = $resolver; }
  25. 25. 26 ¥Illuminate¥Pagination¥AbstractPaginator public static function resolveCurrentPage($pageName = 'page', $default = 1) { if (isset(static::$currentPageResolver)) { return call_user_func(static::$currentPageResolver, $pageName); } return $default; } /** * Set the current page resolver callback. * * @param ¥Closure $resolver * @return void */ public static function currentPageResolver(Closure $resolver) { static::$currentPageResolver = $resolver; } $currentPageResolver を呼び出している $currentPageResolver は……?
  26. 26. 27 ¥Illuminate¥Pagination¥AbstractPaginator public static function resolveCurrentPage($pageName = 'page', $default = 1) { if (isset(static::$currentPageResolver)) { return call_user_func(static::$currentPageResolver, $pageName); } return $default; } /** * Set the current page resolver callback. * * @param ¥Closure $resolver * @return void */ public static function currentPageResolver(Closure $resolver) { static::$currentPageResolver = $resolver; } ここでセットされてる!
  27. 27. 順当にクラス定義をさかのぼって行くと、ここで行き止まる = の呼び出し箇所が見つからない → Laravel には ServiceProvider という仕組みがある • アプリケーションの各所初期処理が行われる場所 28 行き止まり? Paginator::currentPageResolver()
  28. 28. 29 ¥Illuminate¥Pagination¥PaginationServiceProvider public function register() { Paginator::viewFactoryResolver(function () { return $this->app['view']; }); Paginator::currentPathResolver(function () { return $this->app['request']->url(); }); Paginator::currentPageResolver(function ($pageName = 'page') { $page = $this->app['request']->input($pageName); if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return (int) $page; } return 1; }); }
  29. 29. 30 ¥Illuminate¥Pagination¥PaginationServiceProvider public function register() { Paginator::viewFactoryResolver(function () { return $this->app['view']; }); Paginator::currentPathResolver(function () { return $this->app['request']->url(); }); Paginator::currentPageResolver(function ($pageName = 'page') { $page = $this->app['request']->input($pageName); if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return (int) $page; } return 1; }); } リクエストパラメータ ${pageName} を取得
  30. 30. 31 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる) 引数でもらう (わかる) 算出できる (わかる) ??(わからない)
  31. 31. 32 ¥Illuminate¥Pagination¥LengthAwarePaginator // Controller にて $users = App¥User::paginate(15); DBから取得できる (わかる) 引数でもらう (わかる) 算出できる (わかる) わかった!
  32. 32. • Laravel の paginate は一見アクロバティックだが 意外と副作用が無い形になっていた • Laravel のコードを追って行って abstract class や interface で行き止まったら それっぽい ServiceProvider を探すと良い • こういうの調査するのに PhpStorm が便利!(Go To Declaration) • 要 larvae-ide-helper https://github.com/barryvdh/laravel-ide-helper 34 まとめ

×