Both my Post & Comment models have has_one_attached :file
. What I want to do is to count the number of times a specific user downloaded either a post's file or a comment's file. Create a sort of download_counter_cache
on the User model that increments whenever this user downloads a file. How can I achieve this?
Update based on Max's answer
This is what I did for the moment:
# Migration file
class CreateDownloads < ActiveRecord::Migration[6.0]
def change
create_table :downloads do |t|
t.references :user, null: false, foreign_key: true
t.references :resource, polymorphic: true
t.timestamps
end
end
end
# Routes.rb
concern :downloadable do
resources :downloads, only: :create
end
resources :posts, concerns: :downloadable do
[...] # Routes such as like/dislike
resources :comments, concerns: :downloadable do
[...] # Routes such as like/dislike
end
end
# In posts/_post.html.erb
<%= link_to([@post, :downloads], html_options = {class: 'file w-100'}, method: :post) do %>
[...]
<% end %>
My downloads_controller
is exactly the same as suggested in Max's answer, as are my Post, Comment, User & Download models.
The problem is that whenever I try to download it redirects me to downloads#index
which obviously does not exist. I am not sure how I am supposed to create the resource
class.
max has a good answer, but if you just want to have only one column on a User model to count all downloads it could be done without a polymorphic join table.
Let's add an attribute to the User module to store the counts:
add_column :users, :download_count, :integer
Then add a controller that will handle the counts and redirect to a downloaded file:
class DownloadsController < ApplicationController
def create
# using find_by so it doesn't throw any errors
resource = Post.find_by_id(params[:post_id]) || Comment.find_by_id(params[:comment_id])
if resource
current_user.increment!(:download_count)
redirect_to rails_blob_path(resource.file, disposition: "attachment")
else
render nothing: true
end
end
end
Routes will look like:
resources :downloads, only: :create
Download link in view looks like:
# for post file
<%= link_to 'download file', downloads_path(post_id: @post.id), method: :post %>
# for comment file
<%= link_to 'download file', downloads_path(comment_id: comment.id), method: :post %>
As simple as that.
Update: changed the method from new to post to prevent double rendering.