Sam Price
Sam Price

Reputation: 51

Creating association for users with predefined data

Hey any help would be appreciated!

Basically I have two models - User (using Devise) and Sport

class User < ApplicationRecord
  has_and_belongs_to_many :sports
end

class Sport < ApplicationRecord
  has_and_belongs_to_many :users
end

I have a predefined list of Sports that are seeded into my database (e.g. Running, Weightlifting etc). What I want to do is have a user login and then select which sports they do and have that saved through an association. This then allows the user to search for and see what sports other users do etc.

I have managed to create associations through the console like this:

user = User.find(1)
sport = Sport.find(1)
user.sports << sport

but I can't figure how to translate that to a form.

So far I've got this even though all it does is create a new Sport (which I don't want):

#sports_controller.rb
def index
  @sports = Sport.where(["name ILIKE ?", "%#{params[:search]}%"])
end

def new
  @sport = Sports.new
end

def create
  @sport = current_user.sports.create(sports_params)
end

private
 def sports_params
 params.require(:sport).permit(:name)
end

and then for my form:

#sports/new.html.erb
<%= form_for @sport do |f| %>
  <div class="form-group">
    <%= f.label :exercises %><br>
    <%= f.collection_check_boxes :name, Sport.all, :id, :name %>
  </div>

  <%= f.button :submit %>
<% end %>

Is it right doing it through this controller? I'm really lost as to how to do this and it's a core functionality for my uni project.

Thanks in advance!

Upvotes: 1

Views: 47

Answers (1)

RichardAE
RichardAE

Reputation: 2970

Firstly I would get rid of your SportsController, this feels like you are updating a User only. The SportsController would be for creating Sports themselves, which you don't need to do as you are seeding them)

So if you haven't got one already, add the user routes (this won't allow updating devise fields e.g password, but that's outside of the scope of this question):

resources :users

Then in your ApplicationController, add this method:

def after_sign_in_path_for(_resource)
  edit_user_path(current_user)
end

That will redirect the user after signin to their edit page.

Finally the form:

#users/edit.html.erb

<%= form_for @user do |form| %>

  <% @sports.each do |sport| %>
    <%= check_box_tag "user[sport_ids][]", sport.id, form.object.sports.include?(sport) %>
    <%= sport.name %>
  <% end %>

  <%= f.button :submit %>
<% end %>

This will pass up an array of sport_ids, which will automatically associate all the selected sports with your user, due to Rails magic :). The magic being you can also associate objects like so:

@model.associated_model_ids << [1,2,3]

And your UsersController:

def UsersController < ApplicationController
  def edit
    @user = User.find(params[:id])
    @sports = Sport.all.order(name: :asc)
  end

  def update
    @user = User.find(params[:id])

    if @user.update(user_params)
      redirect_to @user
    else
      render 'edit'
    end
  end 

  def show
    @user = User.find(params[:id])    
  end  

  private

  def user_params
    params.require(:user).permit(sport_ids: [])
  end
end

You will need to add the user show.html.erb template but that should be enough to get you going. I think there is a nicer way to do the checkboxes so I might update this later, but that should work.

Example to see if it worked:

#users/show.html.erb

<%= @users.sports.map(&:name).join(', ') %>

Upvotes: 1

Related Questions