2016-09-21 5 views
0

私はgymsreviewsを持つレールサイトを構築しています。私は、ユーザーがジムのレビューを残すことができるようにしたいと思います。私は私のテーブルは今(つまり、管理者のことだとして、することはできませんCRUDジム)ジムコントローラが静的であるレールのネストされたリソース:コントローラとフォーム

class Gym < ActiveRecord::Base 
    has_many :pictures, as: :imageable 
    has_many :reviews 
end 

class Review < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :gym 
    validates :body, presence: true, length: { maximum: 1000 } 
    validates :rating, presence: true 
end 

として設定しているとだけ情報/ wのページをレンダリング。私はレビューを追加しようとしていますが、私は関連を混乱させたくありません。ここに私のジムコントローラ情報

class GymsController < ApplicationController 
    before_action :logged_in_user, only: [:index, :edit, :update, :destroy] 
    before_action :correct_user, only: [:edit, :update] 

    def index 
    @q = Gym.ransack(params[:q]) 
    @gyms = @q.result 
    @other_gyms = Gym.all 
    if @gyms.to_a.count < 1 
     flash[:warning] = "No gym matched #{params[:q][:name_or_phone_number_or_city_or_zip_code_cont]}" 
    end 
    end 

    def new 
    @gym = Gym.find(params[:id]) 
    @review = @gym.review.new 
    end 

    def create 
    @gym = Gym.find(params[:id]) 
    @review = @gym.reviews.build(gym_params) 
    if @review.save 
     flash[:success] = 'Review Saved' 
     redirect_to :back 
    else 
     render 'new' 
    end 
    end 

    def show 
    @gym = Gym.find(params[:id]) 
    @reviews = @gym.reviews 
    end 

    private 
    def gym_params 
    params.require(:gym).permit(:name, :description, :address, :address_2, :zip_code, 
           :phone_number, :website_url, :city, :state, :latitude, :longitude, 
           review_attributes: [:user_id, :rating, :body, :gym_id]) 
    end 

    def logged_in_user 
    unless logged_in? 
     store_location 
     flash[:danger] = 'Please log in' 
     redirect_to login_url 
    end 
    end 

    def correct_user 
    @user = User.find(params[:id]) 
    redirect_to(root_url) unless current_user?(@user) 
    end 
end 

私のルート

resources :gyms, only: [:index, :show] do 
    resources :reviews 
end 

やジム/新しい私が審査フォーム

を持って gyms/:id/reviews

を指すジム/ link_toを示しています

<%= form_for [@gym, @review] do |f| %> 
    <%= f.label :rating, 'Select your rating' %> 
    <div id='ratyRating'></div><br> 

    <%= f.text_area :body, size: '100x10' %> 

    <%= f.hidden_field :user_id, value: current_user.id %> 

    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

thは動作しません。 link_toボタンからインデックスページであるgyms/:id/reviewsに向かいます。私はこれを行うもっと良い方法があるように感じる。誰かが私がここで間違っていることを見ていますか?

+0

あなたの 'correct_user'フィルタがユーザを取得するのを間違っています'params [:id]'のidです。 'flash [:warning] =" {params [:q] [:name_or_phone_number_or_city_or_zip_code_cont]} "はジムにマッチしませんでした" "は、パラメータをエコーバックしているため、ユーザを注射の脆弱性にさらします。 – max

+0

それをキャッチするために感謝!私は今まで知らなかった。あなたはもっと詳しく説明できますか? –

+0

http://guides.rubyonrails.org/security.html#cross-site-scripting-xss – max

答えて

1

コンソールから$ rake routesを実行して開始します。その場合、POST /gyms/:gym_id/reviewsReviewsControllerではなく、GymsControllerで処理されます。

各コントローラーは単一のリソースをCRUDする責任があるため、正確にはどのコントローラーにする必要がありますか。

class ReviewsController < ApplicationController 

    before_action :set_gym! 

    # GET /gyms/:gym_id/reviews 
    def index 
    @reviews = @gym.reviews 
    end 

    # POST /gyms/:gym_id/reviews 
    def create 
    @review = @gym.reviews.new(review_params) do |r| 
     r.user = current_user 
    end 
    if @review.save 
     redirect_to @gym, success: 'Review created!' 
    else 
     render :new 
    end 
    end 

    private 
    def set_gym! 
     @gym = Gym.find(params[:gym_id]) 
    end 

    def review_params 
     params.require(:review).permit(:body) 
    end 
end 

ここで注意しなければならないことはいくつかあります。フォームからユーザーIDを渡してください。それは簡単に偽装する方法になります。代わりに、セッションまたはトークンから現在のユーザーを取得します。

フォームの部分を作成することができます:

<%= form_for [gym, review] do |f| %> 
    <%= f.label :rating, 'Select your rating' %> 
    <%= f.text_area :body, size: '100x10' %> 
    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

その後、レビューが無効である場合にレンダリングされるreviews/new.html.erbビューが必要な場合:

<%= render partial: 'form', gym: @gym, review: @review %> 

を私たちは、その後もgyms/show.html.erbでフォームを埋め込むことができます:

<%= render partial: 'reviews/form', gym: @gym, review: @gym.reviews.new %> 
1

ジムコントローラからreviewを作成しようとしているようです。あなたのフォームがfields_forで手直しする必要が

class Gym < ActiveRecord::Base 
    has_many :pictures, as: :imageable 
    has_many :reviews 
    accepts_nested_attributes_for :reviews 
end 

:それはあなたのジムモデルでaccepts_nested_attributes_forを必要とするネストされた形、だろう

<%= form_for @gym do |f| %> 

     <%= f.fields_for :reviews do |reviews_form| %> 

      <%= reviews_form.label :rating, 'Select your rating' %> 
      <div id='ratyRating'></div><br> 

      <%= reviews_form.text_area :body, size: '100x10' %> 

      <%= reviews_form.hidden_field :user_id, value: current_user.id %> 
     <% end %> 

    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

あなたgym_params、reviews_attributesでこのように見える必要がありますないreview_attributes

def gym_params 
    params.require(:gym).permit(:name, :description, :address, :address_2, :zip_code, 
           :phone_number, :website_url, :city, :state, :latitude, :longitude, 
           reviews_attributes: [:user_id, :rating, :body, :gym_id]) 
end 

次に、あなたのnewアクションで、新しいを作成していますインスタンスとあなたが@reviewインスタンスを作成するときにreviewの複数が欠落しています

@gym = Gym.new 
@review = @gym.reviews.build 

gym has_many reviewsことを忘れないでください - あなたは、可能な場合、複数の reviewsを使用しようとしています。

私がすべてをキャッチしたのかどうかわかりませんが、本当に良い説明については、Nested Formsのセクション9.2にあるRailsガイドをチェックすることをお勧めします。入れ子になったフォームは扱いにくく、別の簡単なオプションは、レビューコントローラで作成された別のレビューフォームを持つことです(@ maxの回答を参照)。

+0

ここでかなり大きな違いがあることに注意してください。 - 'nested_attributes'は、ユーザーが作成できるようにする必要がある場合にのみ本当に便利です同じ要求の中にいくつかのものがあります。この場合、同じユーザーがジムを作成し、同時にレビューします。これはおそらくあなたが望むものではありません。 – max

+0

私は最後の文章で言及したように、あまり役に立たないとあなたに同意します。ああ、別のやり方を学ぶのに傷つけない。 – Ren

関連する問題