0

I have a rails application with Events and Users and compared them in a third table UserEventStates, which belongs to Users and Events and has an integer named state and makes it possible to set the state for an user for any event separately/different. Now I would like to update the state for the user for this event when the matching button was clicked, how can I do this with Turbo?

# models/event.rb
class Event < ApplicationRecord
  has_many :user_event_states
  has_many :users, through: :user_event_states
  accepts_nested_attributes_for :user_event_states
end

# models/user.rb
class User < ApplicationRecord
  has_many :user_event_states
  has_many :events, through: :user_event_states
  accepts_nested_attributes_for :user_event_states
end

# models/user_event_state.rb
class UserEventState < ApplicationRecord
  belongs_to :event
  belongs_to :user
end

1 Answers1

0

Updated answer:

If I got the relationships right:

# models/event.rb
class Event < ApplicationRecord
  has_many :user_event_states
  has_many :users, through: :user_event_states
end

# models/user.rb
class User < ApplicationRecord
  has_many :user_event_states
  has_many :events, through: :user_event_states
  accepts_nested_attributes_for :user_event_states
end

# models/user_event_state.rb
class UserEventState < ApplicationRecord
  belongs_to :event
  belongs_to :user
  accepts_nested_attributes_for :user_event_states
end

And I understand your goal:

I would like to have buttons for each user and update only the state from the selected user

Then here are some things I would change:

Create a UserEventStatesController:

# app/controllers/user_event_states_controller.rb
class UserEventStatesController < ApplicationController
  before_action :set_event
  before_action :set_user_event_state, only: %i[show edit update destroy]
  before_action :set_user, only: %i[show edit update destroy]

  ...

  def update
    if @user_event_state.update(user_event_state_params)
      # action to perform on success
    else
      # action to perform on fail
    end
  end

  ...
  
  private

  def set_event
    @event = Event.find(params[:event_id])
  end

  def set_user_event_state
    @user_event_state = @event.user_event_states.find(params[:id])
  end

  def set_user
    @user = @user_event_state.user
  end

  def user_event_state_params
    params.require(:user_event_state).permit(:event_id, :user_id, :state)
  end
end

And change your EventsController back to a more standard controller:


#app/controllers/events_controller.rb
before_action :set_event, only: %i[ show edit update destroy]

def show
  # get *ALL* UserEventStates for this Event
  @ues_events = UserEventState.where(event_id: @event.id)
end

private

def set_event
  @event = Event.find(params[:id])
end

def event_params
  params.require(:event).permit(:description, :date, :meeting_time, :start_time, :end_time)
end

Nest UserEventState routes under Event:

# config/routes

devise_for :users

resources :events do
  resources :user_event_states
end

Run rails routes from your terminal to check the prefixes of the routes. Should look like this:

$ rails routes
                     Prefix Verb   URI Pattern                              Controller#Action
...
    event_user_event_states GET    /events/:event_id/user_event_states      user_events_states#index
                            POST   /events/:event_id/user_event_states      user_events_states#create
 new_event_user_event_state GET    /events/:event_id/user_event_states/new  user_events_states#new
edit_event_user_event_state GET    /events/:event_id/user_event_states/edit user_events_states#edit
     event_user_event_state GET    /events/:event_id/user_event_states/show user_events_states#show
                            PATCH  /events/:event_id/user_event_states/:id  user_events_states#update
                            PUT    /events/:event_id/user_event_states/:id  user_events_states#update
                            DELETE /events/:event_id/user_event_states/:id  user_events_states#destroy

Now to views:

#app/views/events/show.html.erb

<% @ues_events.each do |ues_ev| %>
  <%= ues_ev.user.email %>
  <%= ues_ev.state %>
  <%= ues_ev.night %>
  <% form_with model: ues_ev, url: [@event, ues_ev] do |form| %>
    <%= form.text_field :state %> <!-- or whatever kind of input :state needs to be -->
    <%= form.submit 'Zusagen' %>
  <% end %>

The form should submit a PATCH request to /events/:event_id/user_event_states/:id, which will hit the UserEventStatesController#update action.

The params[:user_event_state] should be { state: 'form response' }

upgrade idea:

Submitting a form and getting redirected can be slow and not very smooth in terms of user experience.

Say you want to say right on the events#index page and just click buttons to toggle the state for a few users.

You could look at replacing your forms with buttons or links that use:


First answer:

get 'right route' can be whatever you want, as long as it is a viable URI path.

Any symbols in the path are accessible via params

So get 'events/:events_id/users/:id/update-event-state' to: ...

Would have params[:event_id] and params[:id].

Since it looks like you need only :event_id params, it doesn't have to include user.

get 'events/:event_id/user-state', to: ...

But it also doesn't have to follow convention:

get 'foo/bar/baz/:event_id', to: ...

If you want to nest the route, you can, but you lose control over what the URL will be:

resources :events do
  # url would be events/:id/update_user_event_state
  get :update_user_event_state, on: :member
  
  resources :users do
    # url would be events/:event_id/users/update_user_event_state
    get :update_user_event_state, on: :collection

    # url would be events/:event_id/users/:user_id/update_user_event_state
    get :update_user_event_state, on: :member
  end
end

Also, I think you have a typo. It should be like this, I think:

def update_user_event_state
   @ues_event.update(state: 1)
   # not self.update
end
Chiperific
  • 4,428
  • 3
  • 21
  • 41