概要
Rails5でインスタもどきを作ってみたものの肝心の検索とハッシュタグが無いという未完成なものとなっていた。始業前の空き時間を使ってハッシュタグの実装をしたのでコードを貼ってみる。
既存(Tutorial)に加えて準備する必要があるもの
・hashtag用のテーブル
・hashtagとmicropostの中間テーブル
完成した姿
・#記号を判断して次のスペースまでの文字列をハッシュタグとして抽出しDBに保存
・ハイパーリンク化されており、クリックするとハッシュタグのページになる
テーブル準備
ハッシュタグモデルの作成
$ rails g model Hashtag hashname:string
マイグレーションファイル編集
/db/migrate/...create_hashtags.rb
class CreateHashtags < ActiveRecord::Migration[5.2]
def change
create_table :hashtags do |t|
t.string :hashname
t.timestamps
end
add_index :hashtags, :hashname, unique: true
end
end
中間テーブルの作成
$rails g migration CreateMicropostHashtags micropost_id:references hashtag_id:references
マイグレーションファイル編集
/db/migrate/...create_micropost_hashtags.rb
class CreateMicropostHashtags < ActiveRecord::Migration[5.2]
def change
create_table :hashtags_microposts, id: false do |t|
t.references :micropost_id, index: true, foreign_key: true
t.references :hashtag_id, index: true, foreign_key: true
end
end
end
マイグレーション
$rails db:migrate
モデルの編集
マイクロポストモデルに下記を追加
/app/models/micropost.rb
has_and_belongs_to_many :hashtags
.
.
.
#DBへのコミット直前に実施する
after_create do
micropost = Micropost.find_by(id: self.id)
hashtags = self.content.scan(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/)
hashtags.uniq.map do |hashtag|
#ハッシュタグは先頭の'#'を外した上で保存
tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
micropost.hashtags << tag
end
end
before_update do
micropost = Micropost.find_by(id: self.id)
micropost.hashtags.clear
hashtags = self.content.scan(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/)
hashtags.uniq.map do |hashtag|
tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
micropost.hashtags << tag
end
end
中間テーブルモデル
/app/models/hashtag_micropost.rb
class MicropostHashtag < ApplicationRecord
belongs_to :micropost
belongs_to :Hashtag
validates :micropost_id, presence: true
validates :Hashtag_id, presence: true
end
ハッシュタグモデル
/app/models/hashtag.rb
class Hashtag < ApplicationRecord
validates :hashname, presence: true, length: {maximum:99}
has_and_belongs_to_many :microposts
end
マイクロポストコントローラーにハッシュタグのアクションを追加
/app/controllers/microposts_controller.rb
def hashtag
@user = current_user
@tag = Hashtag.find_by(hashname: params[:name])
@microposts = @tag.microposts.build
@micropost = @tag.microposts.page(params[:page])
@comment = Comment.new
@comments = @microposts.comments
end
ここは少しビューの仕様が違うので人によっては不要になる宣言があるかもしれません
マイクロポストヘルパーの編集
/app/helpers/microposts_helper.rb
module MicropostsHelper
def render_with_hashtags(content)
content.gsub(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/){|word| link_to word, "/post/hashtag/#{word.delete("#")}"}.html_safe
end
end
せっかく全角にも対応しようとしたものの、deleteメソッドで複数文字を指定するやり方が分からず断念。。。
ルーティング
/config/routes.rb
get '/post/hashtag/:name', to: "microposts#hashtag"
ビュー
キャプション(ハッシュタグがユーザーによって書かれるところ)
/app/views/microposts/_micropost.html.erb
<%= render_with_hashtags(micropost.content) %>
ページ遷移後のハッシュタグページ
/app/views/microposts/hashtag.html.erb
<% provide(:title, @tag.hashname) %>
<div class="row">
<div class="col-md-9">
<div class="block1_hash">
<%= image_tag("hashtag.png") %>
<div class="block2_hash">
<div class="name_hash">
<%= @tag.hashname %>
</div>
<div class="edit_hash">
<%= render 'users/follow_form' %>
</div>
<div class="fllw_hash">
<%= render 'shared/user_info_default' %>
<span style="margin-right:15px;"></span>
<%= render 'shared/stats_default' %>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="posted_content">
<div class="col-md-8">
<div class="picture_hash">
<%= render @micropost %>
</div><!-- .picture -->
</div>
</div>
</div>
基本的にはユーザーページと同じデザインです。
参考VTR
Ruby on Rails - adding hashtags
今月の社内プレゼンのネタができてよかったです。。。
この調子でがんばります。
編集箇所やアドバイス等ありましたらお気軽にお声がけくださいm(_ _)m