CakePHP2 を勉強してきて、1つのテーブルにデータを入れるには Model::save を使えばよいし、やってみた記録を残してくださっている方々のページも見つかって心強いのです。
けれどもじゃあ、表示したページのフォームにいろいろデータを入力して、登録!とかクリックしまして処理を走らせたときに、2つのテーブルに1度に綺麗にスマートにデータベースに Insert する方法は、意外と見つかりません。
ですので、やってみました。記録を残します。
ポイント
- アソシエーション設定済みの複数モデルのテーブルに1度にデータを保存するには、Model::saveAssociated を使う。
- ちなみに、Model::saveMany はひとつのモデルに複数行を登録するときに使う、らしい(未検証)。
- ちなみに、Model::saveAll は Model::saveMany または、Model::saveAssociated が実行される。慣れないうちは明確に saveMany、saveAssociated を使い分けたほうが無難かも。
データベース
こんな感じに users テーブル、ninjas テーブル、geisyas テーブルでやってみました。一応実際にやりそうなシチュエーションを想定しておりまして、
- users にログイン情報のユーザ名、パスワード
- ninjas、geisyas にニンジャ、ゲイシャの各パラメータ情報
を入れます。
つまり、ニンジャ、ゲイシャが同じシステムにユーザ登録するけれども、入力する情報はそれぞれ異なる、シチュエーションですね。
そして SQL はこちらです。データベースを作るところからはじめましょう。
# データベース、ユーザ、の作成。パスワードの設定 GRANT ALL PRIVILEGES ON sampledb10.* TO sampleuser10@localhost IDENTIFIED BY 'samplepassword10'; FLUSH PRIVILEGES; CREATE DATABASE sampledb10 CHARACTER SET utf8; # テーブル作成 CREATE TABLE users ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password CHAR(40) NOT NULL, created DATETIME, modified DATETIME ); CREATE TABLE ninjas ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id int(11) NOT NULL, jitsu text, tool text, created DATETIME, modified DATETIME ); CREATE TABLE geisyas ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id int(11) NOT NULL, oiroke text, face text, created DATETIME, modified DATETIME );
ソース
まずは一覧です。
- app/Controller/UsersController.php
index アクション
add_user_ninja アクション
add_user_geisya アクション
_addAssociated 関数 - app/Model/User.php
アソシエーション User hasOne Geisya
アソシエーション User hasOne Ninja - app/View/Users/index.ctp
登録データの一覧を見るページ - app/View/Users/add_user_ninja.ctp
ニンジャ登録用ページ - app/View/Users/add_user_geisya.ctp
ゲイシャ登録用ページ
app/Controller/UsersController.php
<?php App::uses('AppController', 'Controller'); /** * Users Controller * * @property User $User */ class UsersController extends AppController { /** * index method * * @return void */ public function index() { $this->User->recursive = 0; $this->set('users', $this->paginate()); } /** * add_user_ninja method * * @return void */ public function add_user_ninja() { $this->_addAssociated(); } /** * add_user_geisya method * * @return void */ public function add_user_geisya() { $this->_addAssociated(); } /** * _addAssociated method * * @return void */ private function _addAssociated() { if ($this->request->is('post')) { $this->User->create(); $this->log($this->request->data, 'debug'); $result = $this->User->saveAssociated($this->request->data); $this->log($result, 'debug'); if ($result) { $this->Session->setFlash(__('The user has been saved')); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(__('The user could not be saved. Please, try again.')); } } } }
$this
->User->saveAssociated(
$this
->request->data); を使っているだけですね。コントローラでは特別なことはしていません。
むしろ注目したいのは、ニンジャの登録も、ゲイシャの登録も、まったく同じロジック、プログラムを使っているという点です。コーディング量が減って、開発が楽になりますね♪
app/Model/User.php
<?php App::uses('AppModel', 'Model'); /** * User Model * * @property Geisya $Geisya * @property Ninja $Ninja */ class User extends AppModel { /** * Validation rules * * @var array */ public $validate = array( 'username' => array( 'notempty' => array( 'rule' => array('notempty'), //'message' => 'Your custom message here', //'allowEmpty' => false, //'required' => false, //'last' => false, // Stop validation after this rule //'on' => 'create', // Limit validation to 'create' or 'update' operations ), 'isUnique' => array( 'rule' => array('isUnique'), ), ), 'password' => array( 'notempty' => array( 'rule' => array('notempty'), //'message' => 'Your custom message here', //'allowEmpty' => false, //'required' => false, //'last' => false, // Stop validation after this rule //'on' => 'create', // Limit validation to 'create' or 'update' operations ), ), ); //The Associations below have been created with all possible keys, those that are not needed can be removed /** * hasOne associations * * @var array */ public $hasOne = array( 'Geisya' => array( 'className' => 'Geisya', 'foreignKey' => 'user_id', 'conditions' => '', 'fields' => '', 'order' => '', 'dependent' => false ), 'Ninja' => array( 'className' => 'Ninja', 'foreignKey' => 'user_id', 'conditions' => '', 'fields' => '', 'order' => '', 'dependent' => false ) ); }
コントローラで saveAssociated を使いましたが、どのテーブルに同時に保存するかはモデルのアソシエーション設定で決まります。$hasOne フィールドですね。
app/View/Users/index.ctp
<div class="users index"> <h2><?php echo __('Users'); ?></h2> <table cellpadding="0" cellspacing="0"> <tr> <th><?php echo $this->Paginator->sort('User.id' ,'ユーザID'); ?></th> <th><?php echo $this->Paginator->sort('User.username' ,'ユーザネーム'); ?></th> <th><?php echo $this->Paginator->sort('User.password' ,'ユーザパスワード'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.id' ,'ニンジャID'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.user_id' ,'ニンジャ・ユーザID'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.jitsu' ,'ニンジャジツ'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.tool' ,'ニンジャ便利ツール'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.id' ,'ゲイシャID'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.user_id' ,'ゲイシャ・ユーザID'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.oiroke' ,'ゲイシャオイロケ'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.face' ,'ゲイシャ化粧'); ?></th> <th><?php echo $this->Paginator->sort('User.created'); ?></th> <th><?php echo $this->Paginator->sort('User.modified'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.created'); ?></th> <th><?php echo $this->Paginator->sort('Ninja.modified'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.created'); ?></th> <th><?php echo $this->Paginator->sort('Geisya.modified'); ?></th> </tr> <?php foreach ($users as $user): ?> <tr> <td><?php echo h($user['User']['id']); ?> </td> <td><?php echo h($user['User']['username']); ?> </td> <td><?php echo h($user['User']['password']); ?> </td> <td><?php echo h($user['Ninja']['id']); ?> </td> <td><?php echo h($user['Ninja']['user_id']); ?> </td> <td><?php echo h($user['Ninja']['jitsu']); ?> </td> <td><?php echo h($user['Ninja']['tool']); ?> </td> <td><?php echo h($user['Geisya']['id']); ?> </td> <td><?php echo h($user['Geisya']['user_id']); ?> </td> <td><?php echo h($user['Geisya']['oiroke']); ?> </td> <td><?php echo h($user['Geisya']['face']); ?> </td> <td><?php echo h($user['User']['created']); ?> </td> <td><?php echo h($user['User']['modified']); ?> </td> <td><?php echo h($user['Ninja']['created']); ?> </td> <td><?php echo h($user['Ninja']['modified']); ?> </td> <td><?php echo h($user['Geisya']['created']); ?> </td> <td><?php echo h($user['Geisya']['modified']); ?> </td> </tr> <?php endforeach; ?> </table> <p> <?php echo $this->Paginator->counter(array( 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') )); ?> </p> <div class="paging"> <?php echo $this->Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); echo $this->Paginator->numbers(array('separator' => '')); echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); ?> </div> </div> <div class="actions"> <h3><?php echo __('Actions!イヨォー!!!'); ?></h3> <ul> <li><?php echo $this->Html->link(__('New Geisya'), array('controller' => 'users', 'action' => 'add_user_geisya')); ?> </li> <li><?php echo $this->Html->link(__('New Ninja'), array('controller' => 'users', 'action' => 'add_user_ninja')); ?> </li> </ul> </div>
app/View/Users/add_user_ninja.ctp
<div class="users form"> <?php echo $this->Form->create('User', array('action' => 'add_user_ninja')); ?> <fieldset> <legend><?php echo __('ログイン用 User レコードと Ninja の内容を同時に登録'); ?></legend> <?php echo $this->Form->input('User.username', array('label' => 'User モデルの username フィールド')); echo $this->Form->input('User.password', array('label' => 'User モデルの password フィールド')); echo $this->Form->input('Ninja.jitsu', array('label' => 'Ninja モデルの jitsu フィールド')); echo $this->Form->input('Ninja.tool', array('label' => 'Ninja モデルの tool フィールド')); ?> </fieldset> <?php echo $this->Form->end(__('登録')); ?> </div>
ビューでの特徴は、モデルとそのフィールドを、複数モデル分記述していることでしょうか、次のビューでも同じですが、User.username と Ninja.jitsu などと複数モデルのフィールドを書いています。
app/View/Users/add_user_geisya.ctp
<div class="users form"> <?php echo $this->Form->create('User', array('action' => 'add_user_geisya')); ?> <fieldset> <legend><?php echo __('ログイン用 User レコードと geisya の内容を同時に登録'); ?></legend> <?php echo $this->Form->input('User.username', array('label' => 'User モデルの username フィールド')); echo $this->Form->input('User.password', array('label' => 'User モデルの password フィールド')); echo $this->Form->input('Geisya.oiroke', array('label' => 'geisya モデルの oiroke フィールド')); echo $this->Form->input('Geisya.face', array('label' => 'geisya モデルの face フィールド')); ?> </fieldset> <?php echo $this->Form->end(__('登録')); ?> </div>
動き
適当に登録してみました。その結果を確認してみますと、いい感じです。
users.id と ninjas.user_id または geisyas.user_id が一致しています♪
おわりに
なんやかんやで本家のドキュメントが一番参考になりました。当たり前ですけれどもね。英語なのでとっつきにくいですけれども、CakePHP2 に慣れてきたせいか、少しずつわかるようになってきました。
「CakePHP2 で Model::saveAssociated を使って1回で複数のテーブルにデータを保存します♪」への2件の返信
[…] http://d.hatena.ne.jp/g2_girichan/20090327/1238154524 http://d.hatena.ne.jp/sutara_lumpur/20100702/1278029850 http://arkmemo.blogspot.jp/2012/10/cakephpsaveall.html https://oki2a24.com/2012/08/15/how-to-save-multiple-model-associations-at-once-with-model-saveassocia… […]
突然失礼致します。
今回、cakePHPを使った制作をしており、悩んでいるのですが、
唯一、本サイトが、私のやりたいことに似たことをしており、
参考にさせていただいております。
不躾で恐縮なのですが、
下記URLの内容でなにかアドバイス頂戴できませんでしょうか?
ご連絡お待ちしております。
宜しくお願い致します。
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13130063598