■【案2】について
2つの中間テーブル(点数テーブル、メモテーブル)を作る方法
テーブル名は、明示的に指定すればいいだけなので、
中間テーブルの命名規則を守れないこと自体は大した問題ではありません。
ただ、AとBのテーブルをつなぐ中間テーブルが2つある、というのが、あまり聞かない構成かなぁという印象です。
中間テーブルを2つ使いたいなら、Modelに中間テーブルを用いたリレーションの記述を「2つ書く」ことになります。
Modelにそのリレーションの記述さえあれば、attach, sync, detachなどの操作
を行うことは可能ですが、「リレーション記述ごとに操作を行う」ので、2個あるならそれぞれを操作することになり、手間ともいえます。
それに、同じ「テーブルAとテーブルBをつなぐ似たような役割のリレーション」なのに、
経由する中間テーブルによって、リレーションが存在したりしなかったりするわけですから、
ややこしいですよね・・?
こういう仕様は、バグの温床になり得るので、私はおすすめしません。
■【案2】よりは【案1】
user_testという1つの中間テーブル(点数カラム、メモカラムを追加)を作る方法
先に述べた理由により、
・大量のnullデータという無駄を省くために、ハイリスクで複雑な構成・ロジックにするか
・nullデータはできるけれど、リスク低めの簡素な構成・ロジックにするか
の2択であれば、「後者を選びたい」ので、2よりは1の方が断然良いかなと。
■(私的には)【案3】がベスト
中間テーブルではないのですが、2つのテーブル(点数テーブル、メモテーブル)を作る方法
(※ご注意※ popcorさんの【案3】を、そのまま推奨しているのとはちょっと違いますので、悪しからず)
提示いただいた仕様で考えると
1)テストを解いて、「結果(点数)」を登録。
2)その結果に対して、「メモ」を登録(することがある)。
フロー的には、こんな感じでしょうか?
質問したように
管理者などによって、ユーザが解くテストは、予め決められている
のであれば、テスト結果がなくても関係性が必要なので、
「ユーザー」と「テスト」を直接リレーションさせたいと考えますが、
ユーザーは、全てのテストを自由に解くことができます。制限はありません。
とのことですので、
「ユーザー」と「テスト」は「テスト結果ありき」の関係性になりますよね。
それなら、
「テスト結果」というテーブルを作って、それに対し「ユーザーテーブル」「テストテーブル」の2つの親テーブルがある
というのが、一番簡潔な構成ではないかと思います。
●ユーザーテーブル
users { id, name, ...}
●テストテーブル
tests { id, title, ...}
●テスト結果テーブル
scores { id, user_id, test_id, score, (memo,) ...}
なお、メモを別のテーブルにするのであれば、scoresにリレーションするのでも良いかも
●テスト結果メモテーブル
memos { id, score_id, memo}
テストにメモを残すような機能があるなら、
「テストをいつ解いたのか」「メモをいつしたのか」という情報も大事なのでは?
と思うのですが、中間テーブルにしてしまうと、これらも全て追加カラムとして扱わなければならず、かなり手間です。
他にも・・・
例えば、これは仕様次第の話ですが、
問題を解くサービスであれば「繰り返し解く」といった機能が備わっていることも多いと思うのですが、結果を中間テーブルに保存する形だと、1つのテストに1つの結果しか残せないことになるので、過去の点数を履歴として残せません。
(※これはscoresのuser_id, test_idを複合ユニークキーにした場合でも、同様の問題が発生しますので、繰り返しますが、仕様次第の話です。)
それから、laravelで、標準のEloquentモデルを使って独自カスタマイズをすることなく、DBのデータを取り扱いたいのであれば、プライマリーキーに複合キーは指定できません。
フローの中に、「テスト結果」テーブルや「メモ」テーブルのデータ更新があり得るなら、autoincrementのid
は絶対に必要なので、無駄ではありません。
なお、リレーションのhasManyThrough
で、scores
を中間テーブルの様に使って、users
とtests
を直接つないでいるかのように見ることが可能ですので、「結果を残すテーブルが中間テーブルであること」にこだわる必要はないだろう・・という判断です。
長くなってしまいましたが、以上です。
実際に開発するにあたっては、細かいシステム要件や、
膨大なデータ量前提で譲れない点などもあるのかと思いますので、
ひとつの参考程度に読んで頂ければと思います。