[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
Laravelではエラーメッセージの表示などで馴染み深いflashセッションの機能がありますが
この機能は「次回のリクエストまでセッションを維持する」という言葉の通り動作します。
もし次回のリクエストでflashセッションを引き続き維持したい時は、
遷移やアクセスの可能性を十分に理解・整理したうえで、
Input::flash();
や
$request->session()->reflash();
をうまく使いましょう。
繰り返しますが、この言葉の通りなので使う時はちゃんとわかって使う事が大事だったりします。
はまりがちな罠 - 1
APIなどの非同期通信で消えてしまう。はまりがちな罠 - 2
public以下に設置したcss, js, imgの404アクセス(laravelのハンドラで処理)で消えてしまう。そんなことがあるのか的な罠(これが言いたかった)
barryvdh/laravel-debugbar を噛ませている場合、エラーハンドリング時は暗黙にreflashされるので.envのdebug=falseで本番デプロイするまで上記の罠 - 2が表面化しない!!!
debug=trueの時に導入される便利ツールたちは便利ではありますが
こういう思いがけない事例もあるのだと感心しました。
flash自体は良い仕組みですので使いこなしていきたいところですが、
ちゃんとケースバイケースで用途を考えて実装する必要がありますね。
エラーハンドラ内で404であればreflashするとか、
APIのミドルウェアで特定の条件でのみreflashするとか、
安直に思いつく手はあれど、十分にケースを網羅した上で正解を決めていきたいところです。
この調査のためにまたソースをたくさん読んだので、良い勉強になりました。
Laravel小ネタ。
Laravelで用意されている便利な配列操作オブジェクト「collection」
これを使用している中で、人によってはハマりそうなケースを発見しました。
こんなことがあったのです
// EloquentのCollectionをゲット。
$persons = App\Person::all();
// あるカラムだけのCollectionにして、重複を排除してみよう。
$nicknames = $persons->pluck('nickname')->unique();
( ゜Д゜)あれー
[Symfony\Component\Debug\Exception\FatalThrowableError] Fatal error: Call to a member function getKey() on integer
普通に重複を排除した値を取ってくれるなら問題ないんですが
エラーで落ちてしまいました。
とりあえず結論から
Eloquentから得たCollectionは
「Illuminate\Database\Eloquent\Collection」
を使っている。
実はこれ、Illuminate\Support\Collection と似ているが、ちょっと違う。
このオブジェクトは所属アイテムがEloquentのModelオブジェクトである事を想定しているようだ!
よって、pluck()等でEloquentオブジェクト以外が所属している状態になった場合は
使うメソッドによっては、エラーになるぞ!
エラーの解決策
Modelのcollectionとして使用しないのなら、混同しないこと。
明示的にIlluminate\Support\Collection オブジェクトにして扱えばよろしい。
$nicknames = collect($persons->pluck('nickname'))->unique();
ちなみに
エラーが発生した場所のコードはこちら。
/**
* Return only unique items from the collection.
*
* @param string|callable|null $key
* @return static
*/
public function unique($key = null)
{
if (! is_null($key)) {
return parent::unique($key);
}
return new static(array_values($this->getDictionary()));
}
からの、
/**
* Get a dictionary keyed by primary keys.
*
* @param \ArrayAccess|array $items
* @return array
*/
public function getDictionary($items = null)
{
$items = is_null($items) ? $this->items : $items;
$dictionary = [];
foreach ($items as $value) {
// ここ!!$itemにgetKey()が実装されている事が前提になっている。
// getKeyは、Eloquent Modelのプライマリキーのカラム名を返すメソッド。
$dictionary[$value->getKey()] = $value;
}
return $dictionary;
}
Laravelはソースがきれいなのですぐ分かります。
なるほどね、と思ったのでした。
LaraveのValidationは大変使いやすいです。
業務システムを作るとき、わりと使いやすいけど調べてもわかりにくいルールを 備忘録として書いておきます。
unique
よくある重複チェック。DB内を検索してくれます。
すべての検索条件が=(イコール)でチェックできるならこんな書き方ができます。
<?php
// 必須ですよ|自分のid以外のcompany_idとshop_idが同じ商品の中でユニークじゃないとだめですよ
$rules = [
'item_code' => 'required|unique:items,item_code,'.$my_id.',id,company_id,'.$my_company_id.',shop_id,',$my_shop_id;
];
実際は下記の構成になっています。
unique:{テーブル名},{対象カラム名},{除外条件の値},{←のカラム名(id等)},{where カラム1},{←の値},{and カラム2},{←の値}, ...
required_if
一緒に渡されたパラメータの値を見て、特定の条件だったら必須とする。
<?php
// 数字ですよ|typeがstudentの時だけ必須ですよ
$rules = [
'age' => 'numeric|required_if:type,student';
];
before (after)
日付に適用できるフィルタ。以前、以後を表現できる。
<
?php
// 必須ですよ|日付ですよ|今日より前である必要がありますよ
$rules = [
'birth_date' => 'required|date|before:'.date('Y-m-d');
];
// 一緒に渡されたto_dateより前である必要がありますよ
$rules = [
'from_date' => 'date|before:to_date';
];
ちなみに、たいていValidationのルール定義はFormRequest内のrules()メソッドで返すのが定石ですが
特殊な入力フローを使っていて、Controller等でValidationを行いたい時もあると思います。
そんな時はこんな感じでもかけます。
<?php
// 手動でValidatorを作るよ
$validator = Validator::make($params, [
// rules...
]);
// Validateしてエラーがあったら
if ($validator->fails()) {
// エラーメッセージと入力された値を持って入力画面に戻るよ
return redirect()->route('edit_page_route_name')->withErrors($validator)->withInput();
}
Laravelのすごいところは、たいていの事を網羅してくれているところだと思います。
Laravelの実装機能の範囲内で対応できない要件は、完全に特殊な入力をする必要があるか
そもそもの考え方や設計がおかしいかのどちらかなので、
書いていく中で「行き詰まる事」が「状況整理のタイミング」として
考えず書き進めるような事を避ける癖が付いていくのが嬉しいですね。