Laravel6 – ユーザー認証

 

プロジェクト開始時に作成されるファイル

Userモデル

プロジェクトを作成すると、appディレクトリー下にUser.phpが作成される。

Userモデルの内容についてはこちらを参照。

コントローラー

プロジェクトを作成すると、Controllers/Authディレクトリー下に以下の認証用コントローラー群が自動的に作成される。

ルーティング設定

ユーザー認証のためのルーティング

プロジェクト作成後にUserモデルと必要なコントローラーはLaravelにより作成されるが、ルーティングは設定が必要になる。

ルーティングファイルに以下の1行を記載すると、ユーザー認証用の各種ルーティングがまとめて定義される。

ルーティングリスト

関係するルーティングは以下の通りで、ログイン関係とユーザー登録関係が定義されている。

ビューの作成

自動作成の場合

  • php artisan ui vue --authコマンドでユーザー認証に必要なビューを作成
  • 作成されるビューはresources/views/authディレクトリー下に配置

カスタムビュー作成の場合

authディレクトリーの作成

  • resources/viewsディレクトリー下にauthディレクトリーを作成
  • 作成したauthディレクトリー下に必要なビューを定められたファイル名と内容で作成

登録用フォームビューの作成~register.blade.php

authディレクトリー下に、以下フォームを含む内容でregister.blade.phpを作成。ビュー名はLaravelが生成するコントローラーで想定されているされるため、これに合わせる必要がある。

たとえばユーザー登録フォームのビュー名はRegisterControlleruseされているRegistersUsersトレイトshowRegistrationForm()メソッドにおいて、'auth.register'として呼ばれる。

ブラウザーからURL:ドメイン名/registerでアクセスすると以下のようなフォームが表示される。

カスタマイズしていない状態で登録ボタンを押すと、URL:ドメイン名/homeにリダイレクトされる(後述のように、そのままでは404エラーになる)。

ログインフォームビューの作成~login.blade.php

authディレクトリー下に、以下のフォームを含む内容でlogin.blade.phpを作成。

ブラウザーからURL:ドメイン名/loginでアクセスすると以下のようなフォームが表示される。

カスタマイズしていない状態で登録ボタンを押すと、URL:ドメイン名/homeにリダイレクトされる。

コントローラー設定

ログイン後の画面遷移

たとえばユーザーログイン後のトップページを'posts/index'とする。

デフォルト設定~404エラー

カスタマイズしていない状態では以下の様になっているが、このままログインすると404エラーとなる。

  • ルーティングファイル→特に設定なし
  • コントローラー

このときブラウザーのURL表示はhttp://localhost:3000/homeとなっている。

方法1~コントロールファイルを変更

以下の様に$redirectToの定義を編集すると、意図したページに遷移する。

方法2~ルーティングファイルに追加

コントローラーが以下の様に生成直後の状態で・・・

ルーティングファイルに以下の1行を追加する。

なお、上記をルーティングファイルに追加すれば、コントローラーの定義は以下でもよい。

ルート名は使えない

ルート名が設定されていても、以下の表現はエラーになる(Constant expression contains invalid operations)。

ユーザー登録後の画面遷移

RegisterControllerでもデフォルトで以下の様に定義されている。

LoginControllerと同じようにルーティング設定をすることで、ユーザー登録後の遷移先を設定することができる。

ユーザー認証処理の詳細

ユーザー登録処理の内容ログイン処理の内容についてはそれぞれのリンクを参照。

2038年問題対応

Laravelでモデルの雛形を生成する場合、MySQLだと作成日時と更新日時のtimestamp型の2038年問題が内包される。

database/migrationsディレクトリー下のマイグレーションファイル、...create_users_table.phptimestampsdatetime型に修正しておくべき。

ログアウト処理

ログアウト処理についてはこちら

 

PHP – マジックメソッド – __callStatic()

__callStatic()はマジックメソッドの一つで、実行させようとしたクラスのstaticメソッドが存在しない時に呼ばれる。

以下の例では、MyClass__callStatic()メソッドのみが定義されている。__callStatic()の内容は、引数の$method$argsを表示させるようにしている。

このクラスに存在しないstaticメソッドを、引数なし、引数1個、2個で実行した場合の実行結果。引数は配列として$argsにセットされ、引数がない場合は空の配列、引数が1個の場合は要素数1(要素番号0)の配列となる。

インスタンスに対して存在しないstaticメソッドを呼んで__callStatic()が実行される場合、通常のstaticメソッドと違って'->'はエラーになる。

 

PHP – static::とself::

概要

static::self::はクラスのスタティックメソッドを指すときに使えるが、それぞれ以下のような違いがある。

  • staticはそれが実行されるときのクラスを指す
  • selfはそれが定義されたときのクラスを指す

参照:new staticとnew self

準備

以下のようにParentClassとそれを継承したChildClassを準備する。

  • いずれも同じ名前のstaticメソッドstatic_method()を持つ
  • それぞれのクラスはインスタンスメソッドparent_method()child_method()を持つ
    • 何れの内容も同じで、static::static_method()self::static_method()を実行する

クラスからstaticメソッドを呼ぶ

クラスからstaticメソッドを呼ぶと、当然それぞれに対応したstatic_method()が実行される。

親クラスのメソッドから呼ぶ

親クラスのインスタンスを作って、インスタンスメソッドparent_method()内でstaticselfstatic_method()を呼び出す場合。

親クラスのstaticメソッドしか対象がないので、いずれの場合も親クラスのstatic_method()が実行される。

子クラスのメソッドから呼ぶ

子クラスのインスタンスを作って、インスタンスメソッドchild_method()内でstaticselfstatic_method()を呼び出す場合。

staticselfとも子クラスを指し、子クラスのインスタンスメソッドが実行される。

子クラスで親クラスのメソッドから呼ぶ

子クラスのインスタンスを作って、継承元の親クラスのインスタンスメソッドparent_method()からstaticselfstatic_method()を呼び出す場合。

staticは実行時のクラスを指すため、Childクラスのstatic_method()が実行される。

一方selfは定義時のクラスを指すため、parent_method()が定義されたParentClassstatic_method()が実行される。

Laravel – artisan – ルーティングリストの表示

下記コマンドでルーティングのリストが表示される。

php artisan route:list

ただし、各ルーティングで呼ばれるコントローラー・アクションが定義されていなければならない。

実行例。

 

Laravel – REST~リソースベース

概要

RESTはREpresentational State Transferの略。ここでは以下のような感じで理解しておく。

  • リソースを一意に特定できるURL
  • GET、POSTなどのメソッド
  • ステートレスで完結

Laravelでは、リソースベースでルーティングを記述し、リソースコントローラーを生成することで、RESTに沿ったコードが書ける。

以降、Itemというモデルを仮定する。Itemモデルのインスタンスをitem、その集合体をitemsとする。

リソースを一意に特定するURL

サーバーにモデルの集合体が保存されていて、これら全体あるいはその中の特定のデータや、データを表示するビューなどのリソースを指定するURLの書き方。

/items
itemのインスタンスの集合体あるいはこれらを表示するビューなどのリソース。
/items/create
1つのitemを作成するための、入力フォームを含むビューなどのリソース。
/items/{id}
itemsのうちidで特定される1つのitemやこれを表示するビューなどのリソース。POST、PATCH、DELETEといったメソッドによってitemに対する操作を指定する。
/items/{id}/edit
idで特定されるitemの内容を編集するフォームを含むビューなどのリソース。

メソッド

GET
URLで指定したビューのHTMLなどのリソースをサーバーに要求する。
POST
URLで指定したリソースの内容をサーバーに送信する。
PATCH
URLで指定したリソースの内容でサーバー上のリソースの内容を変更するよう要求する。
DELETE
URLで指定したリソースをサーバー上から削除するよう要求する。
PUT
指定した内容でサーバー上のリソースを置き換える。LaravelのRESTfuLの枠組みでは使われない。

RESTfuLなルーティング

メソッドとURLを組み合わせることで、サーバー上のリソースに対する操作が確定する。LaravelでのRESTfuLな操作は以下の7つで、それぞれに応じたコントローラーのアクションにルーティングするのが標準。

リソースベース

リソースベースの考え方

RESTfuLなルーティングと、これに対応したアクションを自動的に生成する方法。

  • リソースルーティングの書き方によって、7つのRESTfuLなルーティングを1行で記述
  • artisanのコントローラー作成コマンドの--resourceオプションで、RESTfuLなルーティングに対応した7つのアクションを持つコントローラーを生成

リソースルーティング・名前付きルート

ルーティング定義に以下の1行を書くだけで、RESTfuLな7つのルーティングを書いたのと同義になる。

routes/web.php

このとき各ルートに名前も付けられる

一覧で確認。

リソースの限定

以下のように書くことで、特定のリソースのみを選んだり除外したりできる。

リソースコントローラー

以下のartisanコマンドで、RESTfuLなルーティングに対応したメソッドを持つコントローラーが生成される。

php artisan make:controller ItemController --resource

生成されるコントローラーのアクションは以下のとおり。

  • index()
  • create()
  • store(Request $request)
  • show($id)
  • edit($id)
  • update(Request $request, $id)
  • destroy($id)

app/Http/Controllers/ItemController

 

Laravel – 名前付きルート

記述方法

ルーティングのname()メソッドで、ルートに名前を付けることができる。

Route::メソッド('URL', 'コントローラー@アクション')->name('ルート名');

この名前を使ってルーティングのURLを得るには、route()ヘルパーを使う。

route('ルート名')

URLにルートパラメーターを含む場合、route()でパラメーターの値を設定して、これを含むURLを得ることができる。

route('ルート名', ['パラメーター' => 値])

ルートパラメーターが複数ある場合は連想配列の要素として列挙。いずれのパラメーターも省略することはできない。

route('ルート名', ['パラメーター1' => 値1, 'パラメーター2' => 値2])

ルーティングで以下のように記述する。

コントローラーのnamed_route()アクションでビューを呼び出す。

ビューの中で、route()ヘルパーによってルーティングのURLを表示させる。

結果は以下のように表示される。

名前付きルート
http://localhost:3000/named_route
http://localhost:3000/named_route/100/set
http://localhost:3000/named_route/first/100/second/200

 

メリット

たとえばアンカータグのURLにルートパラメーターを含む場合、文字列で結合するよりも可読性が高くなる。

URLを変更した場合でも、ルーティングファイルのみ書き換えればよい。

このほか、RESTに基づくリソースルーティングの場合は、自動的に名前が付けられる。

 

MySQL – timestampの2038年問題

timestamp型とdatetime型

MySQLの日付・時刻表現にはtimestamp型とdatetime型がある。

MySQLのtimestamp型は内部表現に整数を用いていて、これが32bit精度の場合には、2038-01-19 03:14:07UTCまでしか表現できない。これは日本のタイムゾーンだと2038-01-19 12:14:07JSTに対応する。

一方datetime型は文字列で日時を表現していて、その範囲は1000年~9999年の間とされている。

timestamp型とdatetime型の有効範囲を比較すると以下のとおり。

timestamp datetime
UTC内部表現で保持 タイムゾーン文字列で保持
1970-01-01 00:00:01UTC~2038-01-19 03:14:07UTC 1000-01-01 00:00:00~9999-12-31 23:59:59

timestamp型の2038年問題

確認環境

timestamp型で上限値より大きな値を登録しようとすると、エラーで登録ができない。このことを確認してみる。環境は以下のとおり。

  • Windows(64bit版)上のVagrant+VirtualBOXで構築したCentOS7
  • MySQLのバージョンは8.0.26

確認用のテーブルは以下のとおり。

上限を超える値の登録は不可

まず上限値一杯の日時を登録してみる。UTCに対して日本のタイムゾーンで表現した2038年1月19日12時14分07秒を登録すると、問題なく登録される。

次に上限値に1秒を加えた日時を登録しようとすると、"Incorrect datetime value"となって登録できない。

timestamp型に対する加算の場合は9999年まで

次に登録された上限一杯の値に対してインターバルを加えていき、問題が生じるまでその境界を探していった。

その結果、9999年12月31日23時59分59秒999999…までは登録可能だが、10000年1月1日0時0分0秒に達することはできず、その場合にはNULLとなることがわかった。

32bitの上限を超えた後も値は保持されているが、その次の上限はdatetime型の上限と一致している。以下の何れかと推測される。

  • 内部的には64bit表現だが、入力時には32bitの上限で、演算時にはdatetime型の上限で抑えている
  • 内部的に32bit表現だが、その上限を超えたときにはdatetime型に内部的に切り替えている

まとめ

  • MySQLのtimestamp型は、2038年の上限値を超える値を登録できない
  • timestamp型に対する加算を行った場合、datetime型と同じ上限まで値を保持できる
    • 加算後にdatetime型の上限を超えた場合の値はNULLになる

64bit整数の場合はtimestampも西暦3000億年弱まで扱い可能だが、一部が64bitシステムでも、データベース、言語、フレームワークの全てが対応していないとまずそうなので、日付時刻を扱う際はdatetime型としておくのが安全なようだ。

 

Tips – カラムの幅を一杯に広げる

概要

外枠の中に複数カラムを起き、1つのカラムを最大幅まで伸長するためのCSS。

外枠

外枠を表示領域の80%の幅とし、センタリング。

  • 幅を表示領域の80%に:width: 80%;
  • 左右方向のセンタリング:margin: 0 auto;
 width: 80%; margin: 0 auto;

外枠の中に複数カラム

親要素にdisplay: flex;を指定し、伸長する子要素のみflex: 1;を指定。

2カラム~右拡張

  • 外枠(親要素):display: flex;
  • 左側カラム:幅指定なし→内容に合わせる
  • 右側カラム:flex: 1;→残り幅一杯
margin: 10px;
margin: 10px;flex: 1;
いづれの御時にか、女御、更衣あまた候ひ給ひける中に、いとやむごとなき際にはあらぬが、すぐれて 時めき給ふありけり。

3カラム~中央拡張

  • 外枠(親要素):display: flex;
  • 左側カラム:width: 10%;
  • 中央カラム:flex: 1;→残り幅一杯
  • 右側カラム:width: 10%;
width 10%;
margin: 10px;flex: 1;
いづれの御時にか、女御、更衣あまた候ひ給ひける中に、いとやむごとなき際にはあらぬが、すぐれて 時めき給ふありけり。
width 10%;

 

Laravel – データモデル操作

概要

Laravelにおけるデータモデルの操作をまとめる。

例としてStickyNoteというアプリケーションを仮定する。StickyNoteは付箋紙のようなアプリケーションで、タイトルとメモ本文を扱う。このアプリケーションを通して、フォーム入力、データの読み込み、書き込み、更新、削除の流れをそれぞれ整理。

データ構造

マイグレーションファイル

マイグレーションファイルにより、StickyNotesの個々の付箋メモのデータ構造を定義。タイトル(title)とメモ本文(body)を定義している。

データベース

マイグレーションの結果得られるテーブルの構造。

データの読み込み

ルーティング

ドメイン名/sticky_noteのURL指定に対してコントローラーStickyNoteControllerindexアクションを呼び出す。

routes/web.php

コントローラーでの処理~all()による全データ取得

全データ取得はコントローラーのスタティックメソッドall()を使い、結果はレコード単位の配列で得られる。

ここでは、

  • indexアクションで$sticky_notesに表示対象の全データを取得
  • 取得した配列をビューに渡して遷移
  • モデルを記述するときはフルパスで書くか冒頭でuse宣言

app/Https/Controllers/StickyNoteController.php

\App\StickyNoteのようにモデルをフルパスで指定する必要がある。あるいは冒頭でuse \App\StickyNoteとしてもよい。

ビューによる表示

ビューで受け取った配列をループで表示させている。データベースのカラムに対応した属性で、各カラムのデータが得られる。

resources/views/sticky_note/index.blade.html

データの書き込み

フォームでの入力と送信

書き込むデータをフォーム入力し、POTSTで送信する。

resources/views/create.blade.php

ルーティング~POSTに対する書き込み処理

POSTのルーティング先をコントローラーのstore()アクションとしている。

routes/web.php

コントローラーでの処理~create()によるデータ書き込み

ルーティング先のアクションではフォームリクエストを引数にとり、マスアサインメントと登録処理を1行で記述している。

また、アクションで引用するモデルとフォームリクエストのクラスをuseでインポートしている。

ここでは、

  • コントローラーの冒頭で、モデルStickyNoteとフォームリクエストStickyNoteRequestuseでインポート
  • フォームリクエストStickyNoteRequestを引数にとっている
  • リクエストのonly()メソッドでマスアサイン
  • アサイン結果をモデルStickyNotecreate()スタティックメソッドの引数に与え、データを登録
  • トップページにリダイレクト

app/Https/Controllers/StickyNoteController.php

モデルでの$fillable定義

コントローラーでのマスアサインメントを有効にするため、データモデルで$fillableプロパティーを定義。

ここでは、StickyNoteモデルクラスで$fillablepublic宣言して、読み込むプロパティーをtitlebodyの2つに限定している。

app/StickyNote.php

データの更新

ルーティング

  • 更新対象を指定するデータのidがルートパラメーターで指定されてGETされることを想定
    • URLにルートパラメーター{id}を含めている
    • GETメソッドでトップページなど他のページを区別するために、URLに/editを付けている
  • GETを受け取ったらeditアクションにルーティング
  • たとえばアンカータグでここに飛ばしたい場合は、url()ヘルパーを使って以下のように指定する
    • <a href="{{ url('/diaries') . '/' . $diary->id . '/edit' }}">

routes/web.php

コントローラーでの処理~find()による更新対象の取得

  • ルーティングで設定されたルートパラメーターは、アクションの引数で参照する
  • モデルのfind()スタティックメソッドで、idに対応するモデルのインスタンスを取得
  • 取得したモデルインスタンスを、更新用のフォームを持つビューに渡す
    • 遷移先のビューでidに対応するデータを初期表示するため

app/Https/Controllers/StickyNoteController.php

フォームでの入力と送信

  • 更新用のフォームを表示する
  • フォームの各要素に、受け取ったデータのタイトルと本文を初期表示する
  • 更新結果はPATCHメソッドで送信
    • HTTPにはGETとPOSTしかないので、formタグではPOSTを指定し、@method()ディレクティブでPATCHを指定している
    • 送信先のactionで、idをルートパラメーターとして付加している

resources/views/edit.blade.php

ルーティング

PATCHメソッドによるルートパラメーターidを含むURLへの送信を、update()アクションにルーティング。

routes/web.php

コントローラーでの処理~update()による更新処理

データの書き込みと同じような手順。

  • ルートパラメーターを引数で受け取り
    • 引数の順番は、フォームリクエスト、ルートパラメーターの順番
  • find()の引数にidを与えてデータを取得
  • マスアサインメントでデータを更新
  • トップページへリダイレクト

app/Https/Controllers/StickyNoteController.php

データの削除

ビュー~削除対象指定

  • 全データを表示する際に、各データをフォームで表示し、データごとに削除ボタンを置く
  • 削除ボタンが押されると、DELETEメソッドでidをルートパラメーターに含むURLを呼び出す
  • HTTPにはGETとPOSTしかないため、@methodディレクティブでDELETEを指定

routes/web.php

ルーティング

DELETEメソッドで所定のURLが要求された場合に、destroy()アクションにルーティング。

routes/web.php

コントローラーでの処理~delete()による削除

  • ルートパラメーターで受け取ったidでデータを取り出し
  • 得られたデータをdelete()で削除
  • トップページにリダイレクト

app/Https/Controllers/StickyNoteController.php

 

Laravel – 外部スタイルファイル

ビューに対するスタイルファイルを外部に置く場合、ビューHTMLのヘッダー部分にlinkタグを記述する。

<link rel="stylesheet" href="スタイルファイルのURL">

アプリケーションのpublicディレクトリー下に置く場合は、asset()ヘルパーを使うことができる。

例えばスタイルファイルをpublic/css/style.cssとして準備する場合は以下のようなタグになる。

httpsによるセキュアーな通信環境の場合はsecure_asset()を使う。