Skip to content

Upload files

programrails edited this page Nov 27, 2018 · 1 revision
<%#

# ====================================================
#                    CarrierWave multi-upload partial

# Just put this partial into your 'views/layouts' folder - and use CarrierWave multiple uploads in both edit-show pages:
#
# class FileUpload < ApplicationRecord
#   has_many :images, dependent: :destroy
#   accepts_nested_attributes_for :images, allow_destroy: true
# end

# class Image < ApplicationRecord
#   mount_uploader :file, FileUploader
#   belongs_to :file_upload
# end

<!-- db/migrate/20181126180719_create_images.rb -->
PostgreSQL option: (otherwise use t.json :string)

class CreateImages < ActiveRecord::Migration[5.2]
  def change
    create_table :images do |t|
      t.json :file
      t.references :file_upload, foreign_key: true

      t.timestamps
    end
  end
end

# USAGE:

# Show action:
# <%= render 'layouts/uploaded_files', param: @file_upload %>

<%#
# Edit action:
# <%= render 'layouts/uploaded_files', param: form %>

<%

# These functions definitions are included into this view to make this partial "all-inclusive".

# The idea is that you simply add this partial into your Rails project - and that's it. No more mess.

# Meaning that you get the ability to handle CarrierWave multiple uploads in both edit-show pages for that.


# Including functions definitions into Rails view violates the architectural rules.

# But since image uploading usually happens only in the Admin area, I think that the architecture may be neglected here.

# Anyway if you are such a fan of architecture, you may move these functions definitions into a Rails module/service.


# Beware that CarrierWave multi-upload does NOT allow you to add/delete attachments by ONE (only all of them at once).

# That's why here a helper model is used (containing a single attachement). But you are free to choose whathever name

# for this helper model (not necessarily 'Image' - choose 'Attachment' if you wish), as it's name is auto-detected in this partial.

### ==================== CONFIG ===========================

# Change to 'false' if the uploaded files are not images

  file_is_image = true

  f = param

### ==================== END OF CONFIG ====================

  def klass_name(f)
    object = (f.respond_to? :object) ? f.object : f

    object.class.to_s.underscore
  end

  def assoc_names(f)
    klass = klass_name(f).camelize.constantize

    # https://stackoverflow.com/a/12784574/6594668
    klass.reflections.collect do |_, b| 
      b.class_name.underscore.pluralize if b.macro==:has_many && b.class_name.constantize.uploaders.present?
    end.compact.first
  end

  def assoc_klass(f)
    assoc_names(f).singularize.camelize.constantize
  end

  def helper_by_name(f, image)

    klass = klass_name(f)

    accepts_nested_attributes_for_params =
      {
        "#{assoc_names(f)}_attributes": { id: image.id, _destroy: true }
      }

    url_for(action: 'update', controller: controller_name, id: f.object.id, klass.to_sym => accepts_nested_attributes_for_params)
  end

  def file_url(request, file)
    request.base_url.to_s + file.file.to_s
  end

  def file_names(f)
    klass = klass_name(f)

    "#{klass}[#{assoc_names(f)}_attributes][][file]"
  end
%>

<% if f.class.to_s == "ActionView::Helpers::FormBuilder" %>

    <div class="field">
      <%= f.label "Uploaded file" %><br />

      <% f.object.try(assoc_names(f.object)).each_with_index do |file, index| %>
        <% if file.id %>

          <% if file_is_image %>
            <% url = file_url(request, file) %>
          
            <div class="field">
              <%= image_tag url %>
            </div>
          <% end %>

          <div class="field">
            <%= link_to "Delete", helper_by_name(f, file), method: :patch, data: { confirm: 'Are you sure?' } %>
          </div>
          
        <% end %>
      <% end %>
    </div>

    <%= f.fields_for assoc_names(f).to_sym, assoc_klass(f).new do |ff| %>

      <div class="field">
        <%= ff.label "File" %><br>
        <%= ff.file_field :file, :multiple => true, name: file_names(f) %>
      </div>

    <% end %>

<% else %>

  <strong>Uploaded files:</strong>
  <p>  
    <% f.try(assoc_names(f)).each_with_index do |file, index| %>

      <% if file_is_image %>
        <% url = file_url(request, file) %>

        <div class="field">
          <%= image_tag url %>
        </div>
      <% end %>

      <div class="field">
        id: <%= file.id %> &nbsp;<a href="<%= url %>" target="blank"><%= url %></a>
      </div>

    <% end %>
  </p>

<% end %>

<%# console %>
Clone this wiki locally