質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Laravel

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

Q&A

解決済

2回答

2896閲覧

laravel トランザクション処理 流れや方法について

creative_09

総合スコア80

Laravel

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

0グッド

2クリップ

投稿2020/03/20 04:46

Laravelにてトランザクションをしたいのですが、
処理ごとにどこでエラーなのかわかるように例外を投げる方法を知りたいです

注文テーブルに登録する際のトランザクションです
流れはカートテーブルに入っている情報で間違いがなければ注文テーブルに登録し、カートテーブルを削除という流れです
トランザクション開始
1.在庫を引く
2.在庫を引ければ注文テーブルに登録する
3.登録ができればカートテーブルの削除

コードは以下です

DB::beginTransaction(); try { //在庫を差し引く foreach ($cartproducts as $cartproduct) { Product::where('id', $cartproduct->product_id)->update([ 'total_stock' => $cartproduct->total_stock - $cartproduct->quantity, ]); echo "在庫を" . $cartproduct->quantity . "差し引きました"; }                    //登録処理 foreach ($cartproducts as $cartproduct) { $order->create([            登録処理 ]); }          //カート削除処理                  DB::commit(); } catch (\Exception $e) { DB::rollback(); return redirect('cart')->with('flash_message', 'エラーがありました。もう一度やり直してください'); }

質問1つ目の例外を投げる方法ですが、
在庫を差し引けたかどうかはどうやって判断するんでしょうか?
もう一度DBを参照するなどでしょうか?
エラーが発生した場合にわかるということでしょうか?
この在庫を差し引く部分でエラーが発生した場合のthrowの書き方がわかりません。
在庫エラーや削除エラーをキャッチしたいです
処理ごとにthrowを入れる方法を教えて下さい
よろしくおねがいします

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

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

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

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

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

m.ts10806

2020/03/20 04:55

要件定義してください。 それぞれ何を以て「エラー」とするか、要件定義してください。 「どうやってするか」ではなく「何をするか」「なぜするか」をまず詰めてください。 でないと「どうやって」も決まりません。
creative_09

2020/03/20 05:15

すみません。 在庫がなんかしらのエラーでひくことができなかった場合でお願いします 想定外のエラーが起きた。そんな場合にエラーが出て、表面上では在庫エラーと表示するためにキャッチしたいです
m.ts10806

2020/03/20 05:17

異常系とするなら、「なんかしら」「想定外の」をある程度考えて範囲を決めておかないとテストできません。そういった意味でも 「どうやってするか」ではなく「何をするか」「なぜするか」をまず詰めてください。 でないと「どうやって」も決まりません。 要件不明瞭な状態では答えようがないのが実際です。
creative_09

2020/03/20 05:29

ありがとうございます。 突拍子のないことをきいているようで申し訳ございません。。。 例えば文字列が不明でphpでエラーが発生。 在庫部分でエラーが出たよということで、 それをメッセージと共にわかりたいです
m.ts10806

2020/03/20 05:45

「文字列が不明」が私には不明です。すみません。 より具体的に決める必要があります。いわゆる「異常系設計」というやつで、 「なんとなくやってみたなー」だとアプリケーションが成り立たない部分になるので、 こればかりは決めないと誰からもアドバイスはもらえないのではないでしょうか。 「一般的にはどう設計するのか」と問うのでしたらLaravel要素は排除し「セキュリティー」など 適切なタグを付与して設計について質問すると良いのですけど、実際のところどこまでお考えでしょうか?
creative_09

2020/03/20 06:16

どう説明したらいいのかがわからないのでほんおに申し訳ないです。 例えば在庫を100ひこうとした場合に、在庫は50しかなかった場合、DB側ではマイナスは受け付けない そういった場合にエラーがでます。 トランザクションではこういったエラーでも拾うのでしょうか? 拾った場合、在庫でエラーがでましたよと投げたいんです。
creative_09

2020/03/20 06:23

カートにいれている間はまだ在庫があった。しかし、確認画面で確定をクリックすると 売り切れになっていた場合、引ける在庫がなくてエラーになってしまった。 この場合、在庫でエラーを投げます そういった場合に在庫でエラーが出たのか、登録時にエラーが出たのかを分かるようにメッセージを投げたいんです。 エラーが出た箇所でその内容をthrowする場合に 在庫でエラーがでました。 登録時にエラーが出ました とするためにthrowはどこにどうやって書けばいいのかということなんです。
guest

回答2

0

ベストアンサー

要件がザルなので、それも含めて回答します。

補足部分に書かれている

在庫部分でエラーが出たよ

についてですが、普通に考えれば、在庫以上の注文をした時、エラーにしなければなりませんよね?
であれば、注文を立てる前に、チェックしなきゃいけません。
ソースコードでその処理が書かれていませんので、追加しましょう。

ここで想定される初心者の疑問として、「カートに入れる前にチェックしてるんだからいらないと思う。」となりそうですが、WEBでは複数のユーザーが同時にアクセスすることを想定しなければいけませんので、ここで、注文を確定する直前にチェックします。当然これも、トランザクション の中で行わなければなりません。

例えば、「在庫が3個しかないのに、5個注文しようとした」場合、

  • 注文を3個のみ確定し、処理を進める
  • 注文を全てキャンセルにする

これをどうするのかを決定しておくことが、いわゆる「仕様」です。「仕様」が不明瞭だと、コードが書けません。(質問されても回答できない)

「追記依頼でぶっきらぼうに『仕様は?』と聞かれるのも、質問者がどこまで考えているのかを確認する方法の一つではある」ので、別に責められているわけではありません。気にするなとは言わないけど、留意して欲しい。

いずれにしても、トランザクション内で、ロールバックに処理を持って行きたい時、どうすれば良いのかを調べればいい。

https://readouble.com/laravel/6.x/ja/database.html#database-transactions

イメージ説明

「トランザクション クロージャーの中で例外を投げる」と書かれている。

php

1 2DB::transaction(function () { 3 // 在庫チェック 4 if (在庫 < 注文数) { 5 throw new Exception('在庫がありません'); 6 } 7 // 在庫を引く 8 9 // 注文を立てる 10});

これで、基本的な要件を満たすことでしょう。

投稿2020/03/20 06:13

編集2020/03/20 06:28
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

creative_09

2020/03/20 07:02

ありがとうございます。 ありがとうございます。 トランザクションに入るまでにすでにバリデーション、 dbに登録できる値など、設定しており、 在庫についても、在庫以下など、引けない在庫などの場合はトランザクションの処理までもいけない状態にしておりますが、 トランザクション内でもやっておきたくて、 おっしゃる通り同時アクセスなどの想定です タイミングでのエラー 注文数以外のエラー、データベースを更新できなかったのが、どっちのエラーなのかをわかるthrowの書き方はありますか? これもポストされてくるものではないので、エラーはないとおもいますが、なにかのメンテナンスなどで狂ってしまった場合など。 コントローラーで直接dbの値を読み込みその値を渡しています
退会済みユーザー

退会済みユーザー

2020/03/20 07:04 編集

「注文数以外のエラー」を具体的に「仕様」としてお伝え願います。 あと、文章をちゃんと「推敲」してください。
creative_09

2020/03/20 07:13

すみません。 データベースが更新できないなにかしらのエラーは sql文が通らなくなった。 など、データベース側のエラーです。 それが在庫でおこったのか、 オーダー登録でおこったのか、 などをわかるように例外を投げたいです。 これは仕様になってるんでしょうか
退会済みユーザー

退会済みユーザー

2020/03/20 07:22

それはロールバックの意味分かっていないのかな? データベース側のエラーって、何を想定しています? SQLの構文ミス? →運用に入ってそんなのが見つかることってないよね? デッドロック? → なあ、起こりうるけど、SQLのログ見りゃわかるよね?
退会済みユーザー

退会済みユーザー

2020/03/20 07:27

例外についても、ユーザーに「見せるべきエラー」と「見せる必要のないエラー」をちゃんと区別しなきゃいけないですよ。DBのエラーなんて、「見せる必要のないエラー」の際たるものです。
creative_09

2020/03/20 07:28

ロールバックは一連のデータベース変更においてすべてがセットで変更されるイメージです。 どれかでエラーがでるともとに戻す。 という認識です sqlがなにかしらのエラーを出してしまったとき、 それが、在庫なのか、オーダーなのかをわけて例外を出すことはできないのでしょうか
退会済みユーザー

退会済みユーザー

2020/03/20 07:30 編集

そのようにコードを書けばいいことですよ。 回答に書いたサンプルコード見てもわかりませんか?
creative_09

2020/03/20 07:30

はい。データベースのエラーの内容はみせるつもりはないです。 ただ、在庫エラー、オーダーエラーと表示するだけでいいんです。 あまり深く追わないほうがよいのでしょうか?
退会済みユーザー

退会済みユーザー

2020/03/20 07:31

回答に書いたサンプルコード見てもわかりませんか?
退会済みユーザー

退会済みユーザー

2020/03/20 07:41 編集

「在庫を1減らすクエリを書いて実行したのにデータベースが3引いたかもしれない」とまで疑う必要は通常ないし、そこまでチェックはしない。 そもそもそんな信用できないデータベースなら使わないですよね?
creative_09

2020/03/20 07:41

サンプルにあるような通常のチェックは行っています。 自分はありえない想定まで含んでいるような気がしてきました。 ながながと付き合って頂いて感謝します。 まともな質問を心がけます。 ありがとうございました
退会済みユーザー

退会済みユーザー

2020/03/20 07:42

そうですね。なんか、ありえない想定をして勝手に用心が行き過ぎている印象ですね。
creative_09

2020/03/20 12:37

でも、事前チェックありきで、 トランザクション内でもまたチェックしながらできるということ。 むしろそれをしたほうがいいというところ。 また、明確なthrowを出すやり方などは勉強になりました こじらせてすみませんでした。 ありがとうございます。
guest

0

回答を削除しました。

投稿2020/03/20 06:59

編集2020/03/20 07:05
creative_09

総合スコア80

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問