こんにちは、おーしまです。
今回は、自動車レビューアプリのレビュー投稿機能まで実装できたので、一緒に見ていこうと思います。データとタグと中間テーブルを同時に保存する方法は下の方にあるので、飛ばしてください。
今の出来はこんな感じです。
投稿機能までできて、「そろそろhtml,cssを作っていかないとまずいかな」と思ったので、少しずつそちらの編集も始めました。
「新規レビュー」ボタンから、新規レビュー作成画面に遷移できるようになっています。新規レビューは「タイトル」と「本文」の2つがあれば投稿可能になっていて、今後タイトルと写真だけでも投稿できるようにします。また、新しく投稿したものから、上から順に表示されるようになっています。
実際に、投稿してみましょう。
「新規レビュー投稿」をクリックすると、新規レビュー作成画面に遷移できます。
おっと、ログインしていなかったので、ログイン画面に遷移しました。
「Sign up」を押して新規登録画面に遷移しましょう。
好きな車と自己紹介の欄は記入しなくても、ユーザー登録ができるようになっています。
これで、レビューを投稿することができるようになりました。
新規レビュー投稿画面に移動します。
フォームに対して何の説明も書いてありませんが、左から順に、
タイトル、画像、本文、メーカー、車名、ボディタイプ、タグ
という風になっています。
これで「投稿する」を押すと、、、
このように一番上に表示されました。
見た目がカスなので、あまり良い出来には見えませんが、何とかできたのでよかったです。この程度に二日もかけてしまったのが本当に情けない。。。
それではコードの方も説明します。
まず今回は、レビューとタグが多対多の関係なので、中間テーブルが必要なのと、レビューとタグに同時に保存しなければいけないのでさらにもう一つモデルが必要で、計4つのモデルが必要になります。そして、メーカーとボディタイプはActiveHashGemを用いて、選択できる形にします。
class Maker < ActiveHash::Base self.data = [ { id: 1, name: nil }, { id: 2, name: 'トヨタ' }, { id: 3, name: '日産' }, { id: 4, name: 'ホンダ' }, { id: 5, name: 'マツダ' }, { id: 6, name: 'スバル' }, { id: 7, name: 'ダイハツ' }, { id: 8, name: '三菱' }, { id: 9, name: 'スズキ' }, { id: 10, name: 'レクサス' }, { id: 11, name: 'フォルクスワーゲン'}, { id: 12, name: 'BMW' }, { id: 13, name: 'メルセデス・ベンツ' }, { id: 14, name: 'アウディ' }, { id: 15, name: 'ボルボ' }, { id: 16, name: 'プジョー' }, { id: 17, name: 'ランドローバー' }, { id: 18, name: 'ポルシェ' }, #以下省略
class BodyType < ActiveHash::Base self.data = [ { id: 1, name: nil }, { id: 2, name: '軽自動車' }, { id: 3, name: 'コンパクト' }, { id: 4, name: 'セダン' }, { id: 5, name: 'ワゴン' }, { id: 6, name: 'SUV' }, { id: 7, name: 'ミニバン' }, { id: 8, name: 'クーペ' }, { id: 9, name: 'オープンカー' } ] end
「オススメの車はありませんか?」みたいな投稿もできるようにしたかったので、メーカーなどは空欄で投稿できます。そのため id:1 は nil という形にしました。これで表示するときに、メーカーなどの欄は空白になります。
次はコントローラーです。
class CarsController < ApplicationController before_action :authenticate_user!, only:[:new] def index @cars = Car.all.includes(:user).order('created_at DESC') end def new @car = SaveCarsTag.new end def create @car = SaveCarsTag.new(car_params) if @car.valid? @car.save return redirect_to root_path else render :new end end private def car_params params.require(:save_cars_tag).permit(:title, :image, :text, :maker_id, :car_name, :body_type_id, :name).merge(user_id: current_user.id) end end
特に工夫した点はありません。、、、、そのままですね。以前、ブログに書いたようなコードはもう当たり前に使えるようになりました。成長。
2つのテーブルに同時に保存するため、新しくSaveCarsTagというモデルを作っています。そのモデルのインスタンスを生成して、newアクションからフォームに持って行っていますね。それで帰ってきたparamsをvalid?で振り分けています。保存できるようであればSaveCarsTagモデルのsaveに持っていきます。
class SaveCarsTag include ActiveModel::Model attr_accessor :title, :image, :text, :maker_id, :car_name, :body_type_id, :name, :user_id with_options presence: true do validates :title validates :text end def save car = Car.create(title: title, image: image, text: text, maker_id: maker_id, car_name: car_name, body_type_id: body_type_id, user_id: user_id) tag = Tag.where(name: name).first_or_initialize tag.save CarTag.create(car_id: car.id, tag_id: tag.id) end end
include ActiveModel::Model
で、普通のクラスをモデルのように扱うことができるようになります。
attr_accessor はゲッターとセッターを定義してくれるメソッドで、簡単にいうと、これを書くことで普通のクラスなのに他のモデルのカラムを保存したり、取り出したりすることができるようになります。
その下に、このクラスで取り扱うデータのバリデーションを記述しています。
あとは、それぞれのデータをcarsテーブル、tagsテーブル、car_tagsテーブルに保存して、終わりです。
tag = Tag.where(name: name).first_or_initialize tag.save
これで、すでに保存してあるタグがないか探して、あれば保存しない。無ければ保存する。という流れにしています。
これは正直、ちゃんと把握していません。すいません。
とにかく、これ書いとけば良いです。
今回はここまでにします。
明日は、HTML、CSSに捧げます。
おやすみなさい。