#ruby-on-rails #if-statement #helper
#ruby-на-рельсах #if-оператор #помощник
Вопрос:
Я работаю над приложением, в котором вы можете добавлять игры в библиотеку, а также удалять их. У меня есть функция добавления, работающая с помощью нажатия кнопки, однако мой оператор if для вызова «удалить из библиотеки» не отображается.
Вот мой библиотечный метод в моем игровом контроллере, который управляет функцией добавления / удаления:
def library
type = params[:type]
game = Game.new(game_params)
game.fetch_data
if type == "add"
current_user.library_additions << game
redirect_to user_library_path(current_user), notice: "Game was added to your library"
elsif type == "remove"
current_user.library_additions.delete(game)
redirect_to root_path, notice: "Game was removed from your library"
else
# Type missing, nothing happens
redirect_to game_path(game), notice: "Looks like nothing happened. Try once more!"
end
В представлении кнопка «Добавить в библиотеку» должна появляться для игр, которых нет в вашей библиотеке, и если она есть в вашей библиотеке, она должна переключиться на «Удалить из библиотеки».
<% if user_added_to_library?(current_user, game) %>
<button type="button"><%= link_to 'Remove from library', add_game_path(game.id, type: "remove", game: game), method: :put %> </button>
<% else %>
<button type="button"> <%= link_to 'Add to library', add_game_path(game.id, type: "add", game: game), method: :put %> </button>
<% end %>
Действие, определяемое user_added_to_library? не работает, поэтому я всегда вижу кнопку Добавить в библиотеку.
Вот мой помощник для user_added_to_library?
module GamesHelper
def user_added_to_library? user, game
user.libraries.where(user: user, game: @game).any?
end
end
Я подумал, может быть, мне нужно сменить библиотеки на library_additions, но я получаю сообщение об ошибке StatementInvalid. То, как код написан сейчас, не приводит к появлению ошибок, но с тем же успехом его могло бы вообще не быть.
Моя пользовательская модель, если это необходимо:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :games
has_many :libraries
has_many :library_additions, through: :libraries, source: :game
end
Нужно ли мне изменять мой user_added_to_library? метод или есть другая проблема?
Ответ №1:
Слон в комнате здесь на самом деле представляет собой набор всеобъемлющих проблем с дизайном этого кода. Этот дизайн далек от RESTful и нарушает семантику HTTP-глаголов (PUT не должен удалять ресурс), а наличие единого метода, который выполняет много разных заданий (создание и уничтожение ресурса), действительно вонючее. Вы также даже не проверяете, действительно ли игра сохранена.
Уничтожение ресурса должно быть выполнено с помощью запроса на УДАЛЕНИЕ. В Rails вы можете создавать и изменять ресурсы, просто правильно используя HTTP-глаголы:
POST /games # create a game
PATCH /games/:id # update a game
DELETE /games/:id # destroy a game
Большинство случаев могут и должны обрабатываться стандартными маршрутами CRUD, генерируемыми resources
макрокомандой. Если у вас есть ресурсы с взаимосвязями, вы описываете эти взаимосвязи с помощью вложенных маршрутов. В этом случае вы можете вложить маршрут в отдельный ресурс, поскольку вы добавляете / удаляете игры от текущего пользователя.
# generates
# POST /user/games
# DELELE /user/games/:id
resource :user, only: [] do
resources :games, only: [:create, :destroy]
end
Это будет обработано методами #create
and #destroy
в вашем GamesController
.
Вторая проблема на самом деле заключается в дизайне и моделях вашей базы данных. Если вы хотите создать дизайн, в котором у пользователей есть игры, которые можно организовать в разные библиотеки, вы бы сделали это с помощью:
class User < ApplicationRecord
has_many :libraries
has_many :games, through: :libraries
end
class Library < ApplicationRecord
belongs_to :user
has_many :library_games
has_many :games, through: :library_games
end
class LibraryGame < ApplicationRecord
belongs_to :library
belongs_to :game
has_one :user, through: :library
end
class Game < ApplicationRecord
has_many :library_games
has_many :libraries, through: :library_games
has_many :users, through: :libraries
end
Настройка косвенных ассоциаций вверх по дереву позволяет проверить, есть ли у пользователя игра, с помощью:
class User < ApplicationRecord
has_many :libraries
has_many :games, through: :libraries
def has_game?(game)
games.where(id: game.id).exist?
end
end
На самом деле нет причин, по которым это вообще должно включать вспомогательный метод. В конце концов, вы действительно просто задаете вопрос объекту user. Это не должно включать передачу двух разных объектов в отдельный метод.
Комментарии:
1. В качестве небольшого совета я бы сказал, что вы действительно добавляете здесь гораздо больше сложности, чем могут выдержать ваши навыки. Я бы настроил простое решение users -> user_games -> game и разобрался с основами и железнодорожным способом создания приложений, вместо того, чтобы уходить вглубь.
2. Я думал, что, поскольку данные поступают из драгоценного камня API, а пользователи ничего не создают, не обновляют и не удаляют, мне нужно было создать конкретную функцию добавления / удаления для библиотеки. Но наличие конкретной модели library_game имеет больше смысла. Я отредактирую свои модели и постараюсь сделать решение более элегантным.
3. Тот факт, что данные поступают из API, на самом деле не меняет основных пользователей дизайна. Вы все еще обрабатываете данные, а в Rails это означает REST.