CakePHPで単一の外部キーを持たないモデルで作業する
-
22-07-2019 - |
質問
外部(および完全に変更不可能)のソースからインポートされたデータを持つアプリケーションで作業しています。問題を説明するために、いくつかの表を単純化します。以下は、主キーを持つテーブルです。
invoices (doc_number, date_printed, batch_number)
headers (doc_number, date_printed, batch_number)
deliveries (doc_number, date_printed, batch_number)
transactions (doc_number, date_printed, batch_number, line_number)
messages (doc_number, date_printed, batch_number, line_number)
つまり、請求書、ヘッダー、および配達物には1対1の関係があることがわかります。請求書からトランザクションへ、および請求書からメッセージへは、1対多です。
これらのテーブルを自分のデータベースにインポートする際、既存の主キーを一意のキーに変更し、各テーブルにauto_incrementingフィールド( id
)を追加しました。
Cakeは、実際には複合キーをまったく処理しないため、Cakeでのリレーションシップのセットアップが問題になります。次のような1対1の関係を実現できました。
class Invoice extends AppModel {
public $name = "Invoice"
, $hasOne = array(
"Header" => array(
'foreignKey' => false,
'conditions' => array(
"Invoice.doc_number = Header.doc_number",
"Invoice.date_printed = Header.date_printed",
"Invoice.batch_number = Header.batch_number"
)
)
)
;
}
そして、これは機能します。1対1の関係は、大きなLEFT JOINで一度にクエリされるためです。 1対多の関係(たとえば、請求書とトランザクション)で同じメソッドを試みると、Cakeは2つのクエリを実行するため、死にます。1つ目は一致するすべての請求書を検索し、2つ目は対応する外国最初のクエリの結果と一致するキー:(実行しようとしている単純化されたクエリ)
SELECT `Transaction`.* FROM `transactions` AS `Transaction`
WHERE `Invoice`.`doc_number` = `Transaction`.`doc_number`
AND `Invoice`.`date_printed` = `Transaction`.`date_printed`
AND `Invoice`.`batch_number` = `Transaction`.`batch_number`
おわかりのように、請求書に結合されていないため、クエリは終了します。
これをどのように機能させるかについてのアイデアはありますか?
解決
最悪のシナリオでは、 hasMany関係のfinderQueryパラメーターを使用できます。クエリを完全にオーバーライドできます。
関連するデータフィールドを1つに結合する一意のidフィールドを追加することもできます。データベースがトリガーをサポートしている場合、UPDATEステートメントは既存のデータを修正し、TRIGGERは新しいレコードが追加または変更されたときに更新するようにセットアップできます。
編集:他のすべての複雑さでは、hasMany関係をすべてスキップして、個別のfind()でトランザクションレコードをフェッチする方がよい場合があります。非常に多くの場所でそれを行う必要がある場合は、常に次のような請求書モデルの関数でラップすることができます。
<?php
class Invoice extends AppModel() {
...
function getInvoice($conditions) {
$invoice = $this->find('first', compact('conditions'));
$conditions = array(
'Transaction.doc_number' => $invoice['Invoice']['doc_number'],
'Transaction.date_printed' => $invoice['Invoice']['date_printed'],
'Transaction.batch_number' => $invoice['Invoice']['batch_number']);
$invoice['transactions'] = $this->Transaction->find('all', compact('conditions'));
return $invoice;
}
}
?>
モデルでそのような関数を使用する場合、$ this-&gt; Transactionが定義されるように、直接使用しない場合でも、InvoiceにはTransactionとの定義済みの関係が必要であることを忘れないでください。
また、hasMany関係のようにデータが返されるようにする場合は、常にforeachループを追加して、そのように再結合することができます。
更新 Cake Bakeryには SimpleResultsの動作他のテーブルからこれらの結果を通常のhasManyアソシエーションの形式で簡単に返すことができます。