movieプロジェクトにタグ機能を実装しよう
これまでにmovieプロジェクトで一対多(directorとworks)、および一対一(userとprofile)のアソシエーション について学んできました。ここでは応用編として多対多のアソシエーション を学びましょう。
今回もmovieプロジェクトを題材に、以下の機能を実装します。
・映画作品に複数のタグが付けられるようにする。
・タグをクリックすると、そのタグがついた映画作品の一覧が表示される。
まず前提として、タグの情報を保存するtagsテーブル、およびモデルを作成します。tagsテーブルにはtagの名前が保存できるようにname
というカラムを作成してください。
そのtagsテーブルとworksテーブルのアソシエーションを考えてみましょう。
例えば、上記のStarwarsという作品には、「Japanese」、「Aacademy awards」など複数のタグがついています。そして「Japanese」のタグは、他の映画作品にも使われる可能性がありますので、アソシエーション は多対多の関係性になります。
では、データベースではどのように管理すれば良いでしょうか?
例えば、worksテーブルにtags_id
というカラムを追加してtag_idを保存する…と考える方もいらっしゃるかもしれませんが、ひとつ注意点があります。それは「ひとつのカラムには、原則データはひとつだけ」という点です。ですのでこの方法は使えません。
ここで登場するのが「中間テーブル」という考え方です。中間テーブルとは、今回で言えばworksとtagsのテーブルの「中間(真ん中)」にあるイメージで、それぞれのworkとtagのデータがどのように繋がっているかを保存するテーブルです。
ここでは「work_tags」という名前の新しいテーブル(中間テーブル)を作成し、そこにはwork_idとtag_idが保存できるようにします。実際のwork_tagsテーブルはこのようになります。
workのid=1には、3つのタグ(id=2, 4, 5)がついているのが分かります。また、tag_id=1のタグは、3つの作品(id=1, 5, 9)についていますね。タグや作品そのものの情報はtags、worksテーブルに保存し、その繋がりは中間テーブルに保存する、という仕組みです。早速work_tagsテーブルを作成しましょう。
次に、アソシエーションをモデルに定義します。
work.rb(Workモデル)
has_many :work_tags, dependent: :destroy has_many :tags, through: :work_tags
tag.rb(Tabモデル)
has_many :work_tags, dependent: :destroy has_many :works, through: :work_tags
work_tag.rb(WorkTagモデル)
belongs_to :work belongs_to :tag
WorkモデルとTagモデルでそれぞれhas_many: work_tags
と定義します。その後ろにdependent: :destroy
と記述することで、もしworkやtagのレコードが削除された場合には、関連するwork_tagsテーブル(中間テーブル)のレコードも削除されるようになります。
また、WorkTagモデルでは、belongs_to :work
、belongs_to :tag
と定義し、WorkモデルとTagモデルと紐付けます。
ここまで作業したら、コンソールからサンプルのタグをいくつか登録してみましょう。
コンソールを使ってデータを登録するには