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に表示対象の全データを取得
  • 取得した配列をビューに渡して遷移

app/Https/Controllers/StickyNoteController.php

ビューによる表示

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

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()を使う。

 

Laravel – エラーメッセージの日本語化

日本語化ファイルのダウンロードとインストール

ユーザー認証やバリデーションなどに関するエラーメッセージの日本語化はロケールファイルを編集する方法で可能だが、言語ファイルをダウンロード・インストールする方法がReadDoubleで紹介されている。

以下、プロジェクトのルートディレクトリーで操作。

インストールファイルのダウンロード

インストールファイルの実行

インストールファイルの削除

これによりresources/lang/jaディレクトリーにauth.phppasswords.phppagination.phpvalidation.phpの4つのファイルが生成される。

これでメッセージは日本語化されるが、まだ属性名は以下のようにソースコードで記述したまま。

属性名の日本語化

バリデーションエラーの属性名を日本語化するには、resources/lang/ja/validation.phpを編集する。

たとえばnamehandlenameemailを日本語化するには以下のように編集。

これにより、メッセージ表示は以下のように日本語化される。

 

Laravel – フォームリクエスト

概要

  • FormRequestクラスは、ユーザー権限の認証と入力のバリデーションを専用に扱う
  • 実装は、artisanでFormRequestクラスを継承したクラスを生成し、認証やバリデーションの内容を記述
  • コントローラーのインジェクションをRequestからFormRequestの継承クラスに変更することで、チェック機能をコントローラーから分離することができる

準備

バリデーションで使ったアプリケーションを利用する。

ビュー

コントローラーのアクション

フォームリクエストクラスの生成

以下のコマンドでFormRequestを継承したクラスを作成する。

php artisan make:request リクエストクラス名

リクエストクラスは、app/Http/Requestsディレクトリー下に作成される。

リクエストクラスの内容。ユーザー認証のためのauthorize()メソッドと、バリデーションのためのrules()メソッドが定義されている。

アクションの引数の変更

  • コントローラーの先頭でリクエストクラスをuseでインポート
  • コントローラーのアクションのメソッドインジェクションをRequestからリクエストクラスに変更
  • validate()メソッドはアクションには書かない

今回の例の場合。

ユーザー認証の記述

準備中。認証された場合はauthorize()メソッドの戻り値がtrue

バリデーションルールの記述

rules()メソッドの戻り値の配列に、バリデーションルールを書く。

準備したコントローラーのバリデーション部分をリクエストクラスに記述。

エラーメッセージのカスタマイズ

フォームリクエストのmessages()メソッドをオーバーライドすることで、メッセ維持のカスタマイズができる。

参考:Laravel 6.x バリデーション~エラーメッセージのカスタマイズ

Laravel – マスアサインメント

概要

  • マスアサインメントによって、フォームからPOSTされたパラメーターを一括してモデルの属性にセットして、データベースに書き込める
  • ただしセットできる属性をモデルの$fillable配列に限定列挙する必要がある
  • モデルのインスタンス生成時に、セットできるパラメーターをonlyメソッドで限定列挙できる
  • $fillableのほかに$guarded配列も指定できて、こちらは指定した属性をモデルのセットの際に排除する

準備

以下のようなフォームがあって、namecommentをPOSTする。

POSTはコントローラーのstore()メソッドにルーティングされる。

ルーティング先のコントローラーでは、モデルのインスタンスを生成し、その属性にPOSTされたパラメーターをセットしてデータベースに書き込む。

マスアサインメント

Requestのall()メソッド

Request引数のall()メソッドで得られる内容を確認。

CSRF対策のトークンを含んだパラメーターの配列が得られる。

$request->all()の結果を、モデルインスタンスのfill()メソッドによって属性にセット。dd()で内容を確認してみる。

エラー。

マスアサインメントのためにはfillable属性に加えなければならないと言われる。

モデルの$fillableプロパティー

モデルの定義で、$fillable配列に取得したいプロパティーを列挙する。

先ほどのコードの実行結果。エラーがなくなる。dd()でプロパティーがセットされたインスタンスの内容を確認。

  • fillableプロパティーに2つのパラメーター名がセットされている
  • attributesに属性とその内容が配列としてセットされている
  • guardedは指定しておらず、サイズ1、内容'*'の配列となっている

以上を踏まえて、fillableがセットされたモデルのマスアサインメントとデータベース登録を以下に例示。

マスアサインメントの記述方法

マスアサインメントの書き方には複数あって、同じ結果が得られる。create()スタティックメソッドは、インスタンスの生成とデータベースの書き込みを一つのメソッドで行う。

Requestのonly()メソッドによる限定

意図しないパラメーターの追加を避けるため、all()メソッドではなくonly()メソッドでパラメーターを限定列挙できる。