2015-09-26 4 views
1

У меня есть приложение, где пользователи могут писать обзоры для фильма. То, что я хотел бы сделать, - это ограничить пользователя созданием только одного обзора для каждого фильма. Я сумел сделать это в моем обзорах контроллера, как так:Только разрешить пользователям создавать один обзор за фильм

class ReviewsController < ApplicationController 
    before_action :has_reviewed, only [:new] 
    .... 
    def has_reviewed? 
    if Review.where(user_id: current_user.id, movie_id: @movie.id).any? 
     redirect_to movie_reviews_path 

     flash[:notice] = "You've already written a review for this movie." 
    end 
    end 
end 

Где я сейчас возникаю проблемы переводят эту же логику в шаблон просмотра индекса с хелперами методами Завещания и CanCanCan в моем распоряжении.

<% if user_signed_in? && ... %> # current_user has already created a review for this movie 
    <%= link_to "Edit Review", edit_movie_review_path(@movie, review) %> 
<% else %> 
    <%= link_to "Write a Review", new_movie_review_path %> 
<% end %> 

также: Есть ли способ, чтобы улучшить поиск в моей has_reviewed? способ? Я чувствую, что есть лучший способ написать это, но не могу определить наиболее подходящее исправление.

ответ

0

Вот что я придумал:

Я создал метод экземпляра, чтобы получить обзор фильмов пользователя с помощью метода find_by на Review модели:

class User < ActiveRecord::Base 
    .... 
    def movie_review(album) 
    Review.find_by(user_id: self, album_id: album) 
    end 
end 

Этот метод также пригодится при настройке моего обратного вызова:

class ReviewsController < ApplicationController 
    before_action :limit_review, only: [:new, :create] 
    .... 
    private 
    def limit_review 
     user_review = current_user.movie_review(@movie) 

     if user_review.present? 
     redirect_to edit_movie_review_path(@movie, user_review) 
     end 
    end 
end 

Создан вспомогательный метод для отображения соответствующей ссылки на Редактировать или создать отзыв. Большое спасибо Austio и его suggestion:

module ReviewsHelper 
    def create_or_edit_review_path(movie) 
    user_review = current_user.movie_review(movie) if user_signed_in? 

    if user_signed_in? && user_review.present? 
     link_to "Edit review", edit_movie_review_path(movie, user_review) 
    else 
     link_to "Write a review", new_movie_review_path 
    end 
    end 
end 

И наконец это то, как я называю помощника в моем шаблоне вида (ы):

.... 
<%= create_or_edit_review_path(@album) %> 
+0

Привет, У меня есть аналогичный пример, но я немного смущен. У вас есть @album или @movie? – jedi

1

Почему бы не сделать has_reviewed? метод в вашем классе User?

например.

def has_reviewed?(reviewable) 
    # query in here 
end 

Тогда вы должны использовать это как раз в своем контроллере и своих взглядах.

+0

Я думал сделать это, но самая большая проблема, таким образом, для меня, как это сделать, чтобы получить «current_user» из модели. –

+0

Это метод в пользовательском классе, поэтому у вас будет текущий пользователь, если вы его назвали current_user.has_review? (Отзыв) – DanSingerman

0

Вы хотите сделать это как для новых, так и для создания. В противном случае опытный пользователь сможет запустить сообщение, которое пройдет мимо вашего нового действия.

Я бы поставил link_to либо в помощник, либо в объект презентатора. Как правило, это выглядит так.

def create_or_edit_review_path(movie, current_user) 
    return '' if current_user.blank? 

    if current_user.review.present? 
    #Generate review edit link 
    else 
    #generate new link 
    end 
end 

После этого во всех ваших взглядов было бы просто

<%= create_or_edit_review_path(@movie, current_user) %> 

Тогда в контроллере как новых, так и создать можно сделать либо перед действием или просто перенаправить на каждый.

before_action :enforce_single_review, only: [:create, :new] 

def enforce_single_review 
    if current_user.review.present? 
    redirect_to review_path(current_user.review) 
    end 
end 
+0

Для этого вспомогательного метода вам понадобится слово 'def' в начале, правильно? –

+0

avsolutely будет редактировать. – Austio

+0

Я также должен был отметить, что пользователь имеет 'has_many' отзывы. Так что в основном для третьей строки этого вспомогательного метода я получаю 'undefined method 'review' для # ', поскольку это не ассоциация 'has_one'. –

2

Why not use a validation:

#app/models/review.rb 
class Review < ActiveRecord::Base 
    validates :movie_id, uniqueness: { scope: :user_id, message: "You've reviewed this movie!" } 
end 

Это рассматривает вашу review модель belongs_to :movie


Вы также можете использовать ActiveRecord обратного вызова:

#app/models/review.rb 
class Review < ActiveRecord::Base 
    before_create :has_review? 
    belongs_to :user, inverse_of: :reviews 
    belongs_to :movie 

    def has_review? 
     return if Review.exists?(user: user, movie_id: movie_id) 
    end 
end 

#app/models/user.rb 
class User < ActiveRecord::Base 
    has_many :reviews, inverse_of: :user 
end 

Есть ли способ улучшить поиск в моем has_reviewed? метод?

def has_reviewed? 
     redirect_to album_reviews_path, notice: "You've already written a review for this album." if current_user.reviews.exists?(movie: @movie) 
    end 
Смежные вопросы