##機能要件
・投稿フォームで任意の文字列を送信すると、タグとして投稿に結び付き保存される
・一度の入力で複数のタグの作成が可能(スペース区切りで入力)
##1.モデル・テーブルの作成
以下3つのモデルを作成
・画像投稿用のモデル(PostImage)
・タグ用のモデル(Tag)
・上記2つの中間テーブル(PostImageTag)
$ rails g model PostImage
$ rails g model Tag
$ rails g model PostImageTag
投稿画像テーブル(post_images)
class CreatePosts < ActiveRecord::Migration[5.0]
def change
create_table :post_images do |t|
t.string :image_id
t.text :caption
t.references :user, foreign_key: true
t.timestamps
end
add_index :post_images,[:user_id,:created_at]
end
end
タグテーブル(tags)
class CreateTags < ActiveRecord::Migration[5.0]
def change
create_table :tags do |t|
t.string :name
t.timestamps
end
add_index :tags, :name, unique:true
end
end
中間テーブル(post_image_tags)
class CreatePostImageTags < ActiveRecord::Migration[5.0]
def change
create_table :post_image_tags do |t|
t.integer :post_image_id
t.integer :tag_id
t.timestamps
end
add_index :post_image_tags, :post_image_id
add_index :post_image_tags, :tag_id
add_index :post_image_tags, [:post_image_id,:tag_id],unique: true
end
end
ファイルの修正が完了したらrails db:migrate
でDBにテーブルを作成
##2.モデルにアソシエーションの記述を行う
class PostImage < ApplicationRecord
:
has_many :tags, through: :post_image_tags
has_many :post_image_tags, dependent: :destroy
:
class Tag < ApplicationRecord
:
validates :name, presence:true, length:{maximum:20}
has_many :post_images, through: :post_image_tags
has_many :post_image_tags, dependent: :destroy
:
class ArticleCategory < ApplicationRecord
belongs_to :post_image
belongs_to :tag
validates :post_image_id, presence:true
validates :tag_id, presence:true
end
##3.モデルにタグの作成・更新のメソッドを定義
:
def save_tags(save_post_image_tags)
# 登録されているタグを取得
current_tags = self.tags.pluck(:name) unless self.tags.nil?
# 古いタグを取得(登録されているタグ - フォームから新規に送られてきたタグ)
old_tags = current_tags - save_post_image_tags
# 新しいタグの取得(フォームから新規に送られてきたタグ - 登録されているタグ)
new_tags = save_post_image_tags - current_tags
old_tags.each do |old_name|
# 古いタグを削除
self.tags.delete Tag.find_by(name: old_name)
end
new_tags.each do |new_name|
# 新しいタグがテーブルに存在しなければ新規登録
post_image_tag = Tag.find_or_create_by(name: new_name)
self.tags << post_image_tag
end
end
:
##4. フォーム画面の作成
フォーム部分を部分テンプレートとして用意し、新規投稿および編集画面にrenderで組み込む。
こちらは部分テンプレートのファイル。
<div>
<%= form_with model: post_image, local:true do |f| %>
<%= render 'layouts/error_messages', model: f.object %>
<div class="image-field">
<%= attachment_image_tag post_image, :post_image, fallback: "no_image.jpg", size:"400x400" %>
<%= f.attachment_field :post_image %>
</div>
<div class="form-group">
<%= f.label :"説明文" %>
<%= f.text_field :caption, autofocus: true %>
</div>
<div class="form-group">
<%= f.label :"タグ" %>
<%= f.text_field :tag, value: tag_list %>
</div>
<div class="submit-btn">
<%= f.submit "Upload" %>
</div>
</div>
##5.コントローラにアクションを定義
###新規投稿機能( new, create )
:
def new
@post_image = PostImage.new
@tag = Tag.new
end
def create
@post_image = current_user.post_images.new(post_image_params)
# フォームから送られてきたタグの値を、空白区切りで分割し、配列として代入
tag_list = params[:post_image][:tag_name].split(/[[:blank:]]/)
if @post_image.save
# save_tagメソッドを呼び出し、フォームの値を引数に渡す
@post_image.save_tags(tag_list)
flash[:notice] = "投稿を作成しました"
redirect_to post_image_path(@post_image)
else
render "new"
end
end
:
private
def post_image_params
params.require(:post_image).permit(:image, :caption)
end
# タグのフォーム入力値に対応するストロングパラメータ
def tag_params
params.require(:post_image).permit(:tag_name)
end
:
コメントで書いた部分の記述をもう少し詳しく説明
こちらの行ではparamsのタグのフォーム入力値に値する部分を、splitメソッド
を用いて空白区切りで分割し、配列にして変数tag_list
に代入
tag_list = params[:post_image][:tag_name].split(/[[:blank:]]/)
splitメソッド
の引数を(/[[:blank:]]/)
にすることで、フォーム内の文字列を全角半角関係なくスペースで区切って配列を作成することができる。
また.split(",")
のようにすれば、カンマ区切りで配列を作成できる。
この場合はユーザーへフォーム入力の際にカンマ区切りで記入するように指示する必要がある。
例えば(※複数のタグを作成する際は、タグをカンマ区切りで入力してください)とか。
###編集機能( edit, update )
def edit
@post_image = PostImage.find(params[:id])
# @post_imageに結びついたタグを取得し、空白区切りで表示
@tag_list = @post_image.tags.pluck(:name).join(" ")
end
def update
@post_image = PostImage.find(params[:id])
tag_list = params[:post_image][:tag_name].split(/[[:blank:]]/)
if @post_image.update(post_image_params)
@post_image.save_tags(tag_list)
flash[:notice] = "投稿を更新しました"
redirect_to post_image_path(@post_image)
else
render "edit"
end
end
updateアクション
での処理は新規作成の際と流れは同じ
editアクション
内でDB内に登録されているタグの取得を行っているこちらの一文についての手順は以下の通り
@tag_list = @post_image.tags.pluck(:name).join(" ")
・pluckメソッド
を使用して@post_image
に紐づいたタグのnameカラム
の値を取得
・joinメソッド
で配列の値を結合して文字列を作成
上記の処理内容をコンソールで見てみるとこんな感じ
$ rails console
> post_image = PostImage.find(1)
> post_image.tags
=>[
#<Tag id: 1, name: "犬", created_at:"~~~", updated_at:"~~~">,
#<Tag id: 2, name: "チワワ", created_at: "~~~", updated_at: "~~~">
]
> post_image.tags.pluck(:name)
=> ["犬", "チワワ"]
> post_image.tags.pluck(:name).join(" ")
=> "犬 チワワ"
以上で実装完了
記載内容に間違いがあった場合はご指摘頂けると嬉しいです。
ご連絡お待ちしております。