CakePHP3でレコードを保存(追加、更新、Insert、Update)する複数の方法を紹介
2020/08/24
CakePHP3でレコードを保存(追加(Insert)、更新(Update))する方法を紹介
レコードを追加、更新する方法の前提
CakePHP3でレコードを追加する方法、更新する方法について解説します。
レコードを追加(Insert)する方法も、更新(Update)する方法も 1つではありませんので、複数の方法を紹介しつつ、活用方法や特徴を解説します。
なお、この記事は、下記の記事である「CSVファイルを読み込んでテーブルを更新する」と言う処理の実験をベースに記事を書いています。
CakePHP3でモデルなしフォームからCSVをアップロードしレコードを更新する方法解説
そのため、一般的な「画面から入力された情報を保存する」という処理とは「foreach」文などが若干異なります。
その辺りについては、解説を加えてはいますが。
今回のプログラムで更新対象としているテーブルは以下の内容です。
1 2 3 4 5 6 7 8 9 10 11 12 |
CREATE TABLE bookmarks2 ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, title VARCHAR(50), description TEXT, url TEXT, flag_code INT, flag INT, created DATETIME, modified DATETIME, FOREIGN KEY user_key (user_id) REFERENCES users(id) ); |
https://book.cakephp.org/3/ja/tutorials-and-examples/bookmarks/intro.html
このテーブルは、上記の CookBookの「ブックマークチュートリアル」で利用したテーブルを流用したテーブルになります。
(記事の「bookmarks」テーブルを元に「flag_code」「flag」のカラムを追加した「bookmarks2」テーブルを作成しています。)
また、「ブックマークチュートリアル」に関連した記事としては以下の記事もあります。
CakePHP3のfriendsofcake/searchでブックマークチュートリアルのタグ検索を実装
CSVファイルを更新する Controllerの処理
CSVアップロードの処理全体は「CakePHP3でモデルなしフォームからCSVをアップロードしレコードを更新する方法解説」を読んでいただくとして、そのうちの Controller(/src/Controller/HogesController.php)の処理の基本形は以下のものとなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php namespace App\Controller; use App\Controller\AppController; use App\Form\HogeCsvForm; // モデルなしフォームロード use Cake\Filesystem\Folder; // Folderユーティリティロード class HogesController extends AppController { public function initialize() { parent::initialize(); $this->loadComponent("Csv"); // CSVコンポートロード } public function csv() { $hogesCsv = new HogeCsvForm(); if ($this->request->is(["patch", "post", "put"])) { if ($hogesCsv ->execute($this->request->getData())) { // ファイルコピー $path = ROOT . DS . "uploadfiles" . DS . "tmp"; $folder = new Folder(); $folder->create($path); $file = $path . DS . sha1(uniqid(mt_rand(), true)) . ".csv"; if (move_uploaded_file($this->request->getData("csv.tmp_name"), $file)) { chmod($file, 0644); // CSVデータ取込 $csvData = $this->Csv->getCsv($file,"utf8",true); // データ整形 $data = []; foreach ($csvData as $vol) { $data[] = [ "user_id" => 1, "title" => $vol[1], "flag" => $vol[0], "flag_code" => $vol[0], ]; } // データ登録 $this->loadModel("Bookmarks2"); $entities = $this->Bookmarks2->newEntities($data); $result = $this->Bookmarks2->saveMany($entities); unlink($file); $this->Flash->success("登録しました。"); }else{ $this->Flash->error("登録に失敗しました。"); } } else { $this->Flash->error("登録に失敗しました。"); } } $this->set("hogesCsv", $hogesCsv); } } |
この Controllerの処理の中で、
32~40行目で保存するレコードを整形し、
43~45行目で整形したレコードをテーブルに保存しています。
まず始めに、CSVとして取得したレコード全体で配列を作成し、「saveMany()」メソッドで一括して保存する処理となっています。
この記事では、このレコードを整形し、テーブルに保存する処理のバリエーションを解説します。
ちなみに、この処理では CSVファイルを一括して処理するため速い処理が期待できます。
ですが、カラム数にもよりますが、数百件程度であれば全く問題がなくても、万単位のレコードがある場合は、メモリオーバーや処理時間オーバーなどの不具合が発生する場合も考えられるため、CSVの件数が多くなる可能性がある場合は、件数をカウントして処理を分岐するなどの対応が必要だろうと思います。
テーブルの接続方法のバリエーション
まず始めに、テーブルに接続する方法のバリエーションです。
先の例では下記の記述で「Bookmarks2」のモデルをロードしています。
1 |
$this->loadModel("Bookmarks2"); |
ロードしたモデルを下記のように使用します。
1 |
$this->Bookmarks2->newEntities($data); |
モデルを呼び出すほかの方法としては、下記の記述方法もあります。
まず、Controllerの use句を書いている箇所に下記を追記します。
1 |
use Cake\ORM\TableRegistry; |
また、先の「$this->loadModel("Bookmarks2");
」の記述を、下記の記述に変更します。
1 |
$this->Bookmarks2 = TableRegistry::getTableLocator()->get('Bookmarks2'); |
「save()」メソッドで 1件ずつ追加(Insert処理・その2)
最初のサンプルでは「saveMany()」メソッドを使用して、一括保存する処理でした。
その2では「save()」メソッドを使用して 1件ずつ保存する処理を紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// レコードを新規登録するとき・その2 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { $bookmarks2 = $this->Bookmarks2->newEntity(); $bookmarks2->user_id = 1; $bookmarks2->title = $vol[1]; $bookmarks2->flag = $vol[0]; $bookmarks2->flag_code = $vol[0]; $this->Bookmarks2->save($bookmarks2); // if($this->Bookmarks2->save($bookmarks2)){ // $id = $bookmarks2->id; // } else { // エラー処理 // } } |
この処理が、Bakeを実行して自動的に作成される処理に一番近い処理になります。
foreach文で配列から 1件ずつレコードを取り出して、saveメソッドで保存しています。
また、コメントにしていますが、11行目を 13~17行目と置き換えることで、正常に保存できた場合は保存したレコードの IDを取得することや、エラーが発生した場合はそのエラーの対処することなど、1件ずつ細かな管理をすることを容易にすることができます。
CSVの件数が少ない場合はこのような処理方法も使用できるでしょう。
基本的な処理が CSVファイルを取得することを想定していますので、配列「$csvData」を foreach文で処理しています。
ですが、入力フォームから取得した値 1件を受け取って保存する場合は以下の記述になります。
1 2 3 4 |
$this->loadModel("Bookmarks2"); $data = $this->request->getData(); $bookmarks2 = $this->Bookmarks2->newEntity($data); $this->Bookmarks2->save($bookmarks2); |
より Bakeされた際に作成される処理に近づけるならば下記の記述になります。
1 2 3 4 5 |
$this->loadModel("Bookmarks2"); $bookmarks2 = $this->Bookmarks2->newEntity(); $data = $this->request->getData() $bookmarks2 = $this->Bookmarks2->patchEntity($bookmarks2,$data); $this->Bookmarks2->save($bookmarks2) |
「save()」メソッドで 1件ずつ更新(Update処理・その1)
ここからは、レコードを更新する(Update)処理について解説します。
「get()」メソッドを使い IDからレコードを取得(その1-1)
CSVから取得する項目に IDが含まれ、IDを利用してレコードを更新する場合は下記のように記述することが出来ます。
1 2 3 4 5 6 7 8 9 10 11 |
// レコードを更新するとき・その1-1 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { if( $this->Bookmarks2->exists(["id"=>$vol[0]]) ){ $bookmarks2 = $this->Bookmarks2->get($vol[0]); $bookmarks2->flag = 4; $bookmarks2->title = $vol[1]; $result = $this->Bookmarks2->save($bookmarks2); } } |
getメソッドは、レコードの IDを指定してレコードを取得する処理ですが、指定した IDがない場合はエラーとなります。
テーブルに保存されていない IDが入力される可能性がある場合は、取得できない場合に例外処理(エラー処理)とする方法もありますが、ここではまず始めに existsメソッドでレコードの存在チェックをする方法を採用しています。
「find()」メソッドを使い条件に合致するレコードを取得する(その1-2)
IDがない可能性を考慮する場合や、取得する CSVに IDが含まれない、もしくは、プライマリーキーとなる ID以外をキーとしてレコードを更新したい場合は下記のように findメソッドを使用する方法が有効です。
1 2 3 4 5 6 7 8 9 10 11 12 |
// レコードを更新するとき・その1-2 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { $bookmarks2 = $this->Bookmarks2->find()->where(["flag_code"=>$vol[0]])->first(); if($bookmarks2){ $bookmarks2->flag = 1; $bookmarks2->title = $vol[1]; $result = $this->Bookmarks2->save($bookmarks2); } } |
「find()」メソッドを使い条件に合致するレコードを取得(その1-3)
基本的には「その1-2」と変わりませんが、5行目と 7行目を 1行にまとめて下記のように記述することも出来ます。
1 2 3 4 5 6 7 8 9 10 11 |
// レコードを更新するとき・その1-3 // 「その1-2」の処理を作るくらいならこちらのほうがマシ $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { if($bookmarks2 = $this->Bookmarks2->find()->where(["flag_code"=>$vol[0]])->first()){ $bookmarks2->flag = 1; $bookmarks2->title = $vol[1]; $result = $this->Bookmarks2->save($bookmarks2); } } |
「save()」メソッドで 1件ずつ更新(Update処理・その2)
あまり代わり映えはしませんが、保存するレコードを配列で整形する方法もあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// レコードを更新するとき・その2 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { $bookmarks2 = $this->Bookmarks2->find()->where(["flag_code"=>$vol[0]])->first(); if($bookmarks2){ $data = [ "flag" => 4, "title" => $vol[1], ]; $entity = $this->Bookmarks2->patchEntity($bookmarks2, $data); $this->Bookmarks2->save($entity); } } |
「Query()」メソッドで複数件のレコードを更新(Update処理・その3)
続いて、複数のレコードをまとめて更新する方法です。
更新のその1、その2は、CSVの 1行でテーブルの 1レコードを更新する更新方法です。
「その1-1」は IDをキーに更新を行いますので 1レコードしかありませんが、「その1-2」「その1-3」「その2」に関しては、「["flag_code"=>$vol[0]]
」の条件に該当するレコードが複数件ある場合も存在します。
処理上は、firstメソッドを用いていますので、そのうちの最初の 1件のみ更新する処理になっていますが。
このように、条件に該当するレコードが複数ある場合は、複数件まとめて更新する方法が以下の処理になります。
Queryメソッドを使って Update文を生成する方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// レコードを更新するとき・その3 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { $data = [ "flag" => 4, "title" => $vol[1], ]; $query = $this->Bookmarks2->query(); $query->update() ->set($data) ->where(["flag_code" => $vol[0]]) ->execute(); } |
「UpdateAll()」メソッドで複数件のレコードを更新(Update処理・その4)
前項と同じく、条件に該当するレコードが複数件ある場合は、下記のように UpdateAllメソッドを使う方法もあります。
1 2 3 4 5 6 7 8 9 10 11 12 |
// レコードを更新するとき・その4 $this->loadModel("Bookmarks2"); foreach ($csvData as $vol) { $data = [ "flag" => 4, "title" => $vol[1], ]; $condition = [ "flag_code" => $vol[0] ]; $this->Bookmarks2->updateAll($data,$condition); } |
CakePHP3の関連記事
CakePHP4のCSS、JavaScript、画像のブラウザへのキャッシュをコントロールするCakePHP3でレコードを保存(追加、更新、Insert、Update)する複数の方法を紹介
CakePHP3でモデルなしフォームからCSVをアップロードしレコードを更新する方法解説
CakePHP3でPHP Simple HTML DOM Parserを使ってスクレイピングする方法
CakePHP3のInsert On Duplicate Key Update(upsert)構文を解説・バルク処理も
CakePHP3の1対多での連携を中間テーブルを使った多対多の連携に変更するときの手順
CakePHP3でデフォルトのソート条件を設定してユーザの選択肢たソート条件を有効にする方法
CakePHP3で Ajaxを使う方法の解説。3.6以降対応。Successとthenの両方を解説。
CakePHP3でパンくずの指定は HTMLヘルパーを使って指定する方法を解説
CakePHP3にOGPをfetch、asignを利用してテンプレートごとに指定する方法を解説
その他の「CakePHP3」に関する記事一覧
GoogleAdwords
GoogleAdwords
この記事が参考になったと思いましたらソーシャルメディアで共有していただけると嬉しいです!
関連記事
-
CakePHP 2.3 Search Pluginで検索処理 その3入力エリア一つで複数の項目を同時に検索する方法
CakePHPの検索プラグイン Search Pluginの検索処理の中で入力エリア一つで複数の項目を同時に検索する方法を解説。
-
CakePHP4でcontrollerで実行したバリデーションエラーをmodelのにマージする方法
CakePHP4でcontrollerで実行したバリデーションの結果をModelのバリデーション結果に追加するメソッド「setError()」「setErrors()」の使い方の解説。
-
CakePHP3のfriendsofcake/searchでブックマークチュートリアルのタグ検索を実装
CakePHP3のCookbookにあるブックマークチュートリアル。ここで紹介されているタグで検索する処理を検索プラグイン「friendsofcake/search」で実現する方法を解説しました。
-
CakePHP4のCSS、JavaScript、画像のブラウザへのキャッシュをコントロールする
CakePHP4、CakePHP3でブラウザにキャッシュさせる設定の解説。CSS、JavaScript、画像をブラウザにキャッシュさせるのか、定期的にリロードする設定にするのかの設定が可能。
-
CakePHP3にデザインテンプレートBootstrapを導入する方法・friendsofcake/bootstrap-ui使用
CakePHP3にプラグイン「friendsofcake/bootstrap-ui」、デザインテンプレート「Bootstrap」を設置する手順を解説。Bootstrapの簡単な使い方やデフォルトのデザインとの混在方法なども解説。
-
CakePHP4のCakeDC/Usersの Usersへの接続、バリデーションのカスタマイズ方法解説
CakeDC謹製Usersプラグインの紹介。CakePHP4で使う場合のUsersのカスタマイズとして入力項目のバリデーションの変更を、プラグインのファイルは触らずオーバーライドにより実装する方法を解説する。
-
CakePHP3のメール送信の処理・テンプレート使用・添付ファイル送信も解説
CakePHP3からメールを送信する方法解説。基本的な記述方法を基にして、テンプレートを使う方法、ファイルを添付する方法へと拡張しながら解説。
-
CakePHP4で公開側と管理側のデザインテンプレートを分ける方法・setLayout()
CakePHP4でデフォルトのレイアウトファイル「default.php」は管理側に使用し、これとは別のデザインを公開側のページに設定したい、を実装する方法を解説。
-
CakePHP3のCakeDC/Usersのログイン後のリダイレクトとユーザ権限管理の設定解説
CakeDC謹製Usersプラグインの紹介。ログイン認証後にリダイレクトする先の設定方法についての解説と実運用するために必要なコツを解説。便利な仕組みも仕様の理解があって初めてうまく使いこなせる。
-
CakePHP 2.3で PDFを作成する方法を調査「mpdf」「TCPDF」「FPDF」
CakePHPで PDFを編集、出力するには「mpdf」「TCPDF」「FPDF」といったプラグインがあり、使い勝手を比較検討しました。TCPDFが一番良さそうでした。