CakePHP5入門【WebAPI編⑤】管理用ページ②
A子
まずは「AdminController.php」の中で「Numbers」クラスを使えるようにしよう
C菜
|
class AdminController extends AppController
{ private $Numbers; public function initialize(): void { parent::initialize(); $this->middleware(new HttpBasicAuthMiddleware()); $this->loadComponent('SixSevenWeek'); $this->Numbers = $this->fetchTable('Numbers'); } |
赤字部分が追加した箇所です~
A子
|
//データベース登録
$numbers = $this->Numbers->newEmptyEntity(); $numbers->lottery_time = $lottery_time; $numbers->lottery_date_dt = $lottery_date_dt; $numbers->lottery_date_str = $lottery_date_str; $numbers->lottery_date_year = $lottery_date_year; $numbers->lottery_date_month = $lottery_date_month; $numbers->lottery_date_day = $lottery_date_day; $numbers->lottery_week_int = $lottery_week_int; $numbers->lottery_week_str1 = $lottery_week_str1; $numbers->lottery_week_str2 = $lottery_week_str2; $numbers->lottery_rokuyo_int = $lottery_rokuyo_int; $numbers->lottery_rokuyo_str = $lottery_rokuyo_str; $numbers->num3_str = $num3_str; $numbers->num3_int = $num3_int; $numbers->num3_place1 = $num3_place1; $numbers->num3_place10 = $num3_place10; $numbers->num3_place100 = $num3_place100; $numbers->num4_str = $num4_str; $numbers->num4_int = $num4_int; $numbers->num4_place1 = $num4_place1; $numbers->num4_place10 = $num4_place10; $numbers->num4_place100 = $num4_place100; $numbers->num4_place1000 = $num4_place1000; if (!$this->Numbers->save($numbers)) { $this->Flash->error('ナンバーズファイルのデータベース登録に失敗しました。'); return $this->redirect(['action' => 'index']); } |
これをforeachループ内の末尾に書くよ
B美
エラーになったとき、いきなりリダイレクトしちゃダメよ
ループからbreakしたあと、しかるべき処理を行わないと…
C菜
例えば、10件目の処理でエラーになったときは、最初の1件目から9件目までを元の状態に戻さないといけないのでしょうか~?
(要するに「全て無かったことにする」ということです~)
B美
抽選の各回は独立していて、前後の関係性は無いからね
でも、せっかくだから(練習のために)「トランザクション処理」を実装してみましょう
(途中でエラーになったら、全て「無かったことにする」の)
A子
それって、「トランザクションテーブル」とは違うの?
(【CakePHP5基礎編⑦】を参照)
B美
ここでの「トランザクション処理」というのは、「分離できない一連の処理」のことよ
C菜
B美
【口座】テーブル
| 口座番号 | 預金額 |
|---|---|
| 1234 | 100000 |
| 5678 | 30000 |
というテーブルがあったとして、1234番の口座から5678番の口座へ50,000円の振込処理を行うとしましょう
A子
【口座】テーブル
| 口座番号 | 預金額 |
|---|---|
| 1234 | 50000 |
| 5678 | 80000 |
C菜
B美
①1234番の口座の預金額を100000から50000に更新
②5678番の口座の預金額を30000から80000に更新
この二つって、どちらか一方だけ成功して、一方は失敗って状態は許されるかしら?
A子
①が成功、②が失敗の場合
【口座】テーブル
| 口座番号 | 預金額 |
|---|---|
| 1234 | 50000 |
| 5678 | 30000 |
になっちゃうし、
①が失敗、②が成功の場合
【口座】テーブル
| 口座番号 | 預金額 |
|---|---|
| 1234 | 100000 |
| 5678 | 80000 |
…って状態になるね
C菜
たしかにこれらは「分離できない」処理ですね~
B美
何千件ものデータベース処理をループを回して行う場合、トランザクション処理にするほうが無難でしょうね
(処理速度も速くなるし…)
A子
速くなる?
B美
例えば、3件の処理を行うとして…
【更新処理①→①を確定→更新処理②→②を確定→更新処理③→③を確定】
よりも
【更新処理①→更新処理②→更新処理③→①から③をまとめて確定】
のほうが速くなるってわけ
C菜
B美
1.トランザクションスタート
2.あれやこれやの処理
3.全て成功すればコミット、一つでも失敗すればロールバック
…って感じね
(要は、今までのデータベース処理に1と3が加わっただけ)
A子
「結果にコミットする」
C菜
(一部、伏字)
B美
データベースで使うコミットは「委託する」なんかの意味になるのかな?
(よく知らん)
まぁ、トランザクション型データベースでは、一般的に「更新を確定する」って意味で使ってるけど…
C菜
B美
あ、「更新(UPDATE)」って言ってるけど、「挿入(INSERT)」や「削除(DELETE)」も含んでるからね
A子
たしかに便利そうな機能だね
ちなみに、どんなデータベースソフトにも備わってる機能なの?
B美
(知らんけど…)
なにしろマイクロソフトの「Access」ですらトランザクション型なんだから…
C菜
B美
| $this->Numbers->getConnection()->begin(); |
| $this->Numbers->save($numbers) |
| $this->Numbers->getConnection()->commit(); |
| $this->Numbers->getConnection()->rollback(); |
A子
超・簡単じゃん
|
//トランザクションスタート
$this->Numbers->getConnection()->begin(); $error_flag = false; foreach ($data as $row) { ・・・ if (!$this->Numbers->save($numbers)) { $error_flag = true; break; } } if ($error_flag == false) { //コミット $this->Numbers->getConnection()->commit(); } else { //ロールバック $this->Numbers->getConnection()->rollback(); } |
これでどうよ
C菜
|
if ($error_flag == false) {
$this->Flash->success($cnt.'件の処理を行いました。'); } else { $this->Flash->error('エラーが発生しました。'); } |
ついでに、前回書いたデバッグ用のフラッシュメッセージについても全てコメントアウトしておきますね~
B美
それじゃ、テスト実行してみなさい
C菜
えいっ!
A子
んじゃ、以下のSQL文を実行して確認してみよう
(本当は「select * from numbers」としたかったけど、表示が崩れちゃうんだよね(苦笑))
|
select id,lottery_time,lottery_date_dt,lottery_date_str from numbers where lottery_time between 6925 and 6929;
select lottery_date_year,lottery_date_month,lottery_date_day from numbers where lottery_time between 6925 and 6929; select lottery_week_int,lottery_week_str1,lottery_week_str2 from numbers where lottery_time between 6925 and 6929; select lottery_rokuyo_int,lottery_rokuyo_str from numbers where lottery_time between 6925 and 6929; select num3_str,num3_int,num3_place1,num3_place10,num3_place100 from numbers where lottery_time between 6925 and 6929; select num4_str,num4_int,num4_place1,num4_place10,num4_place100,num4_place1000 from numbers where lottery_time between 6925 and 6929; |
C菜
カレンダーを使って「曜日」や「六曜」についても検証してみましたけど、全く問題ありませんでした~
B美
これを読み込ませる場合、どうする?
A子
(全部で13,868件になっちゃう)
うむむ
…ってことは、すでに登録済みの分(6929件目まで)については、データベースへの登録処理をスキップしないとダメか…
C菜
| select max(lottery_time) from numbers; |
A子
B美
なので、今回は直接SQL文を実行するコードを書こうと思います
A子
C菜
B美
直接SQL文を実行するほうが楽なのよ
てか、ちゃんと覚えてるでしょうね?
A子
データベース操作を学んだときに出てきたような(汗)
(【CakePHP5基礎編⑦】を参照)
C菜
|
use Cake\Datasource\ConnectionManager;
・・・ $connection = ConnectionManager::get('default'); $sql = "select max(lottery_time) from numbers"; $results = $connection->execute($sql)->fetchAll('assoc'); |
B美
「fetchAll()」ではなく、「fetchColumn()」のほうが良いかもね
|
$connection = ConnectionManager::get('default');
$time_max = $connection->execute('select max(lottery_time) from numbers')->fetchColumn(0); |
これによって変数「$time_max」には項目「lottery_time」の最大値が入るんだけど、もしも空のテーブル(ゼロレコード)だったら「null」になるから気を付けてね
A子
| if ($time_max != null && $lottery_time <= $time_max) continue; |
C菜
きちんと最後の10件だけ追加されました~
↓
B美
あ、そうだ
リファクタリングの一環なんだけどさ
今回のようにテーブルの項目数が多い場合、違う書き方があるの
C菜
(【CakePHP5実用編⑬】を参照)
A子
B美
|
$numbers = $this->Numbers->newEmptyEntity();
$numbers->lottery_time = $lottery_time; $numbers->lottery_date_dt = $lottery_date_dt; $numbers->lottery_date_str = $lottery_date_str; ・・・ |
ではなく
|
$numbers = $this->Numbers->newEmptyEntity();
$numbersData = [ 'lottery_time' => $lottery_time, 'lottery_date_dt' => $lottery_date_dt, 'lottery_date_str' => $lottery_date_str, ・・・ ]; $this->Numbers->patchEntity($numbers, $numbersData); |
…って感じで連想配列にデータを格納してから、モデル側へ一気に渡すの
A子
|
//データベース登録
$numbers = $this->Numbers->newEmptyEntity(); $numbersData = [ 'lottery_time' => $lottery_time, 'lottery_date_dt' => $lottery_date_dt, 'lottery_date_str' => $lottery_date_str, 'lottery_date_year' => $lottery_date_year, 'lottery_date_month' => $lottery_date_month, 'lottery_date_day' => $lottery_date_day, 'lottery_week_int' => $lottery_week_int, 'lottery_week_str1' => $lottery_week_str1, 'lottery_week_str2' => $lottery_week_str2, 'lottery_rokuyo_int' => $lottery_rokuyo_int, 'lottery_rokuyo_str' => $lottery_rokuyo_str, 'num3_str' => $num3_str, 'num3_int' => $num3_int, 'num3_place1' => $num3_place1, 'num3_place10' => $num3_place10, 'num3_place100' => $num3_place100, 'num4_str' => $num4_str, 'num4_int' => $num4_int, 'num4_place1' => $num4_place1, 'num4_place10' => $num4_place10, 'num4_place100' => $num4_place100, 'num4_place1000' => $num4_place1000, ]; $this->Numbers->patchEntity($numbers, $numbersData); |
かなり面倒くさかった…(苦笑)
C菜
B美
実は「patchEntity」メソッドって、格納する値の妥当性チェック(データ型が合ってるか…等)も(勝手に)行ってるの
んで、変数「$lottery_date_dt」の中身って「'2026-01-01'」って感じになってるわよね
これって「datetime」型じゃなくて、(おそらくは)「date」型と評価されちゃうのよ
(だって時刻が入ってないし…)
ちなみに、以前のやり方のときにうまくいっていたのは、時刻を(これまた勝手に)補完してくれてたから…(苦笑)
A子
B美
| use Cake\I18n\DateTime; |
連想配列に値を格納する際、こんな感じでdatetime型にしてあげれば良いわ
| 'lottery_date_dt' => new DateTime($lottery_date_dt.' 00:00:00'), |
A子
|
//データベース登録
$numbers = $this->Numbers->newEmptyEntity(); $numbersData = [ 'lottery_time' => $lottery_time, 'lottery_date_dt' => new DateTime($lottery_date_dt.' 00:00:00'), 'lottery_date_str' => $lottery_date_str, 'lottery_date_year' => $lottery_date_year, 'lottery_date_month' => $lottery_date_month, 'lottery_date_day' => $lottery_date_day, 'lottery_week_int' => $lottery_week_int, 'lottery_week_str1' => $lottery_week_str1, 'lottery_week_str2' => $lottery_week_str2, 'lottery_rokuyo_int' => $lottery_rokuyo_int, 'lottery_rokuyo_str' => $lottery_rokuyo_str, 'num3_str' => $num3_str, 'num3_int' => $num3_int, 'num3_place1' => $num3_place1, 'num3_place10' => $num3_place10, 'num3_place100' => $num3_place100, 'num4_str' => $num4_str, 'num4_int' => $num4_int, 'num4_place1' => $num4_place1, 'num4_place10' => $num4_place10, 'num4_place100' => $num4_place100, 'num4_place1000' => $num4_place1000, ]; $this->Numbers->patchEntity($numbers, $numbersData); |
ぱぱっと修正したよ
B美
A子
B美
(主キーである「id」の値が大きくなっちゃうけど…)
C菜
まずは「ナンバーズ3&4_1-6929.xlsx」というExcelファイルからです~
↓
A子
次は、さっきのファイルに10件追記された「ナンバーズ3&4_1-6939.xlsx」だね
↓
C菜
B美
次回はいよいよ本命のWebAPI設計に入りましょう
A子
C菜


