今回は前回に続けて開発を進めていきましょう。
前章をまだご覧になっていない方は以下から開発を始めましょう。
5 章:スレッド作成機能
MVC の復習
MVC 覚えておりますでしょうか。
そんな方は、下記の記事の「MVC のイメージをつかむ」をみてみよう。
また、もっと知りたいよ!という方は是非ググってみましょう。
では、軽く MVC を思い出したところで
重要な機能である「スレッド作成」機能を実装していきましょう。
必要ファイルの作成
それではまずは、一覧画面を作成していきましょう。
- コントローラー
- ルーティング
- ビュー
の順で作成していきましょう。
laradock % docker-compose exec workspace bash /var/www# php artisan make:controller ThreadController --resource Controller created successfully.
`–resource` とは?という方は以下のドキュメント「リソースコントローラー」を確認してみましょう。
https://readouble.com/laravel/6.x/ja/controllers.html#resource-controllers
web.php <?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); Route::resource('/threads', 'ThreadController')->except(['create', 'update']);
新規作成画面、更新処理は行わない予定なので、ルートで処理するアクションの一部を指定しました。
ルーティングが正常に機能しているかを念の為に確認しておきましょう。
laradock % docker-compose exec workspace bash root@b15d7b5e8dc3:/var/www# php artisan route:list +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/user | | Closure | api,auth:api | | | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth | | | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest | | | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest | | | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web | | | GET|HEAD | password/confirm | password.confirm | App\Http\Controllers\Auth\ConfirmPasswordController@showConfirmForm | web,auth | | | POST | password/confirm | | App\Http\Controllers\Auth\ConfirmPasswordController@confirm | web,auth | | | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web | | | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web | | | POST | password/reset | password.update | App\Http\Controllers\Auth\ResetPasswordController@reset | web | | | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web | | | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest | | | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest | | | GET|HEAD | threads | threads.index | App\Http\Controllers\ThreadController@index | web | | | POST | threads | threads.store | App\Http\Controllers\ThreadController@store | web | | | GET|HEAD | threads/{thread} | threads.show | App\Http\Controllers\ThreadController@show | web | | | DELETE | threads/{thread} | threads.destroy | App\Http\Controllers\ThreadController@destroy | web | | | GET|HEAD | threads/{thread}/edit | threads.edit | App\Http\Controllers\ThreadController@edit | web | +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+
それではコントローラーの処理を記載していきましょう。
まずは、一覧画面を表示する画面を記入していきます。
ThreadController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ThreadController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { return view('threads.index'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // } }
index メソッドに threads\index.blade.php を表示するように記載しました。
まだ、threads\index.blade.php は作成していない為、作成しましょう。
views\threads\index.blade.php @extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <h5 class="card-header">新規スレッド作成</h5> <div class="card-body"> <form method="POST" action="{{ route('threads.store') }}"> @csrf <div class="form-group"> <label for="thread-title">スレッドタイトル</label> <input name="name" type="text" class="form-control" id="thread-title" placeholder="タイトル"> </div> <div class="form-group"> <label for="thread-first-content">内容</label> <textarea name="content" class="form-control" id="thread-first-content" rows="3"></textarea> </div> <button type="submit" class="btn btn-primary">スレッド作成</button> </form> </div> </div> </div> </div> </div> @endsection
Bootstrap : Card
https://getbootstrap.jp/docs/4.2/components/card/
Bootstrap : Forms
https://getbootstrap.jp/docs/4.2/components/forms/
デザインは上記の Bootstrap から拝借しています。
Form の送信先は `route(‘threads.store’)` としております。

ルーティングの名前で指定しているんだよな。
今回でいうと、App\Http\Controllers\ThreadController@store になっているな。
php artisan route:list で確認できるね。
それでは一度現状がどうなっているのかを確認してみましょう。

質素なデザインですが、掲示板作成の画面を作ることができました。
Threads テーブルに「内容」に該当するカラムがないんでは?と思った方。正解です。
今回は、新規スレッドを立ち上げる際には「内容」(messages テーブルのレコード)を同時に作成する設計でコーディングしていきます。
FormRequest Validation

バリデーションは Form の入力チェックだよね。
名前は〇〇文字以上にしてください、とかメアドはメールアドレスの形式で入力されていますか、とか。
そうですね。
バリデーションは Controller に記入しても良いのですが、
コントローラーが Fat(太る) になってしまいます。
FatController はコードが冗長になり、読みにくいコードになってしまうことが多いです。
バリデーションは FormRequest に切り出しましょう。
フォームリクエストバリデーション
https://readouble.com/laravel/6.x/ja/validation.html#form-request-validation
では作成していきます。
laradock $ docker-compose exec --user=laradock workspace bash /var/www# $ php artisan make:request ThreadRequest
Requests\ThreadRequest.php <?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ThreadRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required', 'content' => 'required|min:10', ]; } public function messages() { return [ 'name.required' => trans('validation.required'), 'content.required' => trans('validation.required'), 'content.min' => trans('validation.min') ]; } }
実際に Form から送信された入力内容を作成した FormRequest でチェックするようにしましょう。
ThreadController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests\ThreadRequest; class ThreadController extends Controller { // 略 /** * Store a newly created resource in storage. * * @param App\Http\Requests\ThreadRequest $request * @return \Illuminate\Http\Response */ public function store(ThreadRequest $request) { // } // 略
こちらで store メソッドの処理の前にユーザーからのリクエスト内容を ThreadRequest でチェックできるようになりました。
バリデーションに引っかかった際のメッセージを表示するコードを追加していきましょう。
views\threads\index.blade.php @extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> @include('layouts.flash-message') <div class="card"> // 略

@include でフラッシュメッセージを表示する blade を読み込んでいるのか。
フラッシュメッセージを表示するコンポーネントは他の blade にも使いそうだもんな。
使い回せるものは、出来る限りコンポーネントにしておくと便利ですね。
では実際に使い回すコンポーネントを作成していきましょう。
views\layouts\flash-message.blade.php @if ($message = Session::get('success')) <div class="alert alert-success alert-block"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>{{ $message }}</strong> </div> @endif @if ($message = Session::get('error')) <div class="alert alert-danger alert-block"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>{{ $message }}</strong> </div> @endif @if ($message = Session::get('warning')) <div class="alert alert-warning alert-block"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>{{ $message }}</strong> </div> @endif @if ($message = Session::get('info')) <div class="alert alert-info alert-block"> <button type="button" class="close" data-dismiss="alert">×</button> <strong>{{ $message }}</strong> </div> @endif @if (count($errors) > 0) <div class="alert alert-danger alert-block"> <button type="button" class="close" data-dismiss="alert">×</button> @foreach ($errors->all() as $error) <strong>{{ $error }}</strong> @endforeach </div> @endif

Session::get(‘success’) 〜 Session::get(‘info’) までで、Bootstrap を使って 4種類のメッセージ表示を可能にしているな。
最後のブロックでは、バリデーションエラーの為のものだな。
ここまでで、実際に入力がバリデーションに引っかかった際のテストをしてみましょう。
また、バリデーションルールにはたくさん種類がありますので、目を通しておきましょう。
https://readouble.com/laravel/6.x/ja/validation.html#collection-method-list2
保存処理
それでは、バリデーションの処理が完了しましたので実際に保存処理を書いていきましょう。
DB の処理を記載していきますので、まずはモデルを作成していきます。
Thread モデル、Message モデルを作成していきます。
Eloquent : 利用の開始
https://readouble.com/laravel/6.x/ja/eloquent.html
laradock % docker-compose exec workspace bash /var/www# php artisan make:model Thread /var/www# php artisan make:model Message Model created successfully.
Thread.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Thread extends Model { /** * The table associated with the model. * * @var string */ protected $table = 'threads'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'user_id', 'is_user_checked', 'latest_comment_time' ]; }
Message.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Message extends Model { /** * The table associated with the model. * * @var string */ protected $table = 'messages'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'body', 'thread_id', 'user_id' ]; }
モデルを通してデータベースからデータを取得、保存、更新、削除出来るようになりましたので、実際に保存処理を記入していきましょう。
保存処理できそう!という方はぜひ挑戦してみてくださいね。

これは前回の Laravel 掲示板でもやったから、挑戦してみるぞ。
前回の記事はこちら。
https://aburasoba.org/laravel6-board-app-1/
ThreadController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests\ThreadRequest; use App\Message; use App\Thread; use Carbon\Carbon; use Illuminate\Support\Facades\Auth; class ThreadController extends Controller { public function __construct() { $this->middleware('auth')->except('index'); } // 略 /** * Store a newly created resource in storage. * * @param App\Http\Requests\ThreadRequest $request * @return \Illuminate\Http\Response */ public function store(ThreadRequest $request) { // save Thread $thread = new Thread(); $thread->name = $request->name; $thread->user_id = Auth::id(); $thread->latest_comment_time = Carbon::now(); $thread->save(); // save Message $message = new Message(); $message->body = $request->content; $message->user_id = Auth::id(); $message->thread_id = $thread->id; $message->save(); // redirect to index method return redirect()->route('threads.index')->with('success', 'スレッドの新規作成が完了しました。'); }

できたぞ。
ポイントは
① middleware をつけた。
Message, Thread には user_id が必要でしょ。ログインしているユーザーだけが store メソッドにアクセス出来るようにした。
② Carbon クラスを使った。
Thread テーブルの latest_comment_time に日時を保存するために Carbon クラスを使ったよ。
③ redirect で with を使いフラッシュメッセージを表示した。
先ほど作成した、flash-message.blade.php を使う為に with でセッションに「success」という key で「スレッドの新規作成が完了しました。」という value を保存した。これで保存ができたらフラッシュメッセージが表示出来る!

ログインした状態で、入力して、保存。。。。

保存できていますね。素晴らしい。
ですが、まだまだ修正する場所はありそうですね。
これは次の章で確認していきましょう!
それでは、先に動作確認として、MySQL で保存されているかを確認していきましょう。
Laradock の MySQL コンテナに接続
では、Laradock で立ち上げている MySQK コンテナに接続して、先ほどのスレッド情報が保存できているかを確認していきましょう。
それでは ターミナルでコマンドを打ち込んでいきます。
laradock tt% docker-compose exec mysql bash root@bf402a279a73:/# mysql -u default -proot mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.28 MySQL Community Server (GPL) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>

お、 MAMP の MySQL で見たことがある画面になったぞ。ちなみに
-u default -proot の「default」と「root」はどこから出てきたんだ?
良い指摘ですね。
これらは、「第二章:環境構築 Laradock を使ってみる」で記載したコードを元に指定しています。
もう一度確認してみましょう
以下は Laradock の .env です。(2ch の .env ではありません)
.env ### MYSQL ################################################# MYSQL_VERSION=5.7 MYSQL_DATABASE=2ch // DB の名前を 2ch にしています。 MYSQL_USER=default // default という user を MySQL に作ります。 MYSQL_PASSWORD=root // パスワードは root にしています(覚えやすいので)。 MYSQL_PORT=3306 MYSQL_ROOT_PASSWORD=root MYSQL_ENTRYPOINT_INITDB=./mysql/docker-entrypoint-initdb.d
思い出しましたでしょうか。
こちらで、データベースの名前、ユーザー、パスワードを作成しました。
よって、default と root で MySQL にログインできたことがお分かりいただけたと思います。
では、続きをやっていきましょう
mysql> use 2ch Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from threads; +----+---------+-----------+-----------------+---------------------+---------------------+---------------------+ | id | user_id | name | is_user_checked | latest_comment_time | created_at | updated_at | +----+---------+-----------+-----------------+---------------------+---------------------+---------------------+ | 1 | 1 | testtestt | 0 | 2021-04-18 10:09:13 | 2021-04-18 10:09:13 | 2021-04-18 10:09:13 | +----+---------+-----------+-----------------+---------------------+---------------------+---------------------+ 1 row in set (0.00 sec)
いかがでしょうか。上記で、先ほど入力した thread の名前のデータが保存されているはずです。
ここまでで、スレッド作成機能の実装が完了しました。
次の章で、先ほどのコードのリファクタリングを行っていきます。
5 章での変更点は以下からご確認いただけます。
https://github.com/t-aburasoba/thread-board/pull/4/files
MVC ? 確か、Model View Controller の先頭の文字をとった、Web フレームワークで使われる設計思想だよな。
各々がどんなな役割を担うかは忘れたな。。