I'm working on an app where you can add games to a library and also remove them. I have the adding function working via clicking a button, however my if statement to bring up "remove from library" is not showing.
Here is my library method in my games controller which controls the add/remove feature:
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
In the view an "Add to Library" button is supposed to appear on games that aren't in your library, and if it is in your library it should switch to "Remove from Library"
<% 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 %>
The action determined by user_added_to_library? isn't working so I always see the Add to Library button.
Here is my helper for user_added_to_library?
module GamesHelper
def user_added_to_library? user, game
user.libraries.where(user: user, game: @game).any?
end
end
I thought maybe I needed to change libraries to library_additions but I get StatementInvalid error. The way the code is written now is not throwing up errors but it might as well not be there at all.
My User model if that's necessary:
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
Do I need to alter my user_added_to_library? method or is there another issue?
The elephant in the room here is really a set of overarching design issues with this code. This design is far from RESTful and violates the semantics of the HTTP verbs (PUT should NOT delete a resource) and having a single method that does many different jobs (creating and destroying a resource) is really smelly. You're also not even checking if the game is actually saved.
Destroying a resource should be done with a DELETE request. In Rails you can create and modify resources by simply using the HTTP verbs correctly:
POST /games # create a game
PATCH /games/:id # update a game
DELETE /games/:id # destroy a game
Most cases can and should be handled by the standard CRUD routes generated by the resources
macro. If you have resources with relationships you describe those relationships with nested routes. In this case you might choose to nest the route in a singular resource since you are adding/removing games from the current user.
# generates
# POST /user/games
# DELELE /user/games/:id
resource :user, only: [] do
resources :games, only: [:create, :destroy]
end
This would be handled by the #create
and #destroy
methods in your GamesController
.
The second issue is really your database design and models. If you want to create a design where users have games that can be organized into different libraries you would do it by:
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
Setting up indirect associations up the tree makes it possible to check if a user has a game by:
class User < ApplicationRecord
has_many :libraries
has_many :games, through: :libraries
def has_game?(game)
games.where(id: game.id).exist?
end
end
There really is no reason why this should involve a helper method at all. After all you're really just asking the user object a question. That should not involve passing two different objects to a separate method.