Laravel – モデル作成とマイグレーション

データベース設定の確認

アプリケーションディレクトリー直下の.envファイルでデータベースの設定を確認する。DB_DATABASEに設定されたデータベースが作成済みでRDBMSでアクセス可能であることを確認する。

モデルの作成

モデル作成コマンド

モデルの作成には、artisanmake:modelコマンドを使う。同時にデータベースを定義するマイグレーションファイルも作成するときは--migrationオプションを付ける。

php artisan make:model モデル名 --migration

モデル名の考え方は以下の通り。

  • データベースのレコードに保存する対象として、単数の名詞とする
  • モデル名はアッパー・キャメルケース(パスカルケース)で定義する

以下はTestModelというモデルを作成したケース。

モデルの作成に成功したことと、2021_08_23…という名前でマイグレーションファイルが作成されたことが表示されている。

マイグレーションファイルの作成は--migrationオプションを付けたためで、これをつけないとモデルのだけが作成される。

モデルクラスファイル

モデルのクラスファイルはappディレクトリー下に作成される。コマンドで指定したモデル名に拡張子.phpが付けられている。

以下がモデルクラスファイルの内容。

  • 名前空間をAppとしている
  • 名前空間Illuminate\Database\EloquentModelファイルを利用する
  • ModelファイルはModelクラスのファイル
  • 今回作成したTestModelクラスはModelクラスを継承している
  • モデル作成で定義した名前がそのままクラス名になっている
  • 作成したクラスの中身は空

オプション指定

モデル作成コマンドのオプションは-h/--helpオプションで指定できる。

主なオプションは以下の通り。

-c/--controller
コントローラーを作成する。
-m/--migration
マイグレーションファイルを作成する。
-r/--resource
リソースベースのアクションが含まれたコントローラーが生成される。ルーティングでのonlyexceptと関係なく、すべてのアクションが含まれる。
-s/--seed
シーダーファイルを作成する。

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

ファイル名

モデルクラスファイルと同時に作成されたマイグレーションファイルは、database/migrations下にある。新たに作成された2021_08_23...のマイグレーションファイルが確認できる。

マイグレーションファイルのファイル名構成は以下の通り。

日付_時刻_create_テーブル名_table.php

テーブル名はモデル名をスネークケースに変更し、複数形となっている。

マイグレーションファイルの内容は以下の通り。

up()メソッド

up()メソッドではテーブルを作成するときのカラム定義が記述されている。

  • bigincrements()bigint unsigned型、AUTO_INCREMENT設定のカラムidを定義
  • timestamps()timestamp型でcreated_atupdated_atの2つのカラムを定義

bigIncrements()によるIDを外部キーで参照する場合

bigIncrements()で生成されるカラムはbigint(unsigned)となる。他テーブルでこれを外部キーとして参照する場合のカラム型はbigint unsignedとする。マイグレーションファイルで指定する場合はunsignedBigInteger()とする。

down()メソッド

down()メソッドは、test_modelsテーブルが存在する場合にこれを削除(DROP)。

マイグレーション実行

マイグレーションコマンドについてはこちらを参照。

状態確認

マイグレーション実行前のテーブルの状態。準備の時に作成したままになっている。

マイグレーションの実行状態は以下のコマンドで確認。

php artisan migrate:status

今回作成されたマイグレーションファイルだけが未実行となっている。

マイグレート

以下のコマンドで、マイグレーションファイルの設定に従ってテーブルを作成する。

php artisan migrate

マイグレーションの結果、データベースにテーブルが作成される。テーブル名はマイグレーションファイル名にもあるとおりで、以下のように付けられる。

  • モデル名のキャメルケースをスネークケースに変更
  • 複数形にする

データベースでテーブルが作成されていることを確認。

テーブル構造を確認。timestamp()によって、created_atupdated_atの2つのカラムが作られている。

ただしtimestamp型は2038年問題が発生するため、datetime型に変更する方がよい。

ロールバック

ロールバックにより、作成したテーブルを削除してマイグレーション前の状態に戻すことができる。

php artisan migrate:rollback

ロールバックを実行することで、ここまでで作成したtest_modelsテーブルが削除される。

ただしマイグレーションファイルは残っていて、編集などを行った後に再度マイグレーションできる。

マイグレーションファイルの実行状態も未実行に戻る。

マイグレーションファイルの編集とマイグレーション

test_modelsテーブルのカラムを以下のように編集する。

  • idカラムはそのまま
  • VARCHAR(20)product_nameカラムを追加
  • int型でamountカラムを追加
  • created_atupdated_attimestamp型からdatetime型に変更

参考:利用可能なカラムタイプ

マイグレーション前のファイルを以下のように編集してマイグレート。

以下の構造のテーブルが作成される。

モデルの削除

モデルとテーブルを削除する手順は以下の通り。

  1. 該当テーブルのマイグレーションの前までロールバック
  2. マイグレーションファイル削除
  3. モデルファイル削除

timestampの2038年問題について

モデルとマイグレーションファイルを生成させると、自動的にtimestampsが設定されるが、MySQLとの組み合わせの場合に2038年問題が発生する。datetime型で書きなおすなどの対応をしておくべき。

モデル名とテーブル名について

モデルとテーブルを生成するときの命名の流れは以下のようになっている。

  • モデル名は単数形でアッパーキャメルケース
  • モデルクラスファイル名はモデル名に.phpの拡張子
  • モデルクラス名はモデル名と同じ
  • テーブル名はキャメルケースからスネークケースに変換され、複数形になる

複数形への変更はLaravelがエレガントにやってくれて、不規則な複数形にも対応している。ただし不可算名詞にはそのままsが付くようだ。

  • TestModeltest_models
  • TestDiary→test_diaries
  • CompanyPersonCompanyPeople
  • WildGoosWildGees
  • StockWaterStockWaters

なお、敢えてモデル名をスネークケースにすると、クラスファイル名、モデルクラス名もキャメルケースのままで、テーブル名は複数形になる。

  • test_modeltest_models

 

Laravel – 現在のURLの取得

url()ヘルパー

ヘルパーLaravel - url()を参照。現在のドメインやURLに関する情報が得られる。

Route::current()

以下のメソッドでドメイン名を除いた現在のフルパスが得られる。

これとurl()ヘルパーを組み合わせて、以下でもカレントパスを得られる。

 

PHP – 名前空間~namespace

概要

PHPの名前空間(namespace)は、Javaのパッケージのようにコード群をカプセル化し、異なる名前空間の間での名前の衝突を避ける役割を持つ。

namespace 名前空間名;

  • namespaceが記述された以降のコードはその名前空間に属する
  • 同じ名前空間内では、クラス名などはそのまま記述
  • 異なる名前空間のクラスなどを用いるときは、絶対パスを使うかuse文で名前空間を指定する(このuse文はトレイトuseとは異なる)

クラスの例

以下の例は、異なる名前空間のクラスを利用する場合。

  • 名前空間Bでクラス名MyClassだけを記述すると、名前空間BMyClassが使われる
  • 絶対パス\A\MyClassと記述すると、名前空間AMyClassが使われる
  • use \A\MyClassを記述すると、MyClassと記述したときに名前空間AMyClassが使われる(as ...でエイリアスも指定できる)

関数の場合

以下の例は、異なる名前空間の関数を利用する場合。

  • 名前空間Bで関数名funcだけを記述すると、名前空間Bfuncが使われる
  • 絶対パス\A\funcと記述すると、名前空間Afuncが使われる
  • use function \A\funcを記述すると、funcと記述したときに名前空間Afuncが使われる(as ...でエイリアスも指定できる)

定数の場合

以下の例は、異なる名前空間の定数を利用する場合。

  • 名前空間Bで関数名CONSTANTだけを記述すると、名前空間BCONSTANTが使われる
  • 絶対パス\A\CONSTANTと記述すると、名前空間ACONSTANTが使われる
  • use const \A\CONSTANTを記述すると、CONSTANTと記述したときに名前空間ACONSTANTが使われる(as ...でエイリアスも指定できる)

変数は名前空間によらずグローバル

変数は、それが定義された名前空間によらずグローバルスコープになる。

外部ファイルに分ける場合

外部ファイルで名前空間を設定している場合、インクルードしたコードで明示的に名前空間を指定する。

以下の例では2つのPHPファイルを準備し、それぞれの名前空間を設定している。

namespace_a.php

namespace_b.php

そして別のファイルでこれらのファイルを読み込んで実行する。

インクルード後に名前空間なしでクラス名のみ指定すると、クラスが見つからずエラーになる。

絶対パスかuseを使えば名前空間に応じたクラスを指定できる。

名前空間の部分指定

名前空間をパスで設定した場合、useでその一部を指定することができる。

以下の例では、

  • 名前空間japan\aichiの中でMyClassAMyClassBを定義
  • 名前空間japan\osakaの中でMyClassを定義
  • use japan\aichiとすることで、japan\aichiaichiとして使えるようになる
    • japan\aichi\MyClassAaichi\MyClassA
    • japan\aichi\MyClassBaichi\MyClassB
  • use japan\osakaとすることで、japan\osakaosakaとして使えるようになる
    • japan\osaka\MyClassosaka\MyClass

 

PHP – クラス定数

クラスの中で、constキーワードで定数を定義できる。

  • クラス内でアクセスする場合はself::でアクセス
  • クラス外でアクセスする場合はクラス名::でアクセス(変数::でも可能)

self::クラス名::などをつけないと、警告が出たうえで定数名自体が文字列として解釈される。

定数なので値を再代入することはできない。

const定義の右辺に定義済みの定数、算術式を使うことができる。しかし関数は使用できない。

定数定義の際にアクセス修飾子を使えて、アクセス制限ができる。省略した場合はpublic

 

PHP – gettype()/is_* – 型の確認

概要

値が特定のプリミティブ型かどうか、何かのクラスのインスタンスかどうかを確認する方法。

  • gettype()は型の名前の文字列を返す
  • is_*はそれぞれの型に合致しているかどうかをチェック

クラス関係の確認についてはこちらを参照、

gettype()

gettype()は、引数で与えられた値の型を文字列として返す。以下の例のほか、resourceunknown typeが準備されている。

is_*() – 型の確認

is_*(値)で値の型が合致しているかをチェック。

  • is_bool
  • is_numeric, is_finite, is_infinite, is_nan
  • is_int/is_integer, is_long, is_float/is_double/is_real
  • is_string
  • is_array
  • is_object, is_subclass_of

 

PHP – クラスの確認

概要

  • instanceof:値のクラスをチェック
  • is_a():値のクラスをチェック
  • is_subclass_of():値の親クラスをチェック
  • class_uses():トレイトを使っているかどうかチェック

instanceof/is_a()

instanceof

インスタンス instanceof クラス等名

instanceofは、第1オペランドが第2オペランドのクラスに合致しているかどうかをチェックする演算子。第2オペランドにはクラス名などをそのまま記述する。is_a()関数と同じ機能。

以下の例の様に、instanceofは、

  • そのインスタンスのクラス、継承した親クラス、インプリメントしたインターフェイスに対して真となる
  • トレイトについてはuseしていても真とはならない

is_a()

is_a(インスタンス, 'クラス等名');

is_a()は第1引数のインスタンスが第2引数のクラスに合致しているかどうかチェックする。第2引数のクラス名は文字列として与える。

以下の例のように、親クラス、インターフェイス、トレイトに対する判定はinstanceofと同じ。

is_subclass_of()

is_subclass_of(インスタンス, 'クラス等名');

is_subclass_of()は、第1引数のインスタンスのクラスが第2引数のクラスを継承しているかどうかをチェックする。引数の渡し方はis_a()と同じ。

以下の例のように、

  • 引数のインスタンスのクラスそのものは偽
  • 継承した親クラスとインターフェースに対しては真
  • トレイトは偽

class_uses()

class_uses(インスタンス);

class_uses()は、引数インスタンスのクラスがuseしているトレイト連想配列として返す。連想配列のキー・値とも、トレイト名の文字列。

トレイトをuseしているかどうかをチェックするには、class_uses()の結果の連想配列に対して、in_array()array_key_exists()でトレイト名が含まれるかどうかをチェック。

親クラスでトレイトをuseしていても、子クラスのclass_uses()では捕捉されない。

method_exists()/property_exists()

method_exists(インスタンス, ’クラス等名’)
property_exists(インスタンス, ’クラス等名’)

method_exists()は、第1引数で指定したインスタンスのクラスに第2引数で指定したメソッドが定義されているかどうかをチェックする。同様に、property_exists()はプロパティーが定義されているかどうかをチェックする。

以下の例で、method_exists()について確認する。

  • 指定したメソッドが定義されていればtrue、定義されていなければfalse
  • 親クラスのメソッドでも存在する(true)と判定(publicprivateに関わらない)
  • トレイトのメソッドも存在すると判定
  • 親クラスでuseされたトレイトのメソッドは、子クラスでも存在すると判定

 

PHP – array_key_exists() – 配列のキーの存在確認

概要

array_key_exists()$keyが配列のキーとして存在するかどうかをチェックする。比較にあたって、常に型変換が自動で行われる(‘==’と等価)。型までのチェックはされない。

array_key_exists($key, 配列)

連想配列への適用例

以下は連想配列に対する実行例。文字列と数値が自動で変換されている。

配列への適用例

配列に対しても適用可能で、この場合はインデックスが存在するかどうかがチェックされる。このときも自動的に型が変換される。

 

PHP – in_array() – 配列の値の存在確認

概要

in_array()は、指定した$needleが配列($heystack)の要素として含まれるかどうかをチェックする。

in_array($needle, 配列, $strict=false)

  • デフォルトでは$strict=falseで、型変換を行って内容比較する
  • $strict=trueを指定すると、型の一致まで確認する

配列の要素の確認

以下の例ではデフォルトの$strict=falseとなっていて、文字列・数値の変換が自動的に行われている('=='と同じ効果)。

$strict=trueを指定すると、文字列と数値の違いも判定される('==='と同じ効果)。

連想配列の値の確認

in_array()で連想配列を指定すると、キー・値のうち指定した$needleが「値として含まれるか」チェックする。キーとして含まれていてもfalseになる。

デフォルトの$strict=falseでは型変換が自動的に行われる。

$strict=trueに指定すると、型の一致までチェックする。

注意点

  • in_array()は空の配列やnullに対してfalseを返す
  • nullに対しては警告が出される

 

PHP – トレイト – メソッドの汎化

概要

インターフェイスはクラスがメソッドを実装することを明示・保証するが、その実装はインプリメントしたクラスに託されている。

一方トレイトは、予めメソッドを具体に実装しておき、これを用いるクラス中でuse文によって導入される。

トレイトの使い方

定義

traitキーワードでトレイトを定義し、その中で具体のメソッドを定義する。

トレイトの実装

トレイトを導入するクラスで、useキーワードで導入するトレイトを宣言し、メソッドを呼び出す。

実装例

ベースになるコード

インターフェイスで例示したRPGキャラクターのコードを使う。

  • 基底クラスCharacterClass:名前を持ち武器攻撃のみできる
  • インターフェイスSpellcaster:呪文を唱えるメソッドspell()を宣言
  • Swordfighterクラス:CharacterClassを継承
  • Wizardクラス:CharacterClassを継承し、Spellcasterをインプリメント
  • Priestクラス:CharacterClassを継承し、Spellcasterをインプリメント

要件

  • SwordfighterPriestに、クリティカルアタックの機能を持たせる
  • クリティカルアタックはどのクラスにおいても共通の技

トレイトの追加

以下のトレイトを追加する。

トレイトの導入

SwardfighterクラスとPriestクラスでトレイトを導入する。

トレイトのメソッドの利用

クラスのインスタンスでトレイトのメソッドを呼び出す。

留意点

  • プロパティーも宣言・定義できる
  • トレイトを導入するクラスを意図した$thisを使える
  • インスタンスのクラスがトレイトを導入している場合、class_uses()メソッドで導入しているトレイト名を得られる

class_uses~実装しているトレイトの確認

たとえば上の実装例で、別のトレイトも加えてclass_uses()で内容を確認してみる。トレイト名をキー・値とする連想配列となっていることがわかる。

これまでの例で、各クラスのインスタンスを配列に入れて、クラスに応じた動作をさせる。

トレイトを実装しているかどうかのチェックは、class_uses()で導入しているトレイトの連想配列を得て、そのキーに対象となるトレイト名があるかどうかをarray_key_exists()でチェック。

 

PHP – インターフェイス

概要

  • PHPではクラスの多重継承はできないが、複数のインターフェイスをインプリメントできる
  • インターフェイスでは実装すべき関数の宣言のみ行い、実装はこれをインプリメントした関数で行う
  • これにより、インターフェイスを実装するクラスが持つべきメソッドが明示・保証される

基本形

インターフェイスの宣言・定義

インターフェイスの実装

実装例

RPGのキャラクターをイメージしたキャラクターを実装していく。

親クラス

全てのキャラクターはCharacterClassクラスを継承し、

  • 名前を持つ
  • 武器で攻撃するattack()メソッドを持つ

Swordfighterクラス

SwordfighterクラスとWizardクラスはCharacterClassを継承し、武器による攻撃ができる。

Spellcasterインターフェイス

Spellcasterインターフェイスは呪文を使える種族を表し、呪文を唱えるspell()メソッドを持つ。spell()メソッドの実装は、インプリメントするクラスで定義する必要がある。

Wizardクラス

WizardクラスはCharacterClassクラスを継承するとともに、Spellcasterインターフェイスをインプリメントする。Wizardは武器による攻撃ができるほか、spell()メソッドで攻撃魔法を実装している。

Priestクラス

PriestクラスはCharacterClassクラスを継承するとともに、Spellcasterインターフェイスをインプリメントする。Priestは武器による攻撃ができるほか、spell()メソッドで味方の治療を実装している。

ポリモーフィズムによる実行

SwordfighterWizardPriestのインスタンスを生成し、$party_members配列に保存する。

  • 全てのメンバーに武器で攻撃させる
  • その後、Spellcasterのメンバーに呪文使用させる

インターフェイス実装の確認

あるクラスが指定したインターフェイスをインプリメントしているかどうかは、クラスのチェックと同じ型演算子instanceofで確認できる。