プログラミング言語や環境設定を中心としたパソコン関連の技術メモです。
主にシステム開発中に調べたことをメモしています。TIPS的な位置付けで、気が向いたときにちまちま更新していきます。
CakePHP1.3系、CakePHPで複合主キーのテーブルを扱う方法。
CakePHPで作られているシステムがありましてね。
そのシステムに新しい機能を追加しているのですが、
既存の別システムのテーブルを参照する必要がでてきました。
このテーブルが複合主キーのテーブルだったのです。

複合主キーのテーブルってのは、例えば「先生テーブル」ってな名前で
主キーが「学校ID」と「先生ID」みたいなテーブルね。

ん(--?
複合主キーのテーブルってCakePHPでどーやって扱うの(--?

と思ったのでえっちらおっちら調べてみたのですが、

CakePHPは複合主キーをサポートしません。

って書いてありました(--;
2.x系だったけど多分1.3系も一緒でしょ。。


……○ね!(#゚д゚)



……○にやがれ!(#゚д゚)


検索系はquery()メソッドでSQL直打ちすりゃーいーやと割り切ったけど、
更新系は流石にSQL組みたくないな~(-A-;)

だからフレームワーク嫌いなんだよ、とぶちぶち愚痴りながら調べていたら
なんか素敵なことやってる方がいらっしゃいました(人´∀`)

CakePHPで複合主キーのテーブルを更新するためのモデルを
作ってくださってる方がいらっしゃいましたよ。
こんなのφ(*´ェ`*)

CakePHPで複合主キーのテーブルを更新するためのモデル(抽象クラス)


これを参考に……と言うかほぼ丸パクリしてみたのですが、
私の環境(CakePHP1.3系+SQLServer2000)では
何故か実行結果が常にfalseになっちゃうのですよね。
データはちゃんと投入されているのに。

仕方ないのでその部分を自前でちまちま直して、
ついでにsave()メソッドを追加して完成\(--)/

最終的なコードはこんな感じっすφ(--)

■app/models/abs_composite_key_model.php
 ※2012-10-16:削除メソッド「manuallyDelete()」追加
  <?php
  abstract class AbsCompositeKeyModel extends AppModel {
  
      /**
       * O/Rマッパー
       * 
       * <p>複合主キーに非対応なCakePHPのO/Rマッピングツールを使用しない。</p>
       * 
       * @var mixed
       */
      public $useTable = false;
  
      /**
       * テーブル名
       * 
       * @var string
       */
      protected $tableName = null;
  
      /**
       * テーブルのフィールド
       * 
       * @var array
       */
      protected $fieldsDefinition = array();
  
      /**
       * 複合主キー
       * 
       * @var array
       */
      protected $compositePrimaryKey =  array();
  
      /**
       * Constructor.
       *
       * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above.
       * @param string $table Name of database table to use.
       * @param string $ds DataSource connection name.
       */
      public function __construct($id = false, $table = null, $ds = null) {
          parent::__construct($id, $table, $ds);
  
          if (empty($this->tableName)) {
              trigger_error("CardRec::{$this->name} was useful table name does not exist.", E_USER_ERROR);
          }
          if (empty($this->fieldsDefinition)) {
              trigger_error("CardRec::{$this->name} was fields definition does not exist.", E_USER_ERROR);
          }
          if (empty($this->compositePrimaryKey)) {
              trigger_error("CardRec::{$this->name} was composite primary key does not exist.", E_USER_ERROR);
          }
      }
  
      /**
       * 複合主キーに対応したモデルのCREATE
       * 
       * <p>複合キーに未対応なので、O/Rマッピングツールは使用しない。<br />
       * <p>レコード追加に必要なSQLを自前で組み立てて実行する。</p>
       * 
       * @return mixed 実行結果
       */
      //public function manuallyCreate() {
      public function manuallyInsert() {
          $dataSource = $this->getDataSource();
  
          $option   = array();
          $prepared = $this->beforeSave($option);
          if (!$prepared) {
              $result = false;
          } else {
              list($bufFields, $bufValues) = $this->buildQueryToUpdateAllItems();
  
              $fullTableName = $dataSource->fullTableName($this->tableName);
              $insertFields  = implode(', ', $bufFields);
              $insertValues  = implode(', ', $bufValues);
  
              $query = array('table' => $fullTableName, 
                             'fields' => $insertFields, 
                             'values' => $insertValues);
  
              //$statement = $dataSource->renderStatement('create', $query);
  
              //$dataSource->execute($statement);
              //$result = $dataSource->hasResult();
              $result = $dataSource->execute($dataSource->renderStatement('create', $query));
          }
          $this->afterSave(true);
  
          return $result;
      }
  
      /**
       * 複合主キーに対応したモデルのUPDATE
       * 
       * <p>複合キーに未対応なので、O/Rマッピングツールは使用しない。<br />
       * <p>更新に必要なSQLを自前で組み立てて実行する。</p>
       * 
       * @return mixed 実行結果
       */
      public function manuallyUpdate() {
          $dataSource = $this->getDataSource();
  
          $result = false;
          if ($this->manuallyExists()) {
              $option   = array();
              $prepared = $this->beforeSave($option);
              if ($prepared) {
                  list($bufFields, $bufValues) = $this->buildQueryToUpdateAllItems();
  
                  $fullTableName = $dataSource->fullTableName($this->tableName);
                  $rawConditions = $this->builConditionsOfPrimaryKey();
                  $conditions    = $dataSource->conditions($rawConditions, true, true, $this);
  
                  $combined = array_combine($bufFields, $bufValues);
                  $updates  = array(); 
                  foreach ($combined as $field => $value) {
                      if ($value === null) {
                          $updates[] = "{$field} = NULL";
                      } else {
                          $updates[] = "{$field} = {$value}";
                      }
                  }
                  $updateFields = implode(', ', $updates);
  
                  $query    = array('table'      => $fullTableName,
                                    'fields'     => $updateFields,
                                    'conditions' => $conditions);
  
                  //$statement  = $dataSource->renderStatement('update', $query);
                  //$dataSource->execute($statement);
                  //$result = $dataSource->hasResult();
                  $result = $dataSource->execute($dataSource->renderStatement('update', $query));
              }
              $this->afterSave(false);
          }
  
          return $result;
      }
  
      /**
       * 複合主キーに対応したモデルのEXISTS
       * 
       * <p>複合キーに未対応なので、O/Rマッピングツールは使用しない。<br />
       * <p>問い合わせに必要なSQLを自前で組み立てて実行する。</p>
       * 
       * @return mixed 実行結果
       */
      public function manuallyExists() {
          $dataSource = $this->getDataSource();
  
          $fullTableName = $dataSource->fullTableName($this->tableName);
          $rawConditions = $this->builConditionsOfPrimaryKey();
          $conditions    = $dataSource->conditions($rawConditions, true, true, $this);
          $query         = array('fields'     => 'COUNT(*) AS numrows', 
                                 'table'      => $fullTableName,
                                 'alias'      => '',
                                 'joins'      => '',
                                 'conditions' => $conditions, 
                                 'group'      => '',
                                 'order'      => '',
                                 'limit'      => '');
          $statement     = $dataSource->renderStatement('select', $query);
          $result        = $dataSource->fetchRow($statement);
  
          $numrows = 0;
          if (isset($result[0]['numrows'])) {
              $numrows = (int)$result[0]['numrows'];
          }
          $exists = ($numrows > 0);
  
          return $exists;
      }
  
      /**
       * 複合主キーの問い合わせ条件を組み立てる
       * 
       * @return array 問い合わせ条件
       */
      private function builConditionsOfPrimaryKey() {
          foreach ($this->compositePrimaryKey as $field) {
              $$field = Set::extract("{$this->alias}.{$field}", $this->data);
          }
          $conditions = compact($this->compositePrimaryKey);
  
          return $conditions;
      }
   
      /**
       * モデルで定義したフィールドを対象に、フィールド名と値のペアを返却
       * 
       * @return array 更新系の命令を実行するためのデータセット
       */
      private function buildQueryToUpdateAllItems() {
          $dataSource = $this->getDataSource();
  
          $bufFields = array();
          $bufValues = array();
          foreach ($this->fieldsDefinition as $field => $columnType) {
              $bufFields[] = $field;
              $rawValue    = Set::extract("{$this->alias}.{$field}", $this->data);
              $bufValues[] = $dataSource->value($rawValue, $columnType, false);
          }
          $builtQuery = array($bufFields, $bufValues);
  
          return $builtQuery;
      }
  
      /**
       * 保存 2012-10-13 add
       */
      public function save($pData) {
          $retValue = false;    //戻り値
  
          //値設定
          $this->data = $pData;
  
          $retValue = ($this->manuallyExists() ? $this->manuallyUpdate() : $this->manuallyInsert());
  
          return $retValue;
      }

  //2012-10-16 add start
      /**
       * 複合主キーに対応したモデルのDELETE 2012-10-16 add
       * 
       * <p>複合キーに未対応なので、O/Rマッピングツールは使用しない。<br />
       * <p>削除に必要なSQLを自前で組み立てて実行する。</p>
       * 
       * @return mixed 実行結果
       */
      public function manuallyDelete() {
          $dataSource = $this->getDataSource();
  
          $result = false;
          if ($this->manuallyExists()) {
              $option   = array();
              $prepared = $this->beforeSave($option);
              if ($prepared) {
  
                  $fullTableName = $dataSource->fullTableName($this->tableName);
                  $rawConditions = $this->builConditionsOfPrimaryKey();
                  $conditions    = $dataSource->conditions($rawConditions, true, true, $this);
  
                  $query    = array('table'      => $fullTableName,
                                    'alias' => "",
                                    'conditions' => $conditions);
  
                  $result = $dataSource->execute($dataSource->renderStatement('delete', $query));
              }
              $this->afterSave(false);
          }
  
          return $result;
      }
//2012-10-16 add end

  }
  ?>



■app/models/hoge_model.php
  <?php
  require_once dirname(__FILE__) . '/abs_composite_key_model.php';
  
  class HogeModel extends AbsCompositeKeyModel {    //複合キー用のclassを継承
      protected $tableName = 'table01';        //テーブル名
      protected $fieldsDefinition = array(    //カラム
          'column01'   => 'integer',
          'column02'   => 'string',
          'column03' => 'integer',
          'column04' => 'integer',
          'column05' => 'string',
      );
      protected $compositePrimaryKey =  array('column01', 'column02');    //プライマリキー(複合)
  }
  ?>



■app/controllers/hoges_controller.php
  <?php
  class HogesController extends AppController {
  
      var $name = 'Hoges';
      var $uses = array(
          'HogeModel',
      );
  
      public hogera() {
        //値設定
          $this->data['HogeModel']['column01'] = 1;
          $this->data['HogeModel']['column02'] = 'a';
          $this->data['HogeModel']['column03'] = 3;
          $this->data['HogeModel']['column04'] = 4;
          $this->data['HogeModel']['column05'] = 'b';
  
        //保存
          if($this->HogeModel->save($this->data)){
              print "OK";
          }else{
              print "NG";
          }
      }
  }
  ?>


「abs_composite_key_model.php」が複合主キーのテーブル更新用モデルで
「hoge_model.php」がテーブル用のモデル、
「hoges_controller.php」で実際にモデルを使用しています。
細かいところは頑張って解析して下さいな(^^;
スポンサーリンク
 
このエントリーをはてなブックマークに追加 

category:CakePHP1.3系  thema:システム開発 - genre:コンピュータ  Posted by ササキマコト 

  関連記事