エラーメッセージを日本語で表示

1. error messageを日本語にする

  • gem 'rails-i18n'をgemfileに追記する
    コンソールでbundle install実行

  • application.rbに追記

//jaの記述でja.ymlが読み込める
config.i18n.default_locale = :ja 
//localesディレクトリ配下のファイル読み込む
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]
  • エラーメッセージがname属性以外日本語になる

2. name属性も日本語にする

//サンプル
ja:
  activerecord:
    attributes:
      post:
        title: 'タイトル'
        article_text: '記事テキスト'
        status_id: '公開/非公開'
        category_id: 'カテゴリー'
        images: '画像'
      user:
        nickname: 'ニックネーム'
        last_name: '苗字'
        first_name: '名前'
        profile: 'プロフィール'

rails cでテスト

 pry(main)> I18n.t("activerecord.attributes.user.nickname")
=> "ニックネーム"
pry(main)> I18n.t("activerecord.attributes.post") 
=> {:title=>"タイトル", :article_text=>"記事テキスト", :status_id=>"公開/非公開", :category_id=>"カテゴリー", :images=>"画像"}

実装結果 gyazo.com

SQLで中間テーブルを含めて3つのテーブルを結合してみた

今回結合したのは、
Posts、Tags、PostTagRelationsの3つのテーブル

最終目的:Tagsテーブルのnameカラムのvalueの値

3つのテーブル結合した図
select * の部分*(ワイルドカード” * ”を指定)で全てを指定している gyazo.com

カラムをPosts.nameとTags.name指定してみた図
だいぶすっきり
gyazo.com

実際には、select t.name form~だけでいいのでさらに絞ってます

rails cで見てみる

  1. 実際に値を取得し、変数sqlに代入
  2. モデル.find_by_sql(変数)で出力する =>配列になってる gyazo.com
  3. 変数に手順2のfind_by_sqlを代入する
  4. 変数[0].カラム名で出力する =>今回は,@tag_name[0].name

gyazo.com

posts.controller.rb

  def edit
    @post = Post.find(params[:id])
    //@form = PostsTag.new(title: @post.title, article_text: @post.article_text, status_id: 
    //@post.status_id, category_id: @post.category_id, user_id: current_user.id, post_id: 
    @post.id,image: @post.image)
    sql = "SELECT t.name FROM posts p JOIN post_tag_relations ptr ON p.id = ptr.post_id JOIN tags t ON ptr.tag_id = t.id WHERE p.id = #{@post.id}"
    Tag.find_by_sql(sql)
  end


text_fildeにvalue: @tag_name[0].nameで表示させました
edit.html

<%= form_with model: @form, url: post_path, method: :put, local: true do |f| %>

      <div class="tag-field", id='tag-field'>
        <%= f.label :name, "タグ" %><br />
        <%= f.text_field :name, value: @tag_name[0].name,class:"input-tag" %>
      </div>
      <div id="search-result">
      </div>

<% end %>

参考: Rails find_by_sqlでSQLクエリを実行してみた - Qiita

データが1件でもあるかチェックする

exists?

指定した条件のレコードがデータベースに存在するかどうかを真偽値で返すメソッドです。存在すればtrueを存在しなければfalseを返します。

1件でもあればtrueを返す

オブジェクト.exists?(条件)  

例:

 > User.exists?
  User Exists (3.0ms)  SELECT  1 AS one FROM "users" LIMIT $1  [["LIMIT", 1]]
=> true

条件つき

User.exists?(id: [1, 2, 3], name: 'opiyo', role: 'admin')
今回の自身のコード

今回はusers#indexで条件付きでexists?の返り値を@post_userに代入し、viewのif分岐の条件に記述しました
* 条件分岐

<if ログインしているか>

  <if ログインしたユーザーの投稿があるか>
     <if  ログインしたユーザーの投稿>
      //ユーザーの投稿内容のみ表示
  <end>
  <else>
         //ログインユーザーの投稿がない
       hogeさんの投稿がありません
         //全てのユーザーの投稿内容表示
  <end>

<else>
     ログインしてません
    //全てのユーザーの投稿内容表示
<end>

実際のコード

<% if user_signed_in? %>
    <% if @post_user %> //コントローラからtrueまたはfalseを受け取る
      <% @posts.each do |post| %>
          <% if post.user_id == current_user.id %>
            
            <div class="post_wrap">
              <div class="card">
                <div class="article_card">
                  <%= link_to "タイトル:「#{post.title}", post_path(post.id), class: :card__title %>
                  
                  <ul class="tag">
                    <li class="tag_ribbon">
                      <%post.tags.each do |tag| %>
                        #<%=tag.name%>
                      <% end %>
                    </li>
                  </ul>
                  <div class="status">
                    <%= post.status.name%>
                  </div>

                  <%= link_to image_tag(post.image, class: :card__img), post_path(post.id) %>
                  <div class="date">
                    <div class="image-month">12</div>
                    <div class="image-year">2020</div>
                  </div>
                </div>
              </div>
            </div>
          <% end %>
        <% end %>
        
    <% else %>
      <p>まだ投稿していません</p>
        <h1>みんなの投稿</h1>
    <% end %>  
  <% else %>
    <%# ログインしていない %>
    <h1>みんなの投稿</h1>
  <% end %>
  • コントローラー
//users_controller
class UsersController < ApplicationController

  def index
    @posts = Post.all.order(created_at: :desc)
    @post_user = Post.exists?(user_id: current_user.id) //trueまたはfalseを代入
  end

end

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update]

//-----------------------------------------------------------

//users_controller
  def index
    @posts = Post.all.order(created_at: :desc)
  end
end

【Rails】exists?メソッドの使い方(基礎~応用編,prese | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

gemを使わない tag機能の実装

詳しく後日編集します

ポイント

  • 中間テーブル

  • 複数のテーブルを同時にcreateするモデルを使用

  • jsでajax通信

  • updateする

model

アソシエーションの記述
model/post.rb

class Post < ApplicationRecord
  belongs_to :user
  has_many :post_tag_relations, dependent: :destroy
  has_many :tags, through: :post_tag_relations
  has_one_attached :image

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :status
end

model/post.rb

class Tag < ApplicationRecord
  has_many :post_tag_relations, dependent: :destroy
  has_many :posts, through: :post_tag_relations
  //一意性のバリデーションはここに記述
  validates :name, uniqueness: true
end

model/post_tag_relation.rb

class PostTagRelation < ApplicationRecord
  belongs_to :post
  belongs_to :tag
end

model/posts_tag.rb
ここに同時保存の内容を記述(マイグレーションファイルはいらないので、rails gで作成しないで手作業で生成する)

class PostsTag
  include ActiveModel::Model
  attr_accessor :title, :article_text, :status_id, :category_id, :image, :name, :user_id, :post_id

  with_options presence: true do
    validates :title        , length: {maximum: 20}
    validates :article_text , length: {maximum: 300}
    validates :status_id    , numericality: {other_than: 1, message: "は--以外から選んでください"}
    validates :category_id    , numericality: {other_than: 1, message: "は--以外から選んでください"}
    validates :name
    validates :image
  end

  def save
    post = Post.create(title: title, article_text: article_text, status_id: status_id, category_id: category_id, image: image, user_id: user_id)
    tag = Tag.where(name: name).first_or_initialize
    tag.save

    PostTagRelation.create(post_id: post.id, tag_id: tag.id)
  end

  def update
    binding.pry
    @post = Post.where(id: post_id)
    post = @post.update(title: title, article_text: article_text, status_id: status_id, category_id: category_id, image: image, user_id: user_id)
    tag = Tag.where(name: name).first_or_initialize
    tag.save

    map = PostTagRelation.where(post_id: post_id)
    map.update(post_id: post_id, tag_id: tag.id)
  end
end

コントローラー

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update]

  def index
    @posts = Post.all.order(created_at: :desc)
  end

  def new
    @post = PostsTag.new
  end

  def create
    @post = PostsTag.new(post_params)
    if @post.valid?
      @post.save
      return redirect_to root_path
    else
      render :new
    end
  end

  def edit
    @form = PostsTag.new(title: @post.title, article_text: @post.article_text, status_id: @post.status_id, category_id: @post.category_id, user_id: current_user.id, post_id: @post.id,image: @post.image)
  end

  def update

    @form = PostsTag.new(update_params)
    if @form.valid?
      @form.update
      redirect_to root_path
    else
      render :edit
    end
  end

  def tag_search
    return nil if params[:keyword] == ""
    tag = Tag.where(['name LIKE ?',"%#{params[:keyword]}%"] )
    render json:{ keyword: tag }
  end

  private

  def post_params
    params.require(:posts_tag).permit(:title, :article_text, :status_id, :category_id, :image, :name).merge(user_id: current_user.id)
  end

  def update_params
    params.require(:posts_tag).permit(:title, :article_text, :status_id, :category_id, :image, :name).merge(user_id: current_user.id, post_id: params[:id])
  end

  def set_post
    @post = Post.find(params[:id])
  end
end

PostsTagで同時保存

class PostsTag
  include ActiveModel::Model
  attr_accessor :title, :article_text, :status_id, :category_id, :image, :name, :user_id, :post_id

  with_options presence: true do
    validates :title        , length: {maximum: 20}
    validates :article_text , length: {maximum: 300}
    validates :status_id    , numericality: {other_than: 1, message: "は--以外から選んでください"}
    validates :category_id    , numericality: {other_than: 1, message: "は--以外から選んでください"}
    validates :name
    validates :image
  end

  def save
    post = Post.create(title: title, article_text: article_text, status_id: status_id, category_id: category_id, image: image, user_id: user_id)
    tag = Tag.where(name: name).first_or_initialize
    tag.save

    PostTagRelation.create(post_id: post.id, tag_id: tag.id)
  end

  def update
    binding.pry
    @post = Post.where(id: post_id)
    post = @post.update(title: title, article_text: article_text, status_id: status_id, category_id: category_id, image: image, user_id: user_id)
    tag = Tag.where(name: name).first_or_initialize
    tag.save

    map = PostTagRelation.where(post_id: post_id)
    map.update(post_id: post_id, tag_id: tag.id)
  end
end

view(posts/new.html)

※ edit.htmlには@formを渡してます

<%= form_with model: @post, url: posts_path, local: true do |f|%>
      <%= render 'shared/error_messages', model: f.object %>

      <div class="field">
        <%= f.label :status_id, "公開/非公開 必須" %><br />
        <%= f.collection_select(:status_id, Status.all, :id, :name, {class:"genre-select"}) %>
      </div>

      <div class="field">
        <%= f.label :category_id, "カテゴリー 必須" %><br />
        <%= f.collection_select(:category_id, Category.all, :id, :name, {class:"genre-select"}) %>
      </div>

      <div class="tag-field", id='tag-field'>
        <%= f.label :name, "タグ" %>
        <%= f.text_field :name, class:"input-tag" %>
      </div>
      <div id="search-result">
      </div>

      <div class="field">
        <%= f.label :title, "記事タイトル" %><br />
        <%= f.text_field :title, id:"article_title" %>
      </div>

      <div class="field">
        <%= f.label :image, "投稿画像" %><br />
        <%= f.file_field :image, id:"post_image" %>
      </div>

      <div class="field">
        <%= f.label :article_text, "記事テキスト" %><br />
        <%= f.text_area :article_text, class: :form__text, id:"prototype_catch_copy" %>
      </div>

      <div class="actions">
        <%= f.submit "保存する", class: :form__btn  %>
      </div>
    <% end %>

参考: railsでタグ付け機能を実装する方法(投稿から編集まで

where likeであいまい検索!

LIKEによるあいまい検索とは

SQLにはLIKE句を使ったあいまい検索の構文があります。

〜 WHERE 列名 LIKE '%検索値%'
〜 WHERE 列名 LIKE '検索値_'

モデルクラス.where("列名 LIKE ?", "%値%")  # 値(文字列)を含む  
モデルクラス.where("列名 LIKE ?", "値_")   # 値(文字列)と末尾の1文字

「?」はプレースホルダと言うもので、第2引数の値を「?」へ置き換えるための目印です。

検索する際には、前方または後方指定可能

  • 前方一致(から始まる):検索値%

  • 後方一致(で終わる):%検索値

f:id:fujifuji123412:20210327172906p:plain
tag
  

www.sejuku.net

form_withでurlとmodelを同時に記述する理由

  • 入力された情報をデータベースに保存しない時の記述です
<%= form_with url: "パス" do |form| %>
  フォーム内容
<% end %>
  • 入力された情報をデータベースに保存する時の記述です
<%= form_with model: モデルクラスのインスタンス do |form| %>
  フォーム内容
<% end %>
urlとmodelを同時に記述する

以下は、url: donations_pathという記述で送信先のpathを明示する必要があります。urlオプションでpathを明示しないと、form_withはmodelオプションで渡されたモデル名をもとに自動的にpathを判断してしまうためです。

<%= form_with(model: @user, url: donations_path, local: true) do |form| %>
  <h1>ユーザー名を入力してください</h1>

  <div class="field">
    <%= form.label :name, "名前(全角)" %>
    <%= form.text_field :name %>
  </div>

 <div class="field">
    <%= form.label :nickname, "ニックネーム(半角英数)" %>
    <%= form.text_field :nickname %>
  </div>

  <div class="actions">
    <%= form.submit "寄付する" %>
  </div>

<% end %>

pikawaka.com

railsで特定のマイグレーションファイルをロールバックする

サンプル

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20210317022552  Devise create users
   up     20210317030520  Create sns credentials
  down    20210318031327  Create posts
  down    20210318044136  Create active storage tablesactive storage
  down    20210326063233  Create tags
  down    20210326063303  Create post tag relations

コマンド

// マイグレーションファイルの状態を確認                                  
%  bundle exec rake db:migrate:status   

// upのファイルをdownにし、再編集できる
bundle exec rake db:migrate:down VERSION='upのマイグレーショID' 

rails db:rollbackだと1つまえのファイルしか戻せないのでだいぶ前のファイルは上記の方がいいと思います