tfantina
tfantina

Reputation: 804

Creating a record with a has_many through: relationship

I'm having trouble creating a record with a has_many through: relationship in Rails. I have a User model, a Task model and a Timer Model. The idea is that a User has_many :tasks and also has_many :timers through: :tasks Tasks also have many timers. The idea is that a user can create as many tasks as they want and time themselves each day doing a specific task to track progress over time.

User.rb

class User < ApplicationRecord

  has_many :tasks
  has_many :timers, through: :tasks

end

Task.rb

class Task < ApplicationRecord
  validates :user_id, presence: true
  belongs_to :user
  has_many :timers 


end

Timer.rb

class Timer < ApplicationRecord
  belongs_to :task
end

The objective is that the user can see their tasks and start and stop a timer next to each task. The timer is simple enough, it just has a date created and date modified that will track how long it ran.

However, in the timers_controller I'm not really sure how to create a timer:

class TimersController < ApplicationController

def new
  @timer = Timer.new
end

def create
  @timer = current_user.build(timer_params)
end

private
  def timer_params
    params.require(:timer).permit(:start_time)
  end
end

I've tried changing the structure of the create action as well as the params but so far nothing has worked.

For some additional context here is the tasks/index.html.erb

    <% @tasks.each do |t| %>
  <%= t.title %>
  <%= form_for(@user.timers.build) do |f| %>
    <%= f.submit "Start", class: "btn btn-primary" %>
  <% end %>
  <br />
  <% end %>

This is the Timer Migration:

class CreateTimers < ActiveRecord::Migration[5.1]
  def change
    create_table :timers do |t|
      t.integer :user_id
      t.integer :task_id
      t.datetime :start_time
      t.datetime :end_time

      t.timestamps
    end
    add_index :timers, :user_id
    add_index :timers, :task_id

  end
end

Tasks migration:

class CreateTasks < ActiveRecord::Migration[5.1]
  def change
    create_table :tasks do |t|
      t.string :title
      t.text :description

      t.timestamps
    end
  end
end

Upvotes: 0

Views: 74

Answers (2)

Pablo
Pablo

Reputation: 3005

This is not a complete answer but some ideas.

Joe Marion's answer can be right, if a task has only one timer. This seems reasonable, but it's not what you wrote (Tasks also have many timers). So here you have another option.

When you create the timer, it should be associated to a task (not directly to the user). And of course you need to know in advance which task the timer belongs to. The task_id should be a parameter in the form.

So the build command should be something like this

def create
  task = current_user.tasks.find_by(id: params[:task_id])
  if task
    @timer = task.timers.build(timer_params)
    if @timer.save
      # ....
    else
      # .....
  else
    # Error, task not found. Timer cannot be created
    # Whatever you want to do in this case ...
  end
end

In the view, your forms should be associated with tasks and should include the task_id to be used in the create action.

<% @tasks.each do |t| %>
  <%= t.title %>
  <%= form_for(t.timers.build) do |f| %>
    <!-- where is the start_time field? -->
    <%= f.hidden_field :task_id %>
    <%= f.submit "Start", class: "btn btn-primary" %>
  <% end %>
  <br />
<% end %>

Upvotes: 1

Joe Marion
Joe Marion

Reputation: 406

If you want to track how long each task takes you could just add a timer field to a user_tasks table.

User.rb

class User < ApplicationRecord
  has_many :user_tasks
  has_many :tasks, through: :user_tasks
end

Task.rb

class Task < ApplicationRecord
  has_many :user_tasks
  has_many :users, through: :user_tasks
end

Timer.rb

class UserTasks < ApplicationRecord
  belongs_to :task
  belongs_to :user
end

The UserTasks is a join table that represents each individual task by the user. So if you added a time_to_complete field to the UserTasks table it would show the time for each task per user.

Upvotes: 0

Related Questions