自宅のPCがGTX-750Tiを積んでいるので機械学習を!と、Ubuntuを入れたもののNVIDIAドライバーがインストールできず、かつ、端末も使いにくく悪戦苦闘している私です。。トホホ…
そんな曇天な状況ですが沖縄が梅雨明けになるなど確実に今年も熱い季節が間近に迫っております。
詳しい全体像はまたの機会に話しますが、研修ではインスタグラムライクなものを作りました。
追加機能とはなりますが数多の機能の中でもとりわけこだわったハッシュタグ機能について、
その作り方を今回は書いていきます。
完成した姿
先に完成状態を示します。
仕組みとしては以下の図のイメージです。
(1)投稿時にハッシュ記号と一緒に入力
(2)投稿したキャプションはそのままDBに保存
(3)保存直後に”#”~半角スペースまでを単位として読み取り、hashtagテーブルに保存
(4)viewの呼び出し時にハッシュタグ部分をリンク化
(5)ハッシュタグをクリックすることでハッシュタグページに遷移
ハッシュタグの考え方
ハッシュタグは一つの投稿(micropost)に対して複数つけることができます。
これを視点別に見るとすれば、
・micropost的視点:一つのmicropostに複数のhashtagを持つ
・hashtag的視点 :一つのhashtagは複数のmicropostに属する
といえます。
この関係から以下の図のように多対多の関係となります。
したがって必要となるのは中間テーブルです。
以下ではこられを追加していきます。
各種テーブルの準備
hashtagテーブルの用意
新たにhashtagテーブルを用意します。
1 |
$ rails g model Hashtag hashname:string |
Railsではお作法として、モデル名は単数形、コントローラーは複数形で記述する規約があります。
このとき、カラム(hashname)をstring型で作ります。
hashtagマイグレーションファイルの編集
1 2 3 4 5 6 7 8 9 |
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 |
中間テーブルの作成
1 |
$rails g migration CreateMicropostHashtags micropost_id:references, hashtag_id:references |
referencesを使うことで外部キーをのカラムを生成できます。
中間テーブルのマイグレーションファイル編集
1 2 3 4 5 6 7 8 |
class CreateMicropostHashtags < ActiveRecord::Migration[5.2] def change create_table :hashtags_microposts, id: false do |t| t.reference :micropost_id, index: true, foreign_key: true t.reference :hashtag_id, index: true, foreign_key: true end end end |
t.referenceはあくまで外部キー制約をつけられるようになったというだけです。
言い換えればt.referencesの記述があることでforeigh_key:trueを使用することができます。
これにより外部キーが有効になります。
レッツマイグレート
1 |
$ rails db:migrate |
モデルの編集
micropostモデルに下記を追記
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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 |
ここではrailsのコールバック機能を使ってDB保存直後に該当文字列を抽出してhashtagテーブルに保存するようにしています。
今回のキャプションは編集不可なのでbefore_updateは使わないもの、after_createとほぼ同じなので記述しています。
中間テーブルモデル
1 2 3 4 5 6 |
class MicropostHashtag < ApplicationRecord belongs_to :micropost belongs_to :Hashtag validates :micropost_id, presence: true validates :Hashtag_id, presence: true end |
中間テーブルに属するのはmicropostとHashtagモデルですので両者の名前をbelongs_toで書きます。
ちなみにhas_manyするときは複数形、belongs_toされるときは単数形で書くのが常だそうです。
hashtagモデル
1 2 3 4 |
class Hashtag < ApplicationRecord validates :hashname, presence: true, length: {maximum:99} has_and_belongs_to_many :microposts end |
hashtagとして記述できる文字列:hashnameは本物のインスタグラムと同じ99字を登録の上限としています。
has_and_belongs_to_manyは多対多で紐付けるためのもので、ここではmicropostsと紐づけています。
コントローラーの編集
micropostコントローラーにhashtagのアクション記述
1 2 3 4 5 6 7 8 |
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 |
ヘルパーの編集
micropostヘルパーの編集
1 2 3 4 5 |
module MicropostsHelper def render_with_hashtags(content) content.gsub(/[##][\w\p{Han}ぁ-ヶヲ-゚ー]+/){|word| link_to word, "/post/hashtag/#{word.delete("#")}"}.html_safe end end |
メソッドを定義しています。
キャプション内部のハッシュタグ記号を読み取りリンク化します。
またリンク化されたものはそのhashname名を末尾にとったURLを生成し、hashname専用のページに遷移します。
ルーティング
1 |
get '/post/hashtag/:name', to: "microposts#hashtag" |
hashnameを末尾にとったURLに行くようにしています。
Viewの編集
キャプションの追加(micropostパーシャルに)
1 |
<%= render_with_hashtags(micropost.content) %> |
ヘルパーで作ったメソッドと使い、キャプションに対してリンク化をするように仕掛けます。
という感じでやっていくとハッシュタグの実装ができます。
お時間あるときにどうぞチャレンジください。