2016-09-21 3 views
0

Я строию сайт рельсов, который имеет gyms и reviews. Я бы хотел, чтобы пользователи могли оставлять отзывы для спортзалов. Я мои таблицы установлены какРельсы вложенные ресурсы: контроллер и формы

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 

Сейчас контроллер тренажерного зала является статическим (могут не CRUD тренажерных зал, так как это админы вещь) и просто делают страницы ж/Информацию , Я пытаюсь добавить отзывы, но я не хочу смешивать ассоциации. Вот мой тренажерный зал информация контроллер

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 

и тренажерный зал/показать link_to, который указывает на gyms/:id/reviews

В спортзалах/у меня есть форму отзыва

<%= 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' получает пользователя id из 'params [: 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/reviews будет обрабатываться ReviewsController не GymsController.

Это точно так, как должно быть, поскольку каждый контроллер должен отвечать только за CRUD'ing одного ресурса.

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 

Некоторые вещи, которые следует обратить внимание здесь - не передавать идентификатор пользователя через форму. Это позволяет легко обманывать. Вместо этого вы получите текущего пользователя из сеанса или токена.

Позволяет создать частичный для формы:

<%= 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 от контроллера спортзалов. Это было бы вложенная форма, которая потребуется в вашей модели спортзал accepts_nested_attributes_for:

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

Ваша форма должна быть переработана с fields_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 действия, вы создаете новый Gym экземпляра и вам не хватает множественного числа review в при создании экземпляра @review:

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

Помните, что gymhas_manyreviews - так что вы собираетесь использовать множественное число reviews, когда это возможно.

Не уверен, что я все поймал, но я бы рекомендовал проверить руководство по Rails на Nested Forms, раздел 9.2 для действительно хорошего объяснения. Вложенные формы могут быть сложными, а другой более простой вариант - иметь отдельную форму обзора, созданную в контроллере отзывов (см. Ответ @ max).

+0

Обратите внимание, что здесь очень большая разница - «nested_attributes» действительно полезен, когда вам нужно, чтобы пользователь мог создавать несколько вещей в том же запросе. В этом случае это будет тот же самый пользователь, создающий тренажерный зал и обзор одновременно, что, вероятно, не то, что вы хотите. – max

+0

Я согласен с вами в том, что это менее полезно, как я уже упоминал в своем последнем предложении. О, хорошо, не больно учиться по-другому. – Ren

Смежные вопросы