ORMのリレーション機能を使用する場合、DB側の外部キー制約はどのように設定するのがよいのか判断できず、よい方策があればお教えいただきたいと思います。
2017-06-19 追記
今回ぶつかっていた案件では、結局のところDB側でも同様の外部キー制約(連鎖削除する)を設けるようにして解決しました。
ただ、他の方はどうしているのかを知りたく、また1つの答えのようなものもないように思いますので、ORMを使った場合のDB側との使い分けについて(FuelPHPに限らず)、「自分はこうしている」というものがあれば、お教えいただけると幸いです。
現在、FuelPHPのORMを使用しています。
下記のように質問モデルと回答モデルの間にリレーションを設定していると、質問モデル(Model_Question
)のdelete
メソッドを呼び出してレコードの削除を行うと、関連する回答レコードも削除されるようになります。
class Model_Question extends \Orm\Model
{
protected static $_has_many = [
'answers' => [
'key_from' => 'question_id',
'model_to' => 'Model_Answer',
'key_to' => 'question_id',
'cascade_save' => false,
'cascade_delete' => true,
],
];
}
class Model_Answer extends \Orm\Model
{
protected static $_belongs_to = [
'question' => [
'key_from' => 'question_id',
'model_to' => 'Model_Question',
'key_to' => 'question_id',
'cascade_save' => false,
'cascade_delete' => false,
],
];
}
ここで直接SQLを使った処理を行った場合(クエリビルダなども含む)でもデータの矛盾が起こらないようにしたいと思い、DBで外部キー制約をかけるとします。ただ連鎖削除はORMがやってくれるので、DB側では下記のように削除時には何もしないようにしました。
CONSTRAINT `fk_answers` FOREIGN KEY (`question_id`) REFERENCES `questions` (`question_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
この状態で質問モデルで削除操作を行うと下記のようなエラーが発生してしまいます。MySQLの外部キー制約が連鎖削除しないように設定されているのが原因です。
Fuel\Core\Database_Exception [ 23000 ]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`sample-db`.`answers`, CONSTRAINT `fk_answers` FOREIGN KEY (`question_id`) REFERENCES `questions` (`question_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) with query: "DELETE FROM `questions` WHERE `question_id` = 'XXXX' LIMIT 1"
ORMで連鎖削除するように設定しているので、ここはすんなりいってほしいところですが、FuelPHPのORMのモデル\Orm\Model
のdelete
メソッド(参照)は、親レコードを削除してからリレーション先の子レコードを削除するようになっているため(正確に言うと親レコードの削除前に、リレーション先の子レコードの削除処理を呼び出すのですが、リレーションの処理では親が削除されていないと何もしないので、何も起こりません)、親レコードの削除時に外部キー制約に引っかかってしまい、エラーが発生しています。
このエラーが起こらないように外部キー制約で連鎖削除するようにするとうまくいくのですが、FuelPHPのORMではまず親レコードを削除するので、そのタイミングでDB側の連鎖削除が機能することになり、ORMの連鎖削除では実際には何も削除されず、なんだかORMが意味ないカンジがしてモヤモヤしています。
ORMを使う場合、外部キー制約はORMにまかせて、DB側では一切かけないようにするほうがいいのでしょうか?
それとも上記でやっているように両方で同じように設定するのがよいのでしょうか?
もしくはこれ以外でなにか調整して役割分担させるようにするのがよいのでしょうか?
FuelPHPに限らず、ORMを使うならこうしたほうがいい、自分はこうしているということがございましたら、お教えいただきたいです。
よろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
0
今回はORMの連鎖削除設定と、DBの外部キー制約での連鎖削除の両方を設定することで解決しました。
ただ、本文中にも書いていますが、なんかスッキリしない感じなので、皆様がどうしているのか、お教えいただければ幸いです。
よろしくお願いいたします。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.10%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる