Laravel – データが属しているユーザーを取り出す

概要

投稿記事を投稿したユーザーや商品カートの保有ユーザーなど、データが所属しているユーザーを取り出す場合。1対多のリレーションの多に属するデータから1のデータを取得する。

データモデルにリレーションを設定することで、そのデータが属するユーザーを属性として取り出すことができる。

手順

データモデルにuser()メソッドを定義し、そのデータがUserモデルのデータに属していることを記述する。メソッド名はUserの単数形となっていることに注意。

これによってデータモデルにuser属性が追加され、それを介してUserの各属性を取得することができる。

モデルデータ->user->name

例えばユーザー認証は設定済みとして、ユーザーの投稿記事モデルPostsを定義済みとする。Postモデルは記事の投稿ユーザーのIDをuser_idとして持ち、外部キー制約を設定している。

まずPostモデルにuser()メソッドを定義し、belongsTo(App\User)を戻り値とする。

  • belongsTo()PostモデルがUserモデルに属することから、Postのインスタンスが属するUserのインスタンスを返す
  • このリレーション設定によって、Postモデルにuserプロパティーが加えられ、Postインスタンスが属するUserのインスタンスを取得できる

これでPostインスタンスのuserプロパティーに、その記事を投稿したユーザーへの参照が格納される。

たとえば投稿記事が$postに格納されているとして、ビュー側でユーザーを取得して表示する場合、以下のようなディレクティブになる。

 

Laravel – ログインユーザーのデータのみ取り出す

概要

ユーザーが投稿した記事や購入予定のカート・商品など、ログインユーザーに関するもののみを利用したい場合。1対多のリレーションの1から多のデータ群を取得する。

以下のいずれかの方法がある。

  • モデルのwhere()->get()で抽出する
  • Userモデルにリレーションを定義し、Userのプロパティーとして取得する

where()メソッドを使う方法

手順

全ての記事を取得するならコントローラーのアクションでPost::all()として取り出すが、この場合はwhere()メソッドで全データのうちuser_idがログインユーザーのidと同じデータだけを抽出する。

SQLではWHERE user_id = ログインユーザーidとなるが、Laravelでは以下のように書く。

モデル::where('モデルのユーザーid', \Auth::user()->id)->get()

例えばユーザー認証は設定済みとして、ユーザーの投稿記事モデルPostsを定義済みとする。Postモデルは記事の投稿ユーザーのIDをuser_idとして持ち、外部キー制約を設定している。

以下はログインユーザーの全記事をPostControllerindexアクションで取得し、indexビューで表示させる例。ビュー側で受け取った$postsにログインユーザーが投稿した記事だけが配列で格納される。

モデルのリレーションを定義する方法

手順

Userモデルにモデル名の複数形(テーブル名に相当)でメソッドを定義し、リレーションを記述する。

これによってUserモデルの属性にメソッド名と同名のプロパティーが追加され、そのユーザーのidを外部キーに持つモデルのデータのみを取り出すことができる。

そしてコントローラーのアクションで、ログインユーザーのプロパティーでログインユーザーのidを持つデータのみを取得できる。

以下は先と同じPostモデルを扱う例。

まずUserモデルにPostの複数形(テーブル名)でposts()メソッドを定義し、hasMany(App\Post)を戻り値とする。

  • hasMany()は元のモデル(User)が複数の引数モデルを持つことから、Userのインスタンスに属する複数のデータの配列を返す
  • このリレーション設定によって、Userモデルにpostsプロパティーが加えられ、Userインスタンスに属するPostの配列を取得できる

これでユーザーのインスタンスのpostsプロパティーに、そのユーザーが投稿した記事だけが格納される。

コントローラーではログインユーザー(\Auth::user())のpostsプロパティーを参照すればよいので、以下のようになる。

 

Laravel – フォーム入力・データ登録

create, store~データの入力と登録

リソースルーティングでは、以下を意図している。

create
登録するデータを作成する。フォーム入力の場合、フォームを持つビューを表示する。
store
データをデータベースに登録する。createがフォーム入力の場合、フォームのaction先をこのルートにする。

前提

モデルデータの操作で準備した枠組みを使う。ユーザー認証機能を持ち、ユーザーごとに入力されたシンプルな投稿記事Postを扱う。

流れ

  1. ドメイン名/posts/createをGETリクエスト
  2. posts.createルートでPostsControllercreateアクションを実行
  3. createアクションで入力フォームを含むビューを表示
  4. 入力フォームのactionからposts.storeへルーティング
  5. posts.storeルートでPostControllerstoreアクションを実行
  6. storeアクションでデータベースにデータを登録

createアクションの呼び出し

リソースルーティングで以下のように設定されていて、ドメイン/posts/createからPostControllercreateアクションにルーティングされる。ルーティング名はposts.create

コントローラー~createアクション

コントローラーでは、ビューの$titleを設定してresources/views/posts/create.blade.phpを表示させるだけ。

入力フォームビュー

bladeテンプレート

投稿を入力するフォームはposts/create.blade.phpに以下の要領で記述。

actionで指定しているroute('posts.store')はデータベースへの登録アクションstoreへのルーティング。

フォームリクエスト~バリデーション

フォーム入力のバリデーションをフォームリクエストに記述する。

まず、以下のコマンドでフォームリクエストを作成。

app/Http/Requestsディレクトリーに作成されたPostRequest.phpを編集し、バリデーションルールを追加。ここでは唯一のフォーム入力commentに対して、入力必須(required)と最大200文字(max:200)を配列で設定している。

コントローラー~storeアクション

use指定

PostControllerPostモデルとPostのフォームリクエストを使うため、以下のようにファイル冒頭でuse指定する(個別の名前空間指定が不要になる)。

storeアクション

入力フォームのaction先で指定されたアクションメソッドで、フォームの入力をデータベースに書き込む。

ここではPostクラスが継承しているModelクラスのcreate()メソッドで、フォームリクエストのパラメーターをセットしてデータを書き込んでいる。

  • 入力バリデーションはマスアサインメント実行時にフォームリクエストで実行され、エラーがあれば$errorsに格納される
  • Post::create()は最終的にEloquent/Buildercreate()が実行されるらしい(参照:モデルのcreate()メソッドはどこに?)

データ書き込み後posts.indexにリダイレクトしているが、これは投稿記事一覧表示へのルーティング。

モデルでの$fillable設定

create()メソッドによる登録はマスアサインメントになるので、モデルに$fillableを定義しておく。

確認

ユーザーログイン状態でドメイン名/posts/createをGETリクエストすると入力フォームのページが表示され、投稿ボタンを押すとデータが登録される。

たとえばuser1とuser2をユーザー登録しておいて・・・

それぞれの記事を投稿した結果は以下の通り。

 

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

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

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

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

 

Laravel – マイグレーションコマンド

表示

status

マイグレーションの状態表示

SQLの表示

migrateコマンドのオプションに--pretendを指定すると、マイグレーションにおけるテーブル生成のクエリーを表示する。

migrate:rollbackコマンドでも指定可能だが、rollbackの時と同じメッセージが出力される。他のコマンドでは、--pretendは指定できない。

マイグレーションの実行

マイグレートされていない全マイグレーションファイルのマイグレート

ロールバック

rollback

直前のマイグレーションのロールバック

指定したステップ分のロールバック(--step=1は指定なしと同じ)

マイグレーションの再実行

reset

実行済みの全マイグレーションのロールバック

refresh

実行済みの全マイグレーションファイルをロールバックし、マイグレーションを実行。

マイグレート済みの各マイグレーションファイルのdown()メソッドが実行された後、全マイグレーションファイルのup()メソッドが実行される。

デフォルトのdown()メソッドはテーブルをドロップするのみなので、後述のfreshと同じ効果。

fresh

全テーブルをドロップした後、全マイグレーションファイルをマイグレート。

ロールバックは行われずテーブルが再度新規に構築される。各マイグレーションファイルのdown()メソッドは実行されず、up()メソッドのみが実行される。

 

Laravel – モデル – 外部キー

外部キーの準備

通常、外部キーは相手方テーブルで自動連番として生成されたキーを対象とすることが多い。

例えば参照先のテーブルのプライマリーキーが'id'であるとして、これがAuto Incrementの場合にはマイグレーションファイルでは以下のように定義される。

$table->bigIncrements('id');

これにより生成されるカラムのデータ型はunsignedBigIntegerとなるので、参照元のテーブルで外部キーを設定するときは以下のように定義する。

$table->unsignedBigInteger('参照先テーブル名の単数形_id');

例えば参照先テーブルをcustomersとすると、マイグレーションファイルでは以下のように書かれる。

ordersテーブルから上記のcustomersテーブルを参照する場合、ordersテーブルの外部キーは以下のように記述する。

外部キーの設定

Laravelのモデルに外部キーを設定する場合、マイグレーションファイルでforeign()メソッドを使う。

具体的には、テーブルを作成するクラスのup()メソッドで呼ばれるSchema::create()メソッド内に以下を記述。

$table->foreign('外部キー')->references('参照キー')->on('参照テーブル');

外部キー制約

マイグレーションファイルで外部キーを設定した場合、デフォルトの外部キー制約RESTRICTになる。この制約をCASCADESET NULLに変更することができる。

削除、更新に対する制約はonDelete()onUpdate()をメソッドチェーンに追加して設定する。

引数は大文字でも小文字でもよく、CASCADESET NULLRESTRICTの何れかを指定する。

コード例

たとえば.投稿記事のPostモデルのテーブルがuser_idカラムを持ち、ここにUsersモデルのidを外部キーとする場合、PostのマイグレーションファイルのCreatePostsTableクラスのup()メソッドに、外部キー設定の行を記述する。

 

Laravel – ログアウト

概要

基本的な流れの一つ。

  1. form要素のアクションでlogoutを指定して、submitボタンを配置
  2. LoginControllerloggedOut()メソッドをオーバーライドしてリダイレクト先を指定

ログアウトボタンの配置

フォームとボタンによる方法

ログアウト機能を実装するビューで以下のようなform要素を記述。

アンカー要素による方法

ログアウト機能を実装するビューで以下のようなアンカー要素を記述。

アンカー要素はGETメソッドを発行するので、以下のルーティングをroutes/web.phpに追加。

その他の方法

このほか、form要素にhiddenタイプのinput要素を配置して、アンカータグでログアウトさせる方法もあるらしい。

ログアウト後のリダイレクト先を変更

LoginControllerで以下の様にloggedOut()メソッドをオーバーライドし、リダイレクト先を指定。

メソッドインジェクションのRequestは、ファイル冒頭でuseするならフルネームスペースでなくてもよい。

 

Laravel – アクセス制限

ミドルウェアの適用

コントローラー単位でアクション→ビューにアクセス制限をかける場合。コントローラーのコンストラクターでアクセス制限のミドルウェアを適用する。

$this->middleware('auth')

特定のアクションのみに適用したり、逆に適用除外にするには、only()/except()を使う。

たとえばマーケットサイトのCartControllerへのルーティングで、ログインユーザーのみ処理させるには以下の様にコントローラーを書く。

こうすると、ログイン状態でアクセスするとindexなどのアクションが実行されてビューに遷移するが、ログイン状態でない場合はログインページに遷移する。

middleware(‘auth’)

コンストラクターの$this->midlleware('auth')は、コントローラーの全アクションに対して名前'auth'のミドルウェアを適用する。

'auth'Kernel.phpでルートミドルウェアAuthenticateに結び付けられている。

Authenticateミドルウェア

Authenticateミドルウェアは、Laravelのプロジェクト作成時にapp/Middlewareディレクトリーに生成されている。

  • このミドルウェアのhandle()メソッドはIlluminate\Auth\Middleware\Authenticateで定義されている
  • $request->expectsJson()でログイン状態にあるかどうかを判定しているようだが、その流れはよくわかっていない

この'login'へのルーティングを変更すると、未ログインの場合の遷移先を変更できる。

 

Laravel – ログインユーザーの取得

ログインユーザーとプロパティーの取得

ログイン済みのユーザーは、staticメソッド\Auth::user()で取得できる。

ログインユーザーのプロパティーは、以下のように取得できる。

Authは名前空間のルートにあるので先頭の'\'が必要(\Auth)。

なお、\Auth::user()は未ログイン状態ではエラーとなるので、コントローラーでアクセス制限をかけるか、\Auth::check()@auth....@endauthディレクティブでチェックを入れる。

例えば以下は、ビューでログインユーザーを表示させる例。

ただし未ログイン状態のときはエラーになる。ログイン状態の時だけ実行する書き方として、以下の2通りがある。

または

 

Laravel – LoginController

概要

LoginControllerは、Laravelのプロジェクト開始時にapp/Http/Controllers/Authディレクトリー下に生成される。

ルーティングファイルにAuth:routes()を記述することで、このコントローラーの各アクションは以下のようにURLと結び付けられる。

method URL action name
GET /login showLoginForm login
POST /login login

ソースコード

内容

コントローラーの機能

ソースコード冒頭のコメントの要旨は以下の通り。

  • このコントローラーは、アプリケーションにおけるユーザー認証とホームページへの遷移を行う
  • コントローラーはトレイト(AuthrsenticatesUsers)を用いてこの機能を導入している。

AuthenticatesUsersトレイトの導入

AuthenticatesUsersはLaravelで準備されたトレイトで、showLoginFormアクション、loginアクションを実装している

登録後の遷移先

ログイン後の遷移先を設定している。ログイン後の遷移先を変更するには、ここでリダイレクト先を変更するか、HOME定数を変更する。

RouteServiceProviderはコントローラーの冒頭でuseされている。

RouteServiceProviderを見ると、public定数HOME'/home'に設定されている。

ミドルウェアの適用

コントローラーのコンストラクターでミドルウェアを適用している。

'guest'Kernel.phpでルートミドルウェアの名前として定義されていて、RedirectIfAuthenticatedミドルウェアを指している。

RedirectIfAuthenticatedミドルウェアはコントローラー実行前にAuth::guard()メソッドを呼び出している。