20200118のlaravelに関する記事は3件です。

Laravel6でページネーションのカスタマイズ

概要

Laravelのページネーションに触れてみたので備忘録です。
基本的に公式ページに全て書いてある。
Database: Pagination - Laravel - The PHP Framework For Web Artisans

環境

PHP: 7.2.22
Laravel: 6.11.0

事前準備

テスト用の各種設定は以下の通りです。ユーザやルーティングを適当に設定しています。

UsersController.php
class UsersController extends Controller
{
    public function index() {
        $users = DB::table('users')->paginate(10);
        return view('user.index', compact('users'));
    }
}
index.blade.php
<html>
  <head>
      <title>Paging Sample</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
  </head>
  <body>
    <div class="container">
      <h1>Paging Sample</h1>
      <ul class="list-group">
        @foreach ($users as $user)
          <li class="list-group-item">
            {{ $user->name }}
          </li>
        @endforeach
      </ul>
      {{ $users->links() }}
    </div>
  </body>
</html>

スクリーンショット 2020-01-18 21.21.59.png

ページネーション設定

以下コマンドを実行するとresources/views/vendor以下にpaginationのビューファイルが作成されます。現在のバージョンではデフォルトはこの中にあるbootstrap-4.blade.phpが使用されているようです。

$ php artisan vendor:publish --tag=laravel-pagination
Copied Directory [/vendor/laravel/framework/src/Illuminate/Pagination/resources/views] To [/resources/views/vendor/pagination]
Publishing complete.
Publishing complete.
$ ls resources/views/vendor/pagination/
bootstrap-4.blade.php
default.blade.php
semantic-ui.blade.php
simple-bootstrap-4.blade.php
simple-default.blade.php

以下のようにしても同じです。

$ php artisan vendor:publish

 Which provider or tag's files would you like to publish?:
  [0 ] Publish files from all providers and tags listed below
  [1 ] Provider: Facade\Ignition\IgnitionServiceProvider
  [2 ] Provider: Fideloper\Proxy\TrustedProxyServiceProvider
  [3 ] Provider: Illuminate\Foundation\Providers\FoundationServiceProvider
  [4 ] Provider: Illuminate\Mail\MailServiceProvider
  [5 ] Provider: Illuminate\Notifications\NotificationServiceProvider
  [6 ] Provider: Illuminate\Pagination\PaginationServiceProvider
  [7 ] Provider: Laravel\Tinker\TinkerServiceProvider
  [8 ] Tag: flare-config
  [9 ] Tag: ignition-config
  [10] Tag: laravel-errors
  [11] Tag: laravel-mail
  [12] Tag: laravel-notifications
  [13] Tag: laravel-pagination
 > 13

Copied Directory [/vendor/laravel/framework/src/Illuminate/Pagination/resources/views] To [/resources/views/vendor/pagination]
Publishing complete.
Publishing complete.

bladeテンプレート内で呼び出しているlinks()でページネーションにのテンプレートを指定できます。

index.blade.php
{{ $users->links('vendor.pagination.default') }}

スクリーンショット 2020-01-18 22.12.46.png

index.blade.php
{{ $users->links('vendor.pagination.semantic-ui') }}

スクリーンショット 2020-01-18 22.12.58.png

前か次かだけのシンプルなページングを指定することも可能です。

index.blade.php
{{ $users->links('vendor.pagination.simple-default') }}

スクリーンショット 2020-01-18 23.43.27.png

デフォルトでbootstrap-4.blade.phpが呼び出されている訳なので、これを編集することでページネーションの表示をカスタマイズすることもできます。

また、自身で作成したテンプレートも使用できますので、今回はbootstrap-4をちょっとだけ修正してsample-paginationというテンプレートを作成し使用してみる。

index.blade.php
{{ $users->links('vendor.pagination.sample-pagination') }}
sample-pagination.php
@if ($paginator->hasPages())
    <nav>
        <ul class="pagination justify-content-center pagination-lg">
            {{-- Previous Page Link --}}@cannot('update', Model::class)

            @endcannot
            @if ($paginator->onFirstPage())
                <li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
                    <span class="page-link" aria-hidden="true">&laquo;</span>
                </li>
            @else
                <li class="page-item">
                    <a class="page-link text-success" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&laquo;</a>
                </li>
            @endif

            {{-- Pagination Elements --}}
            @foreach ($elements as $element)
                {{-- "Three Dots" Separator --}}
                @if (is_string($element))
                    <li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
                @endif

                {{-- Array Of Links --}}
                @if (is_array($element))
                    @foreach ($element as $page => $url)
                        @if ($page == $paginator->currentPage())
                            <li class="page-item active" aria-current="page"><span class="page-link bg-success border-success">{{ $page }}</span></li>
                        @else
                            <li class="page-item"><a class="page-link text-success" href="{{ $url }}">{{ $page }}</a></li>
                        @endif
                    @endforeach
                @endif
            @endforeach

            {{-- Next Page Link --}}
            @if ($paginator->hasMorePages())
                <li class="page-item">
                    <a class="page-link text-success" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">&raquo;</a>
                </li>
            @else
                <li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
                    <span class="page-link" aria-hidden="true">&raquo;</span>
                </li>
            @endif
        </ul>
    </nav>
@endif

スクリーンショット 2020-01-18 23.05.00.png

まとめ

Laravelのページネーションは意外と簡単に実装できる。
スタイルの変更も分かりやすく、自由に修正できる。

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのEloquent ORMでどっちがhasでどっちがbelongsなのか?

はじめに

LaravelのEloquent ORMでは、Modelに対してhasOne、hasMany、belongsTo、belongsToManyを定義することにより、Model間の関連を表すことが出来ます。
主従(親子)関係より、主(親)となる側にhasOne、hasManyを定義し、従(子)となる側にbelongsToを定義すれば良いのですが、初心者の方には主従関係の見極めが難しいので、どっちにhasを定義し、どっちにbelongsToを定義するのかの決め方をまとめてみました。

どっちがhasかbelongかの決め方

  • 1対1の場合
    • テーブル構造において、相手のidのカラムを持っているModelにbelongsToを定義し、他方のModelにhasOneを定義する。
  • 1対多の場合
    • テーブル構造において、相手のidのカラムを持っているModelにbelongsToを定義し、他方のModelにhasManyを定義する。
  • 多対多の場合
    • 両方のModelにbelongsToManyを定義する。

Laravelの関連の定義

LaravelのModelでの関連の定義については、以下をご参照ください。

https://readouble.com/laravel/5.8/ja/eloquent-relationships.html#one-to-one-polymorphic-relations

1対多の場合

こんな関連

1.png

Modelの定義

productsテーブルにmaker_idが存在するので、ProductモデルにbelongsToを定義する。

class Product extends Model
{
    public function maker()
    {
        return $this->belongsTo('App\Maker');
    }
}

他方のMakerモデルにhasManyを定義する。

class Maker extends Model
{
    public function products()
    {
        return $this->hasMany('App\Product');
    }
}

多対多の場合

こんな関連

2.png

Modelの定義

両方のModelにbelongsToManyを定義する。

class Product extends Model
{
    public function categories()
    {
        return $this->belongsToMany('App\Category');
    }
}
class Category extends Model
{
    public function products()
    {
        return $this->belongsToMany('App\Product');

}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】『Database does not exist.』エラーの原因と対処法

スクリーンショット 2020-01-18 10.30.34.png

PHPフレームワークLaravel入門を学習中にデータベースにアクセスできない問題が発生しました。

エラーメッセージは『Database does not exist.(SQL:PRAGMA foreign_keys = ON;)』との表示。

.envファイルのDB_DATABASE=database.sqliteをコメントアウトすることで解決したのですが、この記事では詳しい原因と対処法をお伝えします。

エラーメッセージの意味

『Database does not exist.(SQL:PRAGMA foreign_keys = ON;)』

こちらのメッセージ、意味は「データベースが存在しません。」です。
本では誤植があったようで、実際にはデータベースファイル(database.sqlite)のパスを指定する必要がありましたが、DB_DATABASE=database.sqLiteと入力していたため、「データベースが存在しない」というエラーが発生したようです。

さらなる問題と対処法

誤植に気付き、ファイルパスを絶対パスで記入したところ、The environment file is invalid!のエラー。
詳細はFailed to parse dotenv file due to unexpected whitespace.(予期しない空白のため、dotenvファイルの解析に失敗しました。)とのこと。
おそらくファイルパスに日本語が含まれていたため、うまくいかなかったのでしょう。

相対パスでの表記もうまくいかず、対処法を探したところ.envファイルのDB_DATABASE=〜をコメントアウトすることでうまくいくとのこと。

.envビフォー
# 前略
DB_DATABASE=# 後略
.envアフター
# 前略
# DB_DATABASE=〜
# 後略

【参考】laravelにてdatabase.sqliteが存在しない(does not exist)と表示される|teratail

実際に書き換えて、サーバーを立ち上げ直したところ、きちんと動作しました。

対処法解説

では、なぜDB_DATABASE=〜をコメントアウトすることで、きちんと動作するようになったのでしょうか?

Laravelではデータベースを指定する際にconfig/database.phpから設定を読み込みます。
このdatabase.phpではデータベースの指定に以下のようなコードが書かれています。

database.php
# 前略
'database' => env('DB_DATABASE', database_path('database.sqlite')),
# 後略

まず、env()から見ていきましょう。

グローバルヘルパー関数 env()

ここで使われているenv()はLaravelに用意されているグローバルヘルパー関数の1つで、環境変数の値を取得します。
取得できない場合はデフォルト値を返します。

$env = env('APP_ENV');

// APP_ENVがセットされていない場合、第二引数がデフォルト値('production')として返る
$env = env('APP_ENV', 'production');

database.phpに書かれているenv('DB_DATABASE', database_path('database.sqlite'))は、「環境変数DB_DATABASEに保存されている値を取得する!なければdatabase_path('database.sqlite')の値を使う!」ということだったんですね。

【参考】ヘルパ 5.5 Laravel( env() )

では、DB_DATABASEをコメントアウトすることで、取得するようになる第二引数database_path('database.sqlite')はどういう関数なのでしょうか?

グローバルヘルパー関数 database_path()

database_path()もグローバルヘルパー関数のひとつです。
database/ディレクトリの完全パスを返します。
database/ディレクトリ内の指定ファイルへの完全パスを生成することもできます。

$path = database_path();

// databaseディレクトリ内のfactories/UserFactory.phpへの完全パスを生成
$path = database_path('factories/UserFactory.php');

env('DB_DATABASE', database_path('database.sqlite'))で使われていたdatabase_path('database.sqlite')は「database/ディレクトリのdatabase.sqliteの完全パスを取得する!」ということだったんですね。

【参考】ヘルパ 5.5 Laravel( database_path() )

2つの関数をまとめると

それぞれの関数でやっていることがわかったので、database.phpに書かれている

database.php
# 前略
'database' => env('DB_DATABASE', database_path('database.sqlite')),
# 後略

が何をしているかをまとめると、

環境変数DB_DATABASEに保存されている値を取得する!なければdatabase/ディレクトリのdatabase.sqliteの完全パスを取得する!

ということになります。

対処法として行った「.envファイルのDB_DATABASE=database.sqliteをコメントアウトする」というのは、database_path()で取得した完全パスをデータベースとして指定するようにするということだったんですね!

その他の対処法

そうなると、「.envファイルのDB_DATABASE=database.sqliteをコメントアウトする」以外にも対処法が見えてきますね!

.envファイルのDB_DATABASE=database.sqliteをコメントアウトせずに

database.php
# 前略
'database' => env('DB_DATABASE', database_path('database.sqlite')),
# 後略

database.php
# 前略
'database' => database_path('database.sqlite'),
# 後略

のように変更して、環境変数の読み込みをなくして直接database.sqliteの完全パスを指定してもきちんと動作するようになりました。

まとめ

Laravelで『Database does not exist.』のエラーが出た際は、データベースのパスの指定が間違っている可能性があるので、

  • .envファイルのDB_DATABASE=database.sqliteをコメントアウト
.env
# 前略
# DB_DATABASE=〜
# 後略

もしくは

  • 環境変数の読み込みをなくして直接database.sqliteの完全パスを指定
database.php
# 前略
'database' => database_path('database.sqlite'),
# 後略

を試してみましょう!

参考まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む