LoginSignup
6
2

More than 1 year has passed since last update.

【Rails】ハッシュタグ機能の実装

Last updated at Posted at 2021-12-23

概要

備忘録としてインスタやTwitterでよく見かける#ハッシュタグの実装方法を投稿します

参照

以下の記事を参考にしました。
ほとんど同じですが、上手く行かなかったところを自分なりに組み合わせました。
https://qiita.com/Naoki1126/items/4ea584a4c149d3ad5472
https://qiita.com/MitsuTurtle/items/614b154641ab4e4b56ba
https://glodia.jp/blog/3936/

完成図

hashtag_video2.gif

アプリの作成

ターミナル
$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 %>

完成!
お疲れ様でした!

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2