Open2
Laravelに関するメモ
モデル周り
マイグレーションファイルを変更してマイグレーションをかけるときは、これがいるっぽいのでいれておく。
$ composer require doctrine/dbal
UUID 導入
参考:Laravel Eloquent の主キー(ID)を UUID に変更する
インストール
Laravel のバージョンに合わせて、対応しているバージョンを入れる。
$ composer require goldspecdigital/laravel-eloquent-uuid:^7.0
モデルファイルの継承元を変更
User モデルの場合
use Illuminate\Foundation\Auth\User as Authenticatable;
↓
use GoldSpecDigital\LaravelEloquentUUID\Foundation\Auth\User as Authenticatable;
User モデル以外のモデルの場合
生成コマンドを使うと、この継承元にしてくれる
$ php artisan uuid:make:model [モデル名]
use GoldSpecDigital\LaravelEloquentUUID\Database\Eloquent\Model;
timestamp 型を datetime 型へ
参考:Laravel MySQL timestamp型 2038年問題に対応する
マイグレーションファイルの timestamp の部分を置き換えるだけ
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
↓
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->dateTime('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->dateTime('created_at')->nullable();
$table->dateTime('updated_at')->nullable();
});
主キー名変更
参考:
Laravel のモデルのデフォルト主キー名はid
であるため、変更するには以下の手順を行う。
- マイグレーションファイルのカラム名を変更する
- モデルファイルで、主キー名をオーバーライドする
protected $primaryKey = 'user_id';
リレーション設定
モデルファイルのリレーション定義では、キー名を自動推測するようになっているので、変更した主キーを使ってもらうよう指定しておく。
Users(1):Memos(多)の場合の例
User.php
/**
* リレーション - Memos
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function memos()
{
// デフォルトでは、自分自身のモデル名の「スネークケース」に _id のサフィックスをつけた名前と想定
return $this->hasMany('App\Memo', 'user_id');
}
Memo.php
/**
* リレーション - users
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
// デフォルトでは、リレーションメソッド名に _id のサフィックスを付けた名前をデフォルトの外部キー名と想定
// 親のモデルの主キーが id でない、もしくは子のモデルと違ったカラムで紐付けたい場合は、親テーブルのカスタムキー名を第3引数に指定
return $this->belongsTo('App\User', 'user_id', 'user_id');
}
フォームリクエスト
リクエストのバリデーションはフォームリクエストで行うとよい。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MemoRequest 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 [
'title' => 'present|string|max:100',
'content' => 'present|string|max:65535'
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
// カスタムバリデーションメッセージ
return [
'title.string' => 'メモタイトルは文字列である必要があります。',
'title.max' => 'メモタイトルの最大文字数100を超えています。',
'content.string' => 'メモ内容は文字列である必要があります。',
'content.max' => 'メモ内容の最大文字数65535を超えています。'
];
}
}
リクエストパラメータを加工
Laravel 5.4 から ConvertEmptyStringsToNull ミドルウェアにより、リクエストパラメータの値に空文字があると null に変換される仕様になっている。
これは空文字のままデータ保存をしたい時に不都合なので、フォームリクエストで再度変換を書けるとよい。
/**
* Get data to be validated from the request.
*
* @return array
*/
public function validationData()
{
リクエストパラメータを加工したい時に記述
$all = parent::validationData();
// 値がnullの時は空文字を入れる
if (is_null($all['title'])) {
$all['title'] = '';
}
if (is_null($all['content'])) {
$all['content'] = '';
}
return $all;
}
加工したパラメータを使うには、コントローラーでこのフォームリクエストを適用し、$request->validated()
で使用する。
直接 $request->title
などとすると、加工前のパラメータになるので注意。
/**
* (ログインユーザで)メモ作成API
*
* @param MemoRequest $request
* @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory
*/
public function create(MemoRequest $request)
{
// パラメータは直接使うのでなく、フォームリクエストで加工したものを使う
$memo = new Memo($request->validated());
/** @var App\User $user */
$user = Auth::user();
$user->memos()->save($memo);
return response($memo, 201);
}
フォームリクエストのテスト
dataProvider を使えば、ひとつのテストメソッドで複数のパターンのテストを実行できる。
<?php
namespace Tests\Unit\Requests;
use App\Http\Requests\MemoRequest;
use Illuminate\Support\Facades\Validator;
// ファザードを使用するのでLaravelが提供するTestCaseを継承する
use Tests\TestCase;
class MemoRequestTest extends TestCase
{
/**
* MemoRequestのバリデーションテスト
*
* @param array 項目名の配列
* @param array 値の配列
* @param boolean 期待値(true:バリデーションNG、false:バリデーションOK)
* @dataProvider dataMemoExample
*/
public function testMemoRequest(array $keys, array $values, bool $expect)
{
$dataList = array_combine($keys, $values);
$request = new MemoRequest();
$rules = $request->rules();
$validator = Validator::make($dataList, $rules);
// バリデーションに引っ掛かるとtrue
$result = $validator->fails();
$this->assertEquals($expect, $result);
}
public function dataMemoExample()
{
return [
'OK' => [
['title', 'content'],
['テスト タイトル', 'テスト 内容'],
false
],
'OK:空文字' => [
['title', 'content'],
['', ''],
false
],
'NG:titleが存在しない' => [
['content'],
['テスト 内容'],
true
],
'NG:titleが文字列でない' => [
['title', 'content'],
[123, 'テスト 内容'],
true
],
'NG:titleがnull' => [
['title', 'content'],
[null, 'テスト 内容'],
true
],
'OK:titleが100文字以内' => [
['title', 'content'],
[str_repeat('a', 100), 'テスト 内容'],
false
],
'NG:titleが100文字より多い' => [
['title', 'content'],
[str_repeat('a', 101), 'テスト 内容'],
true
],
'NG:contentが存在しない' => [
['title'],
['テスト タイトル'],
true
],
'NG:contentが文字列でない' => [
['title', 'content'],
['テスト タイトル', 123],
true
],
'NG:contentがnull' => [
['title', 'content'],
['テスト タイトル', null],
true
],
'OK:contentが65535文字以内' => [
['title', 'content'],
['テスト タイトル', str_repeat('a', 65535)],
false
],
'NG:contentが65535文字より多い' => [
['title', 'content'],
['テスト タイトル', str_repeat('a', 65536)],
true
],
];
}
}