地域防災サイトを作成する記録（第３回）

# 今回実装する機能
投稿機能
目的: ユーザーがリアルタイムで災害状況を共有できる。

# 1.投稿モデルの作成
投稿に関連するモデルを生成します。
```ruby: bash
rails g scaffold Post title:string content:text image:string location:string user:references
```
生成されたマイグレーションファイルを確認し、必要であれば修正してからマイグレーションを実行します。
```ruby: bash
rails db:migrate
```
# 2. 投稿とユーザーの関連付け
`Post`モデルにユーザーとの関連を設定します。
```ruby: app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  has_one_attached :image # 画像アップロード用
  validates :title, :content, presence: true
end
```
`User`モデルに関連付けを追加します。
```ruby: app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
end
```
# 3.画像アップロード機能
画像をアップロードするには、RailsのActive Storageを利用します。Active Storageを有効化するために以下を実行します。
```ruby: bash
rails active_storage:install
rails db:migrate
```
投稿フォームに画像アップロード機能を追加します。
`app/views/posts/_form.html.erb`を編集します。
```erb: app/views/posts/_form.html.erb
<%= form_with(model: post, local: true) do |form| %>
  <%= form.label :title %>
  <%= form.text_field :title %>

  <%= form.label :content %>
  <%= form.text_area :content %>

  <%= form.label :image %>
  <%= form.file_field :image %>  <!--　変更部分　-->


  <%= form.label :location %> 
  <%= form.text_field :location %>

  <%= form.submit %>
<% end %>
```
画像の表示を追加します。
`app/views/posts/show.html.erb`で以下を挿入します。
```erb: app/views/posts/show.html.erb
<% if @post.image.attached? %>
  <%= image_tag @post.image %>
<% end %>
```
# 4. 投稿の編集と削除（投稿者本人のみ）
コントローラに認可ロジックを追加します。
`app/controllers/posts_controller.rb`に以下を追記します。
```ruby: app/controllers/posts_controller.rb
before_action :authenticate_user!
before_action :set_post, only: %i[show edit update destroy]
before_action :authorize_user!, only: %i[edit update destroy]

private

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

def authorize_user!
  redirect_to posts_path, alert: "操作権限がありません。" unless @post.user == current_user
end
```
# 5.いいね機能
`Like`モデルを作成します。
```ruby: bush
rails g model Like user:references post:references
rails db:migrate
```
`Post`と`User`に関連を追加します。
```ruby: bush
# app/models/post.rb
class Post < ApplicationRecord
  has_many :likes, dependent: :destroy
end

# app/models/user.rb
class User < ApplicationRecord
  has_many :likes, dependent: :destroy
end

# app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
```
ルーティングとコントローラを設定します。
`config/routes.rb`に以下を追加します。
```ruby: config/routes.rb
resources :posts do
  resources :likes, only: [:create, :destroy]
end
```
`LikesController`を作成します。
```ruby: app/controllers/likes_controller.rb
class LikesController < ApplicationController
  before_action :authenticate_user!

  def create
    post = Post.find(params[:post_id])
    post.likes.create(user: current_user)
    redirect_to post_path(post)
  end

  def destroy
    like = Like.find_by(post_id: params[:post_id], user: current_user)
    like.destroy if like
    redirect_to post_path(params[:post_id])
  end
end
```
投稿ビューにいいねボタンを追加します。
```erb: app/views/posts/show.html.erb
<% if current_user.likes.find_by(post_id: @post.id) %>
  <%= button_to 'いいねを取り消す', post_like_path(@post, current_user.likes.find_by(post_id: @post.id)), method: :delete %>
<% else %>
  <%= button_to 'いいね', post_likes_path(@post), method: :post %>
<% end %>
```
# 6.コメント機能
`Comment`モデルを作成します。
```ruby: bash
rails g model Comment content:text user:references post:references
rails db:migrate
```
ルーティングとコントローラを設定します。
`config/routes.rb`に以下を追加します。
```ruby: config/routes.rb
resources :posts do
  resources :comments, only: [:create, :destroy]
end
```
`CommentsController`を作成します。
```rb: app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  before_action :authenticate_user!

  def create
    post = Post.find(params[:post_id])
    comment = post.comments.new(comment_params)
    comment.user = current_user
    if comment.save
      redirect_to post_path(post), notice: 'コメントを追加しました。'
    else
      redirect_to post_path(post), alert: 'コメントを追加できませんでした。'
    end
  end

  def destroy
    comment = Comment.find(params[:id])
    if comment.user == current_user
      comment.destroy
      redirect_to post_path(comment.post), notice: 'コメントを削除しました。'
    else
      redirect_to post_path(comment.post), alert: '権限がありません。'
    end
  end

  private

  def comment_params
    params.require(:comment).permit(:content)
  end
end
```
投稿ビューにコメントフォームとリストを追加します。
```erb: app/views/posts/show.html.erb
<h3>コメント</h3>
<% @post.comments.each do |comment| %>
  <p><%= comment.content %> - <%= comment.user.name %></p>
  <% if comment.user == current_user %>
    <%= link_to '削除', post_comment_path(@post, comment), method: :delete, data: { confirm: '本当に削除しますか？' } %>
  <% end %>
<% end %>

<%= form_with(model: [ @post, Comment.new ], local: true) do |form| %>
  <%= form.text_area :content, placeholder: 'コメントを追加...' %>
  <%= form.submit 'コメントする' %>
<% end %>
```

今回は以上になります。

# 参考文献

https://zenn.dev/goldsaya/articles/02360f3fcb89e2

https://qiita.com/k19911848/items/a082cc4e0c0103f935b1

https://techtechmedia.com/percent-notation-rails/

https://qiita.com/mirimu/items/fc9c99314ffe9079c2ff

https://zenn.dev/ganmo3/articles/c071ba9aecaa51

https://zenn.dev/ganmo3/articles/937dc8578d9294