LaravelでDBから取得した主キーがおかしい

前提条件
以下のような設定でテーブルを作成していた。
先に言ってしまうと、主キーが文字列なことが問題点。
Schema::create('members', function (Blueprint $table) {
$table->string('id', 50)->primary();
$table->text('name');
$table->timestamps();
$table->softDeletes();
});
データ内容
| id | name |
|---|---|
| a.ikeda | Ikeda A |
| b.ikeda | Ikeda B |
データを取得し、内容を表示すると……
dump(Member::all()->toArray());
array:2 [▼
0 => array:5 [▼
"id" => 0
"memo" => "Ikeda A"
]
1 => array:5 [▼
"id" => 0
"memo" => "Ikeda B"
]
]
idが「0」になってしまう。
原因
ドキュメントの Eloquent ORM > Eloquentの準備にある、「主キー」
さらに、Eloquentは、主キーが増分整数値であることも想定しています。これは、Eloquentが主キーを自動的に整数にキャストすることを意味します。
そのまま書いてありました。主キーを整数にキャストしてしまうため、文字列だけ入れていると「0」になってしまうのでした。
そして、解決方法も同じように書いてあります。
非インクリメントまたは非数値の主キーを使用する場合は、モデルにpublicの$incrementingプロパティを定義し、falseをセットする必要があります。
と
モデルの主キーが整数でない場合は、モデルにprotectedな$keyTypeプロパティを定義する必要があります。このプロパティの値はstringにする必要があります。
の2箇所です。
対策
app\Models\*****.phpに、以下の記述を追加することで解決します。
<?php
class Member extends Model
{
public $incrementing = false; // モデルのIDを自動増分するか
protected $keyType = 'string'; // IDのデータ型
// 以下省略
}
LaravelでFormの入力データから改行が消える件

はじめに
Laravelで、Formから入力したデータを受け取った時に奇妙な動きをしていた。
テキストエリアで、文頭や文末に入れた改行が消えてしまう。
入力値そのままにDBへ入れようとしていた時に発見。
原因の調査と、対策についてをまとめておく。
原因
まあ、ちゃんとドキュメントに記述がありました。
入力のトリムと正規化
リクエストに応じてすべての受信文字列フィールドを自動的にトリミングし、空の文字列フィールドをnullに変換します。
なるほど。
対策
A. 対象外の項目を指定する
\App\Http\Middleware\TrimStrings::classの中に、$except という変数がある。
この中に、トリミングしたくない入力枠のnameを記入する。
B. ミドルウェアを無効にする
すべての入力内容で、トリミングしないようにしてしまう方法。
laravel/app/Http/Kernel.php にある、\App\Http\Middleware\TrimStrings::classをコメントアウトしてしまう。
※空の時にnullになるのが困る場合は、\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::classも。
Laravelに認証機能を導入(Laravel Fortify)

概要
とあるレンタルサーバに、Laravelで作ったWEBアプリをリリースしようとしたら、npmが使えない事態が発生。
Laravel Jetstreamを使っていたので、構築段階でコケる状態になってしまった。
代替手段として、Laravel Fortifyを使ったので、導入方法の備忘録。
コマンド実行
composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
php artisan migrate
config
config/app.phpに、以下を追加します。
App\Providers\FortifyServiceProvider::class,
登録、ログインページの定義
app/Providers/FortifyServiceProvider.phpを編集します。
<?php
// ... 省略 ...
class FortifyServiceProvider extends ServiceProvider
{
// ... 省略 ...
public function boot()
{
// ... 省略 ...
// 登録ページは、resources/views/auth/register.blade.phpを表示する
Fortify::registerView(function () {
return view('auth.register');
});
// ログインページは、resources/views/auth/login.blade.phpを表示する
Fortify::loginView(function () {
return view('auth.login');
});
}
}
view
登録ページ
登録時に必要なのは、「名前(name)」「メールアドレス(email)」「パスワード(password)」「パスワード再入力(password_confirmation)」の4項目です。
それさえあれば良いので、以下だけでも動きます。
<form method="post" action="{{route('register')}}">
@csrf
name:<input type="text" name="name"><br />
mail:<input type="text" name="email"><br />
pass:<input type="password" name="password"><br />
pass:<input type="password" name="password_confirmation"><br />
<button type="submit">登録</button>
</form>
ログインページ
こちらは、「メールアドレス(email)」「パスワード(password)」があれば良いので、更にシンプル。
<form method="post" action="{{route('login')}}">
@csrf
mail:<input type="text" name="email"><br />
pass:<input type="password" name="password"><br />
<button type="submit">ログイン</button>
</form>
動作確認
http://(ドメイン)/registerから登録し、http://(ドメイン)/loginからログインできるかを確認。
備考:ログイン後の遷移先を指定
app/Providers/RouteServiceProvider.phpにある、HOMEの値を変更すればOK。
【Laravel】バリデーションの前に、パラメータを操作する方法

やりたいこと
Laravelのバリデーションを行う前に、入力値に対して処理を行いたい。
例:半角英数字を入力してもらいたい項目だが、全角で入力された場合でも、半角に変換して受け入れてあげたい。
Request
prepareForValidationを利用することで、実現可能です。
<?php
// ... 省略 ...
class HogeRequest extends FormRequest
{
public function rules()
{
return [
// 必須 英数字 50文字
'key' => ['required', 'alpha_num', 'max:50'],
];
}
public function prepareForValidation() {
$this->merge([
'key' => mb_convert_kana($this->key, 'a'),
]);
}
}
Laravelでブログサイトを作る 21. 管理画面・記事のプレビュー機能

目的
記事編集途中に、プレビュー表示して確認したい。
ルーティング
routes/web.php
プレビュー用のルーティングを追加します。更新と同様に、入力値をPOST送信する予定です。
// ...省略...
Route::group(['prefix' => 'admin', 'middleware' => 'auth', 'namespace' => 'App\\Http\\Controllers\\'], function() {
// ...省略...
Route::post('articlePreview', 'ArticleController@preview')->name('admin_article_preview'); // プレビュー
// ...省略...
});
テンプレート
resources/views/admin/articleEdit.blade.php
プレビューボタンを設置します。
更新する時と同じく、入力内容をsubmitします。
{{-- 省略 --}}
<form method="post">
{{-- 省略 --}}
<input type="button" id="preview" value="プレビュー" />
<input type="button" id="update" value="{{ isset($articleData->id) ? '更新' : '登録' }}" />
</form>
{{-- 省略 --}}
<script>
$(document)
.on('click', '#preview', function(){
$(this).parents('form').attr('action', '{{ route('admin_article_preview') }}').attr('target', '_blank');
$(this).parents('form').submit();
})
.on('click', '#update', function(){
$(this).parents('form').attr('action', '{{ route('admin_article_update') }}').attr('target', '');
$(this).parents('form').submit();
})
;
</script>
Controller
app/Http/Controllers/ArticleController.php
記事詳細表示の共通化
フロントの記事詳細画面と、プレビュー表示で共通化できる部分が多いので、先に対応します。
(記事の詳細表示するcontent()の一部分を抜き出す。抜き出した箇所にはreturn $this->articleView($articleData, $articleChildrenData);を記述する)
/**
* 記事1件分のページ表示(フロントの記事詳細と、管理画面のプレビュー機能から)
* @param $articleData
* @param $articleChildrenData
* @return mixed
*/
private function articleView($articleData, $articleChildrenData) {
// 親カテゴリを取得
$parentCategoryList = $this->parentCategory->orderBy('number', 'asc')->orderBy('id', 'asc')->get();
// 子カテゴリは、キーを親カテゴリIDにした連想配列にする
$data = $this->childCategory->orderBy('number', 'asc')->orderBy('id', 'asc')->get();
$childCategoryList = [];
foreach ($data as $item) {
$childCategoryList[$item->parent][] = $item;
}
// 目次対応
$articleData->body = $this->makeIndex($articleData->body);
return view('front.article', [
'articleData' => $articleData,
'articleChildrenData' => $articleChildrenData,
'parentCategoryList' => $parentCategoryList,
'childCategoryList' => $childCategoryList,
'parentNum' => $this->getParentArticleNum(),
'childNum' => $this->getChildArticleNum(),
'newArticleData' => $this->getNewArticleList(),
]);
}
プレビュー表示
内容はシンプルで、
- 入力値を取得して変数に格納する
- 本来はDBから取得する記事データを、入力値にする
- JOINで取得する親カテゴリ名を別途取得してセットする
- 記事と関連付けられている子カテゴリは、入力値から子カテゴリのリストとして取得する
という感じです。
これらの情報を、先程の共通化メソッドに渡してやれば、ページ表示できます。
/**
* プレビュー表示更新
*
* @param Request $request
* @return mixed
*/
public function preview(Request $request)
{
// 入力値取得
$id = $request['id'];
$tiitle = $request['title'];
$body = $request['body'];
$parent = $request['parent'];
$childList = $request['child'];
$open = $request['open'];
$private = $request['private'];
$description = $request['description'];
// 記事取得
$articleData = new Article();
$articleData->title = $tiitle;
$articleData->body = $body;
$articleData->parent = $parent;
$articleData->open = $open;
$articleData->private = $private;
$articleData->description = $description;
// 親カテゴリ名を保持させる
$parentData = $this->parentCategory->where('id', $parent)->first();
$articleData->name = $parentData->name;
// 記事と関連付けられている子カテゴリの情報を取得
$articleChildrenData = null;
if (is_array($childList)) {
$articleChildrenData = $this->childCategory->whereIn('id', array_keys($childList))->get();
}
return $this->articleView($articleData, $articleChildrenData);
}
