🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

Q&A

解決済

2回答

3249閲覧

[laravel]1度のPOSTで複数のテーブルにデータを登録するには?

marutto

総合スコア32

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

0グッド

1クリップ

投稿2019/12/02 02:46

編集2019/12/02 09:43

現在laravelでタイピングゲームを作っています。

2つだったテーブルを更新・追加して6テーブルに分けようと考えています。

以前はPOSTされたデータを1つのテーブルに登録すれば良かったのですが、
今度は3つのテーブルに登録する必要があります。
この時にコントローラー内の登録アクションをどういった処理にすればいいのか?が分からないのです。

drillsにデータを登録するためには紐づくcateogryのIDが決定してないといけません。

POSTされた複数の値の中からcategory_nameの部分をcategoryテーブルに登録するのはいいとして、
その後、drillsテーブルに他のテーブルと関連するidをどうやって入力すればいいのか?

problemテーブルについても主テーブルとなるdrillsのIDをどうやって取得、登録すればいいのでしょうか?

なお、drillsテーブル、categoryテーブル、problemsテーブルのidカラムはオートインクリメント&プライマリーキーです。

以下、アプリの詳細です。

・テーブル構成
before↓
イメージ説明
・現時点のコントローラー内の登録アクション

public function store(Request $request) { $drill = new Drill; Auth::user()->drills()->save($drill->fill($request->all())); return redirect('/drills')->with('flash_message', __('Registered.')); }

・現時点の投稿フォーム

<div class="form-group row"> <label for="title" class="col-md-4 col-form-label text-md-right">{{ __('Title') }}</label> <div class="col-md-6"> <input id="title" type="text" class="form-control @error('title') is-invalid @enderror" name="title" value="{{ old('title') }}" autocomplete="title" autofocus> @error('title') <span class="invalid-feedback" role="alert"> <strong>{{ __("$message") }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="category_name" class="col-md-4 col-form-label text-md-right">{{ __('Category') }}</label> <div class="col-md-6"> <input id="category_name" type="text" class="form-control @error('category_name') is-invalid @enderror" name="name" value="{{ old('category_name') }}" autocomplete="category_name" autofocus> @error('category_name') <span class="invalid-feedback" role="alert"> <strong>{{ __("$message") }}</strong> </span> @enderror </div> </div> @for ($i = 1; $i <= 10; $i++) <div class="form-group row"> <label for="problem{{$i - 1}}" class="col-md-4 col-form-label text-md-right">{{ __('Problem').$i }}</label> <div class="col-md-6"> <input id="problem{{$i - 1}}" type="text" class="form-control @error('problem'.($i - 1)) is-invalid @enderror" name="problem{{$i - 1}}" value="{{ old('problem'.($i - 1)) }}" autocomplete="problem{{$i - 1}}" autofocus> @error('problem'.($i - 1)) <span class="invalid-feedback" role="alert"> <strong>{{ __("$message") }}</strong> </span> @enderror </div> </div> @endfor

after↓
イメージ説明

public function store(Request $request) { $category = new Category; $category->name = $request->name; $category->save(); $drill = new Drill; $drill->title = $request->title; $drill->user_id = Auth::user()->id; $drill->category_id = $category->id; $drill->save(); $problem = new Problem; $problem->drill_id = $drill->id; $problem->problem0 = $request->problem0; $problem->problem1 = $request->problem1; $problem->problem2 = $request->problem2; $problem->problem3 = $request->problem3; $problem->problem4 = $request->problem4; $problem->problem5 = $request->problem5; $problem->problem6 = $request->problem6; $problem->problem7 = $request->problem7; $problem->problem8 = $request->problem8; $problem->problem9 = $request->problem9; $problem->save(); // $drill = new Drill; // Auth::user()->drills()->save($drill->fill($request->all())); return redirect('/drills')->with('flash_message', __('Registered.')); }

アドバイスを元に、上記で登録できました!
categoryをアドバイス通りに実装しなかったのは、categoryのデータを他のユーザーと共有するのを考えた場合、勝手にnameの値を変えられてしまうからです。
$guardedを使って変更不可にし、追ってアドバイスを通りの実装にしようと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2019/12/02 03:01

IDはどのように決定するのでしょうか。
marutto

2019/12/02 03:05

修正依頼ありがとうございます! categoryテーブルのID、problemsテーブルのIDはオートインクリメント、プライマリーキー設定です。 つまり、登録順序に応じて連番で登録されるようにします。 この点を質問内容にも追記させていただきます。
hentaiman

2019/12/02 05:30

今回の質問に限って「実務に立つバックエンドエンジニア樣」に限定している理由はなんでしょうか?言わずとも汲み取って欲しいような、質問だけでは説明し切れていない要件があるのでしょうか?
m.ts10806

2019/12/02 06:19

あまり業務従事者を求めると作業依頼になり得ますね。質問になってません。
marutto

2019/12/02 06:22

バックエンド言語PHPのFWに関する質問ということで、特に他意はなかったですが・・・。 不愉快に思われたでしたらすみません。 該当箇所について訂正します。
hentaiman

2019/12/02 06:32

毎回それを書くような質問者なら回答する気失せるので無視しますが、書いてあったのが今回の質問だけだったので、上手く説明の出来ない察して欲しい要件でもあるのかと気になっただけです 既についている回答の内容で済みそうですね
guest

回答2

0

ベストアンサー

drillsにデータを登録するためには紐づくcateogryのIDが決定してないといけません。

DBに同名のカテゴリが保存されていているか調べるwherefirst
見つからない場合、新たにcreateorfill/save
firstOrCreateでも良い

POSTされた複数の値の中からcategory_nameの部分をcategoryテーブルに登録するのはいいとして、

その後、drillsテーブルに他のテーブルと関連するidをどうやって入力すればいいのか?

saveしたらsaveしたモデルのidが入っているのでそれを使う。
先に必要な方からsaveしていく。
どうしても都合が悪ければnullabeにして先にdrillsをsaveできるようにする
(今回は関係ないが、循環する場合など)

problemテーブルについても主テーブルとなるdrillsのIDをどうやって取得、登録すればいいのでしょうか?

drillをsaveしたらidが取れるはずです

まとめると、順番的には

  • categoryの存在チェック
  • なければcategory登録
  • drillをsave
  • problemをsave

という順番です。

余談ですが
関連したモデルの挿入/更新の項
を理解すると、よりかっこよく保存できます

投稿2019/12/02 05:01

mikkame

総合スコア5036

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

marutto

2019/12/02 06:26

回答ありがとうございます! laravelのDB操作に扱うメソッドを詳細に記述して下さっていて、実装する見通しが立ちました。 実装できたら、改めてお礼を伝えさせていただきます!
marutto

2019/12/02 09:39

たった今DBへの登録処理ができました。 コード的に改善点が多々あるので、提示いただいたページのポリモーフィックなリレーションに調整していきます。 一応、他の方の参考になるようにコントローラーの処理も載せさせていただきます。 アドバイスがなかったら1~2日は詰まっていたと思うので、本当に助かりました! ありがとうございました!
guest

0

確実なのはauto incrementではなくシステム側でidを払い出すことです。lastInsertIdといった機能は確かに用意されていますが、PHPマニュアルにあるように一貫性のある値を返さない可能性があります。

投稿2019/12/02 06:30

編集2019/12/04 12:38
m.ts10806

総合スコア80875

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mikkame

2019/12/04 13:30

> lastInsertIdといった機能は確かに用意されていますが、PHPマニュアルにあるように一貫性のある値を返さない可能性があります。 こちらの根拠を教えてください。 例示されているリンクはPHPのマニュアルではないので、php.net内で該当する記事を教えてください。 こちらで調べている限り、少なくともmysqlの場合 https://www.php.net/manual/ja/mysqli.insert-id.php > AUTO_INCREMENT 属性を 持つカラムがあるテーブル上でのクエリ (通常は INSERT) により生成された ID を返します。 と書かれています。 また、AutoIncrementは必ずPKに設定されるものなので、重複登録も起こりえません。
m.ts10806

2019/12/04 14:02

コメントありがとうございます。 例えばですが、 https://www.php.net/manual/ja/pdo.lastinsertid.php >注意: このメソッドは、異なる PDO ドライバ間で意味のあるもしくは 一貫性のある結果を返さないかも知れません。 構成しているデータベースが自動インクリメントフィールド、 もしくはシーケンスの概念をサポートしていないかも知れないためです。 確かに「返さないかも知れない」を「返さない可能性がある」と言い換えてはいますが、意味合いとしては同じという認識です。
m.ts10806

2019/12/04 14:04

質問を見る限りではDBが限定されている様子はないのでpdoを根拠として出しています。
mikkame

2019/12/04 14:36 編集

少なくともLaravelが想定しているMySQL/PostgreSQL/SQLite/SQL Serverについては全てオートインクリメントないし、シーケンスの概念をサポートしていますし 自動採番の利用はLaravelが想定している事だと思います。 オートインクリメントの使用を控えるような回答はいささか不適切かと思います (そもそも保存方法を聞いているのにオートインクリメントどうこうは関係なかったですね・・・)
m.ts10806

2019/12/04 21:22

了解です。 「確実に払い出すには」という観点ではあります。 私もLaravel自体の業務経験は薄いので意見、指摘もらえて良かったです。 最近普通に回答したつもりが無言マイナス多くて。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問