困りごと
PHP歴3ヶ月、Laravel歴1ヶ月の初学者です。LaravelのDBに値が保存できずにハマってしまったので、同じような方が減ることを祈って投稿します。「データの入力→DB保存→DB呼び出し→データの表示」をしようとした際、下図の赤枠のように値が一項目だけ表示されず、エラーも吐き出されないという事象が発生しました。
(この事象に何度かハマったことが何度かあり、その度に少し悲しくなります…)
開発環境
PHP 7.2.34 / Laravel 6.20.5 / phpMyAdmin 4.9.3
結論
Eloquentモデルを使ってcreateする際は、Modelクラスに割り当ての許可が必要。
許可の方法はホワイトリスト方式($fillable
) or ブラックリスト方式($guarded
) 。
解決に至るまでに疑ったこと
①変数名の入力ミス(タイポ)
これがほとんどなので無条件で一番初めに疑いますね。変数名が長くて不安な時は、View,Controller,Model全ての変数名をコピペして確実に潰します。本件は違いました。
次にどの過程でバグが発生しているかを下記のように場合分けしました。なるべく作業の最小単位でバグを探すと良いですよね。
②POSTされた値がコントローラーで取得できていない
③コントローラーで取得した値がDBに保存されていない
④DBから表示する際に取得できていない
全体のコードイメージは下記のようになります。
Route::get('/contract','ContractController@create');
//…略
<form action="/contract" method="post">
@csrf
<tr>
<th>使用開始</th>
<td>
<input type="date" name="dateDeparture" value="{{old('dateDeparture')}}">
<input type="time" name="timeDeparture" value="{{old('timeDeparture')}}">
</td>
</tr>
<tr>
<th>使用終了</th>
<td>
<input type="date" name="dateRevert" value="{{(old('dateRevert'))}}">
<input type="time" name="timeRevert" value="{{old('timeRevert')}}">
</td>
</tr>
<tr>
<th>車両情報</th>
<td>
<dd>車種名:{{$ownerInfo->nameCar}}(最大{{$ownerInfo->numPeople}}人乗り)
<dd>ナンバープレート:<input type=text name="carNumber" value="{{old('carNumber')}}">
</td>
</tr>
<tr><th>使用料金 </th>
<td>
<input type="text" name="subTotal" value="{{old('subTotal')}}">
</td>
</tr>
</form>
class ContractController extends Controller
{
public function create(Request $request) {
//バリデーション
$request->validate([
'dateDeparture' => 'required',
'timeDeparture' => 'required',
'dateRevert' => 'required|after_or_equal:dateDeparture',
'timeRevert' => 'required',
'carNumber' => 'required|string',
'subTotal' => 'required|numeric|min:1',
'confirm' => 'required',
]);
//情報の保存
Contract::create([
'nameDriver' => $request->nameDriver,
'nameOwner' => $request->nameOwner,
'dateDeparture' => $request->dateDeparture,
'timeDeparture' => $request->timeDeparture,
'dateRevert' => $request->dateRevert,
'timeRevert' => $request->timeRevert,
'nameCar' => $request->nameCar,
'numPeople' => $request->numPeople,
'carNumber' => $request->carNumber,
'subTotal' => $request->subTotal,
]);
バグを検証していきます。
②POSTされた値がコントローラーで取得できていない
これに関しては、Laravelお馴染みのdd($value)
で確認可能です。ContractControllerクラスのcreateメソッド内で確認したところ、postされた値は全て取得できていました。
③コントローラーで取得した値がDBに保存されていない
phpMyAdminを使用して確認したところ、この時点で値が抜け落ちていることが分かりました。つまり、「POSTされた値をコントローラーは取得しているが、DBへ保存する際に抜け落ちている。View→Controllerは問題無いため、残るModelが怪しい」と想定されます。
この辺りで私も閃いて、 ModelクラスのホワイトリストにnameCarだけ入れ忘れていることに気付いて追加。これで解決しました。
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Contract extends Models
{
protected $fillable = [
'nameDriver',
'nameOwner',
'dateDeparture',
'timeDeparture',
'dateRevert',
'timeRevert',
'numPeople',
'carNumber',
'subTotal',
'nameCar', // 抜けていたため追加
];
Eloquentモデルを使ってcreateする際は、Modelクラスに割り当ての許可が必要になります。
ホワイトリストとは割り当て許可を与えたい要素を記述する方式のことで、$fillable
に配列を設定してprotectedすることで、その値のみ書き換えることができます。(fillableの直訳は充填可能)
反対にブラックリストはその要素に割り当て許可を与えない要素を記述する方式となり、$guarded
に配列します。(guardedの直訳は保護)
※ホワイトリストとブラックリストを同時に設定することはできません。
下記に詳しく書かれているのでご参考にされてください。
https://readouble.com/laravel/5.6/ja/eloquent.html
最後に
割り当てを与えていないだけではエラーとして吐き出されないため、原因が分からずハマってしまいました。
生のPHPで書いている時には割り当てなんてあまり気にしなかったような気がします。私が忘れているだけ?
この辺りはもう少し柔軟に書けるような記事も見つけたので、今後の課題とします。
(もしこの記事に誤りがありましたらご教授いただけると幸いです。)