環境
- Mac(12.2.1)
- MacBook Pro (13-inch, 2020)
- 2 GHz クアッドコアIntel Core i5
- 16 GB 3733 MHz LPDDR4X
- ruby (3.0.0p0)
- rails (7.0.1)
- mysql2 (0.5.3)
- 使用gem
- devise
- simple_crendar
はじめに
考えたことを実装し動くことを優先に作成したので、読んでもよく分からないところもあるかもしれません
前提知識
- simple_clendar
- pluckメソッド
- strftimeメソッド
参考にした記事
simple_calendar
strftimeについて
配列の扱い方
whereについて
pluckについて
link_toについて
作成イメージ
詳細
カレンダー内には投稿記事が表示され、かつ、投稿可能な記事の数は1個だけ
更に日付はリンクとなっており投稿記事の有無により、リンク先が変更されている機能を作ってみた
具体的には
- 投稿記事をDBに保存し、それをsimple_calendarのカレンダー内に記事を表示
- カレンダー内の動き
- 記事の作成・編集・削除はカレンダーの日付をクリックすると専用のページに移行する
- DB内に投稿記事がない場合:newページのリンクが生成され、リンク先で新規投稿(新規投稿時にカレンダーの日にちと同日の日付が連動して保存)
- DB内に投稿記事がある場合:editページのリンクが生成され、リンク先で投稿編集と削除(編集時は日時変更はされないように設定)
- 記事の作成・編集・削除はカレンダーの日付をクリックすると専用のページに移行する
作成コード一覧
Rails.application.routes.draw do
resources :diaries, only:[:new,:index,:create,:edit,:update,:destroy]
devise_for :users
・
・
・
class DiariesController < ApplicationController
def new
@diary = Diary.new
@diaries = Diary.where(user_id: current_user.id)
end
def index
@diaries = Diary.where(user_id: current_user.id).all
end
def create
@diary = Diary.new(diary_params)
if @diary.save
flash[:notice] = "保存に成功しました"
redirect_to action: :index
else
flash[:alert] = "保存に失敗しました"
render 'new'
end
end
def edit
@diary = Diary.find(params[:id])
end
def update
@diary=Diary.find(params[:id])
if @diary.update(diary_params)
flash[:success] = "保存に成功しました"
redirect_to request.referer
else
flash.now[:alert] = "保存に失敗しました"
render 'new'
end
end
def destroy
@diary=Diary.find(params[:id])
if @diary.destroy
flash[:notice]="削除しました"
redirect_to action: :index
else
flash[:alert] = "保存に失敗しました"
render 'edit'
end
end
private
def diary_params
params.require(:diary).permit(:diary_content_0,:diary_content_1,:diary_content_2,:created_at,:updated_at).merge(user_id: current_user.id)
end
end
<%= month_calendar events: @diaries do |date,diaries| %>
<% a=@diaries.where(created_at: date).pluck(:created_at)%>
<% b=a.map{ |i| i.strftime("%Y-%m-%d")}%>
<%b[0]%>
<%date%>
<% c=@diaries.where(created_at: date).pluck(:id)%>
<!--ここで現在のuser_idの選択したカレンダーの日にちの記事のidを取得している-->
<%c[0]%>
<%if b[0].to_s == date.to_s%>
<%= link_to date.day,edit_diary_path(c,diary_day: date) %>
<%elsif%>
<%b[0].to_s != date.to_s%>
<%=link_to date.day,new_diary_path(current_user,diary_day: date)%>
<%else%>
<%=link_to date.day, edit_diary_path(current_user,diary_day: date)%>
<%end%>
<% diaries.each do |event| %>
<div>
<%= event.diary_content_0 %>
<%= event.diary_content_1 %>
<%= event.diary_content_2 %>
</div>
<% end %>
<% end %>
<%=form_with model: @diary,local: true do |f|%>
<%= f.label :title1 %>
<%=f.text_field :diary_content_0%>
</br>
<%= f.label :title2 %>
<%=f.text_field :diary_content_1%>
</br>
<%= f.label :title3 %>
<%=f.text_field :diary_content_2%>
<% f.label :diary_day %>
<%=f.text_field :created_at,value: (params[:diary_day])%>
</br>
<%=f.submit "投稿する"%>
<%end%>
<%=form_with model: @diary,local: true do |f|%>
<%= f.label :title1 %>
<%=f.text_field :diary_content_0%>
</br>
<%= f.label :title2 %>
<%=f.text_field :diary_content_1%>
</br>
<%= f.label :title3 %>
<%=f.text_field :diary_content_2%>
</br>
<%=f.submit "投稿する"%>
<%end%>
<%=form_with model: @diary, method: :delete,local: true do |f|%>
<%=f.submit "削除する",class: "btn btn-danger"%>
<% end %>
つまづいたところ
主にindex(カレンダー表記部分)に9割ほどつまづいていた
strftimeメソッドが使える対象
strftimeとは時間の表記(フォーマット)を変換するメソッド
日本表記にしたい時や曜日の表記にしたい時などTimeメソッドや Dateメソッドでよく使われるらしい
今回はリンク生成時にnewかeditのどちらを生成するかの条件分岐を書く際の
必要な材料のひとつとして、今回strftimeを使用した
どのように使用したか
今回使用しているgemはsimple_calendarである
このsimple_calendarで取得されている日付は例えば
2022-03-01
といった年月日のみの日付が使われている
<%= month_calendar events: @diaries do |date,diaries| %>
<%date%>
<%end%>
のような日付のフォーマットが表示される
上記のフォーマットを用いて
- カレンダーの日付と登録した記事の日付と比較して日付が同じであればedit
- カレンダーの日付と登録した記事の日付と比較して日付が違うのであればnew
といった条件分岐を作成しようと考えた
そこで投稿記事のテーブルにあるcreated_atカラムを
simple_calendarで使われているyyyy-mm-ddの形式に変換して
条件分岐を作成しようと考えた
現状だとcreated_atカラムを取得するとこのような形となる(投稿した記事の取得したcreated_atカラムの情報)
[Tue, 01 Mar 2022 00:00:00.000000000 JST +09:00]
- 時間の部分が00:00:00.000000000となっているのは記事保存時にsimple_calendarの日時を取得して保存しているため
- 今回で言う
<%= month_calendar events: @diaries do |date,diaries| %> <%date%>
のdateの年月日を取得してきて、それを投稿記事のcreated_atに保存しているため
- 今回で言う
配列の中に上記のフォーマットが取得される
そこでstrftimeメソッドを使用してyyyy-mm-ddのフォーマットに変換しようと考えた
pluckメソッドがどういった動きをしているのか
whereとfind_byを比較したときの動きの違い
試したこと一覧
controllerは下記の設定の上でindex.htmlで試したことを書いていきます
class DiariesController < ApplicationController
・
・
・
def index
@diaries = Diary.where(user_id: current_user.id).all
@test=Diary.pluck(:created_at)
end
whereとfind_byを比較したときの動きの違い
find_byで取得したときの値
<%=@diaries.find_by(created_at: date)%>
#<Diary:0x00007fcd68225a28>
DBから探し出している
whereで取得したときの値
<%= @diaries.where(created_at: date)%>
ActibeRecordを通してからDBから探し出している(値を取り出しているのではなく、探した結果を表示しているだけ)
pluckで取得したときの値
<%= @diaries.pluck(:created_at)%>
該当するcreated_atの値自体を取り出している
試したことを考慮した上での日付の変換したときの失敗例と実例
失敗例1:strftimeの使用時「NoMethodError in Diaries#index」
エラー情報
undefined method `[]' for Tue, 01 Mar 2022 00:00:00.000000000 JST +09:00:ActiveSupport::TimeWithZone
<%=@diaries.where(created_at: date).pluck(:created_at).map { |e| e[0].strftime("%Y-%m-%d")}%>
ここでは配列の中が空であるとのことでした
手順 1. カレンダーの日付(date)の値を用いて、その日付に該当する投稿記事のDBのcreated_atカラムを探し出してみる
<%= @diaries.where(created_at: date).pluck(:created_at)%>
手順 2. 日付を"%Y-%m-%d"表記にしてみる
<% a=@diaries.where(created_at: date).pluck(:created_at)%>
<%= b=a.map{ |i| i.strftime("%Y-%m-%d")}%>
手順 3. 日付を配列から取り出す
<% a=@diaries.where(created_at: date).pluck(:created_at)%>
<% b=a.map{ |i| i.strftime("%Y-%m-%d")}%>
<%=b[0]%>
記事の日付とsimple_calendarの日付が同じ表示になっているかの確認
<%=b[0]%>
<%=date%>
手順.4 条件分岐を書く
・
・
・
<%if b[0].to_s == date.to_s%>
<%= link_to date.day,edit_diary_path(c,diary_day: date) %>
<%elsif%>
<%b[0].to_s != date.to_s%>
<%=link_to date.day,new_diary_path(current_user,diary_day: date)%>
<%else%>
<%=link_to date.day, edit_diary_path(current_user,diary_day: date)%>
<%end%>
・
・
・
to_sメソッドで文字列に直してから比較してみました
結果
上記のように条件分岐によって生成したリンクの作成ができました
おわりに
もっといい方法があるかもしれません
もしいい方法があればご助言をいただけると幸いです