PHPのフレームワークLaravel で blog アプリを作成しながら、laravelの使い方を説明しています。
Laravelはバージョン7を使います。
仕様と手順は、以下の記事の通りです。
今回は、3. アプリ作成の【7 Blog機能作成】の続きとなります。
今回の学習ポイントは次のとおり。
7.7. 新規投稿機能の作成 + ログインチェック機能追加
いろんな書き方はあるのですが、基本的であり、応用が利きそうな方法を使って作成していきます。
今回作成する新規投稿のページは下図のようになります。
なお、前回までで投稿一覧ページ・投稿詳細表示ページまで作成しましたが、もしまだの方は、作成してから、またこちらに戻ってきてくださいね。
↓のリンクに一覧を載せてます。
新規投稿機能の作成
ログイン認証を入れる
新規投稿機能ですが、今回は【投稿できるのはログインしたユーザだけ】という仕様で作っていきます。
今回作成しているアプリの仕様は「作成予定のWebアプリケーションの概要」で書いてます。
もしログインしていないゲストユーザでアクセスしたときは、【ログイン画面】を表示させたいです。
Laravelなら、こういったことを手軽に実現できる認証機能がついています。
ログインしたユーザだけアクセスさせたいときにLaravelで用意されている機能は、authミドルウェアといいます。
authの意味ですが、Authentication(認証という意味)の略。Laravelではログイン認証機能もAuthと略されて表記されています。
使い方ですが、コントローラのコンストラクターでmiddlewareメソッドを呼び出すことで実現できます。
ほかにもルート設定に記述する方法がありますが、公式アニュアルに説明があります。↓
上のマニュアルに、下図のような記述があります。
僕は後半に書かれているコントローラに書く方法のほうがわかりやすくて好きです。
今回も、この方法で認証機能を付けていきます。
最初にLaravelの環境を構築する際、認証機能を追加しているかと思います。
このとき、app/Http/ControllersにHomeController.php ファイルが追加されます。
このHomeController.phpを開いてみてください。
上のほうをコピーします。
これを今回のPostsController のコンストラクターにコピペします。
そして、下記のように修正を加えてください。
1 |
$this->middleware('auth')->except(['index', 'show']); |
の部分が修正ポイントです。
->except(['index', 'show']) とメソッドをつないで例外を指定しています。
これは indexアクションとshowアクションは例外、つまり、「index と show は認証なしでアクセスOKにしてください」という指示になります。
ゲストユーザがアクセスできるのは、投稿の一覧ページと投稿詳細表示ページだけ。残りはすべてログイン認証済みのユーザのアクセスだけにしたいので、このようにindexとshowだけ例外指定してます。
少し補足:例外指定について
except(例外)指定については、公式マニュアルのコントローラのページに記載されています。↓
下図の抜粋箇所はexcept()メソッドで複数アクションを指定する方法が、ルート設定に記述する場合だけ書かれていますが、コントローラのコンストラクタに記述する場合も一緒です。
少し脱線:middlewareについて
ちなみに「middleware(ミドルウェア)って何?」って思うかもしれませんが、コンピュータでは昔からある言葉で、「中間にあるソフトウェア」のことです。
今回の場合、ルータから処理が振り分けられてきたとき、コントローラのアクションメソッドが呼ばれる前に実行される処理になります。
コントローラクラスが作られるとき、コンストラクタで設定されることにより、アクションメソッドが呼ばれるたびに、呼ばれるようになります。
ミドルウェアは難しいので、とりあえず今のところこれ以上知る必要はないですが、一応、公式マニュアルのミドルウェアのページの冒頭に、どんなものか書いてあります。↓
ユーザ認証の確認機能があるミドルウェアのついて言及されてますね。
createアクションを作成
create は、投稿新規作成用のフォームを表示するアクションです。
まず、createを作りましょう。
コントローラにcreateメソッドの実装
PostsController の create()メソッドには、一行だけ実装します。
1 |
return view('posts.create'); |
views/posts/create.blade.php を使って表示用htmlを作成するという意味です。
新規作成なので、特にモデルからの読み出しもありません。
ビュー posts.create の実装
次に、ビューの実装です。
ユーザーに表示されるcreateページを作っていきます。
views/posts フォルダにcreate.blade.php ファイルを作って、下リストの内容を実装してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@extends('layouts.app') @section('content') <div class="container"> <h1>新規投稿</h1> <form action="{{route('posts.store')}}" method="post"> @csrf <div class="form-group"> <label for="title">タイトル</label> <input type="text" name="title" id="title" class="form-control" value="{{old('title')}}" placeholder="タイトルを入力してください"> </div> <div class="form-group"> <label for="body">本文</label> <textarea name="body" id="body" class="form-control" rows="5" placeholder="本文を入力してください">{{old('body')}}</textarea> </div> <input type="submit" value="投稿" class="btn btn-primary"> <input type="reset" value="キャンセル" class="btn btn-secondary" onclick='window.history.back(-1);'> </form> </div> @endsection |
下図のような感じになります。
以下、HTMLとフォームの部分の説明と、既出の説明は割愛して、bladeテンプレートエンジンでまだ説明していない項目の説明をします。
CSRF対策
1 |
@csrf |
この一行は、CSRF(シーサーフ)対策のためのトークンを埋め込んでくれます。
フォームの中で、input タグの type = hidden として、ページには見えない隠し入力値として埋め込まれます。下図のような感じです。
CSRFは、説明するとちょっと長くなるので、下記の水色の補足欄にまとめました。
Webプログラマとして必須の知識なので、もし知らなければ、ぜひ、目を通してください。
入力エラー時の前回値の表示 old()関数
1 |
{{old('title')}} |
old()関数は、ヘルパと呼ばれるLaravelのお助け関数の一つで、直前の入力値を取得します。
フォームにはバリデーションという入力値のチェック機能が付きます。
たとえば、タイトルに何も入力していないとき、「タイトルを入力してください」とエラー表示させたりする機能です。
エラーが表示された時にも、ユーザーがこれまでにフォームに入力した値は保持されるようにしておく必要があります。
old()関数を使えば、バリデーションでエラーになった時、入力していた値を読み出すことができます。
技術的には、バリデーションでエラーとなった時、フォームの入力データをセッションデータとしてサーバに記録しておき、old関数ではセッションデータを読み出す感じです。
old関数については、Laravelマニュアルの「ヘルパ」のページに記載があります。
storeアクションを実装
次にでは、新規投稿フォームから投稿ボタンが押された時の処理であるstoreアクションを実装していきます。
PostsControllerにstoreメソッドを実装
まず、コントローラを実装しましょう。PostsController の storeメソッドに以下の処理を実装してください。
1 2 3 4 5 6 7 8 9 10 11 12 |
$request->validate([ 'title' => 'required|max:255', 'body' => 'required' ]); $post = new Post; $post->user_id = auth()->user()->id; $post->title = $request->input('title'); $post->body = $request->input('body'); $post->save(); return redirect(route('posts.index'))->with('success', 'ブログに新規投稿しました'); |
実装後のリストは下図のようになります。
コードについて説明していきます。
バリデーション(入力データのチェック)
1 2 3 4 |
$request->validate([ 'title' => 'required|max:255', 'body' => 'required' ]); |
まずバリデーションしています。
フォームのバリデーションというのは入力データのチェックという意味です。
$request 変数には、ブラウザから送られてきたリクエストデータが入っており、validateメソッドでバリデーションのルールを指定してバリデーションを実行します。
ルールは2行指定してます。ここでは、
- タイトルは、必須項目で、最大255文字までというルール、
- 本文は必須項目 というルール
を指定しています。
バリデーションの結果、フォーム入力に問題がないときは、次の行が実行されます。問題がある場合は、エラーメッセージと現在の入力値をセッションデータとして準備して、フォームのページにリダイレクトします。
先ほど、create.blade.php の説明で、old() 関数を準備したのは、このセッションデータを利用するためです。エラーメッセージの表示については、後ほど解説します。
狙いとしては、下図のような感じでのバリデーションエラー表示です。
本文には入力した文字が残っています。そしてエラーメッセージは上部に表示されています。
バリデーションについては、マニュアルのバリデーションのページに記載があります。
バリデーションルールはいっぱいあります。マニュアルの同じページに記載があります。
下図のような感じです。
新規投稿をPostsモデルとして作成・postsテーブルに保存
バリデーションOKなら次の行が実行されます。
1 2 3 4 5 |
$post = new Post; $post->user_id = auth()->user()->id; $post->title = $request->input('title'); $post->body = $request->input('body'); $post->save(); |
ここでは、まず、Post モデルのインスタンスを新規作成して変数$postに代入してます。
そして、$post の user_id に、ログイン中ユーザのid を入力してます。
ログインしていない状態だと、auth()->user()->id がエラーになります。
$post->title = $request->input('title') は、$post の title にフォームの title 入力値を代入してます。
$post->title = $request->input('body') に関しても同様ですね。
$post->save() で$post の内容をテーブルに保存します。
リダイレクト
最後に、ブラウザに表示するページのhtmlを作成し、戻り値として渡して処理を終了しています。
ここではredirect を使ってるのでhtmlを作成するのではなく、「リダイレクトしてください」という指示を返答していますね。
1 |
return redirect(route('posts.index'))->with('success', 'ブログに新規投稿しました'); |
redirect()関数 は、違うURLのページを表示するという意味で、URLの指定にはroute()関数を使用してルート設定で付けた名前を利用しています。
posts.index は、「投稿の一覧表示」ページのURLです。
with()メソッドも、ビューについて解説した記事で既出ですね。
ただし、今回はリダイレクトなので view の時のように変数として使うのではなく、リダイレクト先でセッションデータとして使用することになります。
リダイレクト先のページにセッションデータで「ブログに新規投稿しました」という内容の’success’という名前のデータを渡す役割を果たします。
ビューにメッセージ表示処理を追加
store用のビューは、今回は実装しません。
ただエラーメッセージやsuccessメッセージを渡すためのビューを追加しましょう。
これはテンプレートの共通部分に追加しておくと便利なので、今回もそうします。メッセージがあるときだけ表示させることができます。
resources/views/layouts/app.blade.php の @yield('content') の手前に以下の内容を追加しましょう。これは、inc/msg.blade.php に書かれた内容をそのままここに挿入するという意味になります。
メッセージ表示部分がちょっと長くなるので、app.blade.php の読みやすさを保つために、別ファイルにするという意味があります。@include(インクルード)を使ってみる練習にもなります。
1 2 3 |
<div class="container"> @include('inc.msg') </div> |
挿入後のapp.blade.php は下図のような感じになります。
では、inc/msg.blade.php を作りましょう。
まず、resources/viewsフォルダに新しくincフォルダを作ります。
その中に、msg.blade.php ファイルを作って以下の内容を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@foreach ($errors->all() as $error) <div class="alert alert-danger"> {{$error}} </div> @endforeach @if (session('success')) <div class="alert alert-success"> {{session('success')}} </div> @endif @if (session('error')) <div class="alert alert-danger"> {{session('error')}} </div> @endif |
実装後は下図のようになります。
では、コードを説明します。
バリデーションエラーの表示
1 2 3 4 5 |
@foreach ($errors->all() as $error) <div class="alert alert-danger"> {{$error}} </div> @endforeach |
この部分はバリデーションエラーを表示する部分になります。バリデーションエラーにより、もとのフォームに戻ってくると、$errors変数にエラーメッセージが入っています。そのメッセージをひとつずつforeach構文で取り出して$error変数に代入。
これを、ひとつづつ、 {{$error}} で表示しています。
バリデーションエラーの表示方法に関しても、マニュアルのバリデーションのページに記載があります。下に引用しますので、表示をカスタマイズしたい場合など参照ください。
SUCCESSメッセージの表示
今回、新規投稿の作成がうまく行ったとき、投稿一覧のページにリダイレクトして、「ブログに新規投稿しました」という内容の’success’といいう名前のセッションデータを渡しています。
下のコードでは、success というセッションデータがあったら表示させるという内容になってます。セッションに保存されているデータはsession()関数で読み出すことができます。
1 2 3 4 5 |
@if (session('success')) <div class="alert alert-success"> {{session('success')}} </div> @endif |
ちなみに、PostsController のindexメソッドでのview()関数 では、posts は $posts 変数として参照できました。
1 |
return view('posts.index')->with('posts', $posts); |
ですが今回のようにリダイレクトした場合は、ページをまたいでいるのでセッションデータとして参照する必要があります。
ERRORメッセージの表示
今回使ってませんが、SUCCESSメッセージのような感じで、ERRORメッセージも表示できるようにしておきましょう。
1 2 3 4 |
@if (session('error')) <div class="alert alert-danger"> {{session('error')}} </div> |
ついでに用意しておけば、バリデーション以外のチェック処理なんかでエラーにしたい場合、メッセージを表示することができて便利なので。
バリデーションエラーの日本語表示と投稿時刻を日本時間に変更
このままでは、バリデーションエラー時のメッセージが英語になってしまいます。日本語表示にしましょう。
設定ファイルapp.phpの変更
まず設定ファイルを変更します。設定ファイルは configフォルダの中にいくつか入っていますが、今回は config/app.phpファイルを2か所変更します。
まず、timezone です。投稿日時など時刻に日本時間を使いたいため変更します。
デフォルト設定はUTCとなっています。UTCは協定世界時と言って世界標準時刻ですが、イギリスの時間になっています。日本より9時間遅い時間になっています。
‘UTC’ ⇒ ‘Asia/Tokyo’ に変更しましょう。
1 |
'timezone' => 'Asia/Tokyo', |
次にエラーメッセージです。
Laravelでは多言語を切り替えられるような仕組みになっていますので、まずは、config/app.php の local を en(英語)からja(日本語)に変更します。
1 |
'locale' => 'ja', |
locale(ロケール)は場所という意味の言葉です。よく言語設定で出てくる用語となっています。
メッセージファイルのコピーと修正
バリデーションエラーメッセージですが、 resources/lang/en フォルダの中のvalidation.php ファイルに記述されています。
今回、ロケールをjaにしたのですが、アプリケーションフォルダには、デフォルトで英語のメッセージしか用意されていません。そこで日本語用のメッセージを準備する必要があります。
ベースとなるファイルを英語用からコピーして作りましょう。
メッセージファイルをコピー
resources/lang フォルダの下に ja フォルダを作成します。そしてresources/lang/en フォルダの中のvalidation.php ファイルを jaフォルダの下にコピーします。
メッセージファイルの修正
コピーしてきたvalidation.php のうち、今回のバリデーションエラーに関係のあるところだけ修正しましょう。
まず、required で検索して バリデーションのrequired のメッセージが書かれた部分を検索します。
そして、
1 |
'required' => 'The :attribute field is required.', |
の部分を
1 |
'required' => ':attribute を入力してください', |
のように修正します。
:attribute の部分は、フォームの要素のname属性になっています。フォームのname属性から日本語表記に変更しましょう。
同じ validation.php ファイルの一番下のほうの 'attributes' => [], の部分に、変換したいname属性を指定します。今回は下リストのように2項目設定します。
1 2 3 4 |
'attributes' => [ 'title' => 'タイトル', 'body' => '本文', ], |
変更後の該当箇所は以下のようになります。
もうひとつ、メッセージを日本語化しておきましょう。
‘max’ で検索して、下記の最大値のバリデーションエラーメッセージを探してください。
1 2 3 4 |
'max' => [ 'numeric' => 'The :attribute may not be greater than :max.', 'file' => 'The :attribute may not be greater than :max kilobytes.', 'string' => 'The :attribute may not be greater than :max characters.', |
上記の部分を、次のように修正します。
1 2 3 4 |
'max' => [ 'numeric' => 'The :attribute may not be greater than :max.', 'file' => 'The :attribute may not be greater than :max kilobytes.', 'string' => ':attributeは :max 文字以内で入力してください', |
:max の部分は、最大値として設定した文字数となります。
メニューにブログ新規投稿を追加
最後に、今回の新規投稿のページをメニューに追加しましょう。
ビューのメニュー項目追加
共通部分なので変更するのは app.blade.php になります。
今回は左側部分にメニューを追加しましょう。
「Left Side Of Navbar」とコメントのある ul の下に下記の li タグを追加します。
1 2 3 |
<li class="nav-item"> <a class="nav-link" href="{{ route('posts.create') }}">ブログ新規投稿</a> </li> |
追加後のapp.blade.php は下図のようになります。
動作確認
では動作確認しましょう。
まず、ログインしていない状態でトップページを表示させてみましょう。メニューの左側に「ブログ新規投稿」が表示されています。クリックしましょう。
ログイン認証の対象としているため、ログイン画面が表示されます。ログインしましょう。
やっと新規投稿ページが表示されます。
なにも入力せずに「投稿」をクリックするとエラー表示されることを確認します。
タイトルに255文字以上入れたときは、以下のようにエラーメッセージが表示されます。また、前回入力値がタイトルの入力欄にキープされています。
ちゃんと入力できた時には以下のように投稿一覧のページにリダイレクトして、かつ、先頭に「
ブログに新規投稿しました」のSUCCESSメッセージが表示されます。
まとめ
今回は、新規投稿機能を実装しました。
学習した項目としては以下の通りです。
- ログイン認証
- フォームのcsrf対策
- フォームからのデータ入力の処理
- バリデーションとバリデーションエラーメッセージ
- 新しい投稿の登録
- リダイレクト
- SUCCESSメッセージの表示
投稿の作成と保存を中心に、セキュリティ対策からメッセージの日本語化まで盛り込んだ内容になりました。
まずは記事を参考に、実際にコードを入力し、動作を確認してみてください。
そのあと、記事で紹介しているマニュアルの該当箇所へも目を通してみてください。
Laravelへの理解がぐっと深まり、今後、あなたがいちからLaravelで開発を進めるときに役立ちます。
それでは次は、既存投稿の編集・更新処理を解説していきますね。
===目次===
- laravelはPHPで動くフレームワーク ~laravel入門01~
- laravelでログイン機能付きサイトを作ってみよう、作成予定のWebアプリケーションの概要と作業手順の概略~laravel入門~
- 開発環境のインストール
- XAMPP
- VS code
- Git for Windows
- SourceTree
- laravelインストール
- composerインストール
- laravel本体インストール
- laravelのUIインストール
- node.jsインストール
- アプリ作成
- データベース設定
- ログイン機能追加
- npmインストール
- css, jsコンパイル
- Topページ作成
- Aboutページ作成
- 問い合わせページ作成
- Blog機能作成
- Model作成と一緒にmigrationファイルも作る。~Laravel7入門~
- リソース コントローラを自動生成して CRUD のうち一覧表示 indexアクションを実装する ~Laravel7入門~
- リソース コントローラで、詳細表示 showアクション を実装する ~Laravel7入門~
- リソースコントローラで新規作成:create, storeアクション を実装する~Laravel7入門~
- bladeテンプレートエンジンでタグを無視させつつ、改行を表示させる方法 ~Laravel7入門~
- リソースコントローラで更新:edit, updateアクション を実装する~Laravel7入門~
- リソースコントローラで削除:destroyアクション を実装する~Laravel7入門~
- 投稿にカバー画像を追加する ~Laravel7入門~
コメント