Active Storageで複数画像の投稿・削除


#環境
Ruby 2.5.1
Rails 5.2.4.3

#やりたい事
Rails標準のファイル管理機能Active Storageを使い、画像を複数を投稿・削除できる機能を実装したい。

###まずアプリを作成
```
$ rails new sample_app
$ cd sample_app 
$ bin/rails db:create
```

別ターミナルで

```
$ bin/rails s
```
http://localhost:3000 にアクセスして馴染みの画像が出ていればOK。

<img width="1278" alt="スクリーンショット 2020-07-26 21.55.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606118/bc39ab62-bfce-b612-0899-5cece97a17d4.png">


###modelとcontrollerを作成
今回はpostという名前で。

```
$ bin/rails g model post name:string 
$ bin/rails db:migrate
$ bin/rails g controller posts
```
###ルーティング
```ruby:config/routes.rb
Rails.application.routes.draw do
  resources :posts
end
```
###Active Storageをインストール
```
$ bin/rails active_storage:install
Copied migration 20200726095142_create_active_storage_tables.active_storage.rb from active_storage
```

以下の２つのテーブルを作成するマイグレーションファイルが作成される。
・active_storage_attachments
・active_storage_blobs
migrateしてテーブルを作成。

```
$ bin/rails db:migrate
== 20200726095142 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs)
   -> 0.0020s
-- create_table(:active_storage_attachments)
   -> 0.0019s
== 20200726095142 CreateActiveStorageTables: migrated (0.0041s) ===============
```

モデルと関連付けて使えるようにする

```ruby:models/posts.rb
class Post < ApplicationRecord
  has_many_attached :images
end
```

###controllerを記述
```ruby:controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def new
    @post = Post.new
  end

  def create
    @post = Post.new(post_params)

    if @post.save
      flash[:success] = "作成しました"
      redirect_to posts_path
    else
      render :new
    end
  end

  def destroy
    @post = Post.find(params[:id])
    @post.destroy
    flash[:success] = "作成しました"
    redirect_to posts_path
  end

  private

  def post_params
    params.require(:post).permit(:name, images: [])
  end
end
```

###viewを作成
投稿一覧ページ

```erb:posts/index.html.erb
<h1>投稿一覧</h1>
<%= link_to '新規投稿', new_post_path %>
<%= render @posts %>
```

今回はpostのpartialを作って表示させる。

```erb:posts/_post.html.erb
<div class="post-partial">
  <li id="post-<%= post.id %>">
    <%= post.name %>
    <% post.images.each do |image| %>
      <%= image_tag(image, width:100) %>
    <% end %>
    <%= link_to '削除', post, method: :delete, data: { confirm: '削除してよろしいですか?' } %>
  </li>
</div>
```

###新規作成ページ
画像を複数投稿する場合はmultiple: trueが必要

```erb:posts/new.html.erb
<%= form_with(model: @post, local: true) do |f| %>
  <div>
    <%= f.label :name, '名前' %>
    <%= f.text_field :title %>
  </div>

  <div>
    <%= f.label :images, '画像' %>
    <%= f.file_field :images, multiple: true %>
  </div>

  <div>
    <%= f.submit  '投稿する' %>
  </div>
<% end %>
```

###ブラウザから http://localhost:3000/posts にアクセス

新規投稿から画像を複数投稿する事ができた。

<img width="643" alt="スクリーンショット 2020-07-26 20.51.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606118/a921a6ea-9945-02dc-f02d-e5aae2df51ae.png">

##任意の画像を削除する

###controllerにメソッド追記
編集に必要なedit、updateメソッドを追記します。

```ruby:controllers/posts_controllers.rb
  def edit
    @post = Post.find(params[:id])
  end

  def update
    post = Post.find(params[:id])
    if params[:post][:image_ids]
      params[:post][:image_ids].each do |image_id|
        image = post.images.find(image_id)
        image.purge
      end
    end
    if post.update_attributes(post_params)
      flash[:success] = "編集しました"
      redirect_to posts_url
    else
      render :edit
    end
  end
```
###投稿編集ページの作成
新規投稿ページとほぼ一緒だが、登録されている画像を表示しチェックを入れる部分を追記。

```erb:posts/edit.html.erb
<%= form_with(model: @post, local: true) do |f| %>
  <div>
    <%= f.label :name, '名前' %>
    <%= f.text_field :name %>
  </div>

  <div>
    <%= f.label :images, '画像' %>
    <%= f.file_field :images, multiple: true %>
  </div>

  <% if @post.images.present? %>
    <p>現在登録されている画像（削除するものはチェックしてください）</p>
    <% @post.images.each do |image| %>
      <%= f.check_box :image_ids, {multiple: true}, image.id, false %>
      <%= image_tag image, size:"100x100" %> <br>
    <% end %>
  <% end %>

  <div>
    <%= f.submit  '編集する' %>
  </div>
<% end %>
```

###indexに編集リンクを追記
```erb:posts/index.html.erb
<div class="post-partial">
  <li id="post-<%= post.id %>">
    <%= post.name %>
    <% post.images.each do |image| %>
      <%= image_tag(image, width:100) %>
    <% end %>
    <%= link_to '編集', edit_post_path(post.id) %> 　#追加
    <%= link_to '削除', post, method: :delete, data: { confirm: '削除してよろしいですか?' } %>
  </li>
</div>
```

###確認
ブラウザから編集のリンクをクリックし、削除したい画像にだけチェックを入れる。
<img width="646" alt="スクリーンショット 2020-07-26 21.35.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606118/a3f45adc-05b8-2d86-ec91-d8bd20376a59.png">

任意の画像だけ削除できました。

<img width="638" alt="スクリーンショット 2020-07-26 21.36.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606118/677096af-5c03-e96c-0edd-e7e775594124.png">

#参考
[ActiveStorageを使って複数画像管理をしてみる](https://qiita.com/me-654393/items/3b259bbc70f62898bd6f)
[ActiveStorage で画像を複数枚削除する方法](https://h-piiice16.hatenablog.com/entry/2018/09/24/141510)