概要
備忘録としてインスタやTwitterでよく見かける#ハッシュタグの実装方法を投稿します
参照
以下の記事を参考にしました。
ほとんど同じですが、上手く行かなかったところを自分なりに組み合わせました。
https://qiita.com/Naoki1126/items/4ea584a4c149d3ad5472
https://qiita.com/MitsuTurtle/items/614b154641ab4e4b56ba
https://glodia.jp/blog/3936/
完成図
アプリの作成
ターミナル
$cd desktop
$rails new hashtag_sample
$cd hashtag_sample
DBの作成
DB(データベース)を作成していない人は作成しましょう!
作成済みの人は無視して次のモデルの作成に進みましょう!
ターミナル
$rails db:create
#モデルの作成
ターミナル
$rails g model Post body:text hashbody:text
$rails g model Hashtag hashname:string
$rails g model HashtagPost post:references hashtag:references #中間テーブル
コントローラーの作成
ターミナル
$rails g controller posts
マイグレーションファイルの編集
db/migrate/create_hashtags.rb
class CreateHashtags < ActiveRecord::Migration[6.1]
def change
create_table :hashtags do |t|
t.string :hashname
t.timestamps
end
add_index :hashtags, :hashname, unique: true
end
end
ここでマイグレーション
ターミナル
$rails db:migrate
モデルのアソシエーションを記述
model/hashtag.rb
class Hashtag < ApplicationRecord
validates :hashname, presence: true, length: { maximum: 50 }
has_many :hashtag_posts, dependent: :destroy
has_many :posts, through: :hashtag_posts
end
model/hashtag_post.rb
class HashtagPost < ApplicationRecord
belongs_to :post
belongs_to :hashtag
validates :post_id, presence: true
validates :hashtag_id, presence: true
end
model/post.rb
class Post < ApplicationRecord
has_many :hashtag_posts, dependent: :destroy
has_many :hashtags, through: :hashtag_posts
after_create do
post = Post.find_by(id: id)
# hashbodyに打ち込まれたハッシュタグを検出
hashtags = hashbody.scan(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/)
hashtags.uniq.map do |hashtag|
# ハッシュタグは先頭の#を外した上で保存
tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
post.hashtags << tag
end
end
#更新アクション
before_update do
post = Post.find_by(id: id)
post.hashtags.clear
hashtags = hashbody.scan(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/)
hashtags.uniq.map do |hashtag|
tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
post.hashtags << tag
end
end
end
ルーティングについて
config/routes.rrb
Rails.application.routes.draw do
resources :posts
root'posts#index'
get '/post/hashtag/:name' => 'posts#hashtag'
get '/post/hashtag' => 'posts#hashtag'
end
ヘルパーの編集
helpers/posts_helper.rb
module PostsHelper
def render_with_hashtags(hashbody)
hashbody.gsub(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/) { |word| link_to word, "/post/hashtag/#{word.delete("#")}",data: {"turbolinks" => false} }.html_safe
end
end
post_controller
controllers/post_controller.rb
class PostsController < ApplicationController
def hashtag
if params[:name].nil?
@hashtags = Hashtag.all.to_a.group_by{ |hashtag| hashtag.posts.count}
else
name = params[:name]
name = name.downcase
@hashtag = Hashtag.find_by(hashname: name)
@post = @hashtag.posts
@hashtags = Hashtag.all.to_a.group_by{ |hashtag| hashtag.posts.count}
end
end
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def new
@postnew = Post.new
end
def create
@postnew = Post.new(post_params)
if @postnew.save
redirect_to posts_path
else
render('posts/new')
end
end
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post, notice: "Post was successfully updated." }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def destroy
@post = Post.find(params[:id])
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:body, :hashbody, :user_id, postss: [], hashtag_ids: [])
end
end
Viewの編集
まずはapp/view/postsに
・index.html.erb
・new.html.erb
・hashtag.html.erb
・show.html.erbを作成!
投稿フォーム
views/posts/new.html.erb
<div class= "row">
<div class="col-lg-2 col-md-2">
</div>
<div class="col-xs-12 col-lg-8 col-md-8 col-sm-12">
<div class= "post-new-box">
<% if @postnew.errors.any? %>
<div id="error_explanation">
<h3><%= @postnew.errors.count %>件の入力エラーにより、投稿出来ませんでした</h3>
<ul>
<% @postnew.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<h3>新規投稿</h3>
<div class="previw">
</div>
<%= form_for @postnew, url: posts_path do |f| %>
<br>
<div class= "post-body-box">
<p>投稿の詳細を入力してください</p>
<%= f.text_area :body, size:"55x3" %>
<br>
<p>ハッシュタグ入力欄</p>
<%= f.text_area :hashbody, size:"55x3" %>
<br>
<div class= "post-new">
<%= f.submit "新規投稿" ,class:'post-new-button' %>
</div>
<% end %>
</div>
</div>
</div>
<div class="col-lg-2 col-md-2">
</div>
</div>
<%= link_to 'Back', posts_path %>
詳細ページ
views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Body:</strong>
<%= @post.body %>
</p>
<p>
<strong>Hashbody:</strong>
<%= render_with_hashtags(@post.hashbody) %>
</p>
<%= link_to 'Back', posts_path %>
ハッシュタグ
views/posts/hashtag.html.erb
<h2>ハッシュタグ #<%= @hashtag.hashname %></h2>
<div class= "hashtag-name">
<% @hashtags.sort.reverse.each do |count| %>
<% count[1].each do |hashtag| %>
<p><%= link_to "##{hashtag.hashname} (#{hashtag.posts.count}) 件","/post/hashtag/#{hashtag.hashname}",data: {"turbolinks" => false} %>
</p>
<% end %>
<% end %>
</div>
<ul>
<% @post.each do |t| %>
<li>
<%= t.body %>
</li>
<% end %>
</ul>
<%= link_to 'Back', posts_path %>
投稿一覧
views/posts/index.html.erb
<p id="notice"><%= notice %></p>
<h1>投稿一覧</h1>
<table>
<thead>
<tr>
<th>Body</th>
<th>Hashbody</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= render_with_hashtags(post.hashbody) %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_post_path %>
完成!
お疲れ様でした!