Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ツイッター風Railsアプリ最短復習(忙しい人の流し読みで開発シリーズ)

はじめに

こんにch… え?忙しい?? んじゃぁスタート!!!

具体的な手順

完成品GitHub

①アプリ立ち上げ

Terminal
$ cd Desktop
$ rails _5.2.4.1_ new cheaptweet -d mysql
$ cd cheaptweet
$ rails db:create
$ rails s
webBrowser
localhost:3000

rails.png

②テーブル作成

cheaptweet.jpeg

Gemfile
# 省略
gem 'devise'
Terminal
$ bundle install
$ rails g devise:install

control + c
$ rails s

$ rails g devise user
db/migrate/2020xxxxxxxxx_devise_create_users.rb
# 省略
 t.string :nickname,           null: false
# 省略
Terminal
$ rails db:migrate

aa.png

Terminal
$ rails g model tweet
db/migrate/2020xxxxxxxxxxxx_create_tweets.rb
# 省略
 t.string :text, null: false
 t.references :user, foreign_key: true, null: false
# 省略
Terminal
$ rails db:migrate

text.png

app/models/user.rb
#省略
 validates :nickname ,presence: true
 has_many :tweets
#省略
app/models/tweet.rb
#省略
 validates :text ,presence: true
 belongs_to :user
#省略

③会員登録・ログイン・ログアウトのみの基本循環構築

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
  end
end
Terminal
$ rails g devise:views
app/views/devise/registrations/new.html.erb
<!--省略-->
<div class="field">
  <%= f.label :nickname %><br />
  <%= f.text_field :nickname, autofocus: true %>
</div>
<!--省略-->
<!--  他の autofocus: true を削除 -->
Terminal
$ rails g controller tweets index
config/routes.rb
# get 'tweets/index'

#    |
#    v

resources :tweets, only: [:index]
root 'tweets#index'
#省略
app/views/layouts/application.html.erb
<!--省略-->
<body>
<!--↓追記↓----------------------------------------------->
  <header style="height: 50px; background-color: grey;">
    <% if user_signed_in? %>
      <%= current_user.nickname %>
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
    <% else %>
      <%= link_to "ログイン", new_user_session_path %>
      <%= link_to "会員登録", new_user_registration_path %>
    <% end %>
    <%= link_to "トップへ", root_path, style:"float: right;" %>
  </header>
<!--↑追記↑---------------------------------------------->
  <%= yield %>
</body>
<!--省略-->
app/views/tweets/index.html.erb
<div>※確認用</div>

main1.png main2.png
sign_up.png
login.png

④投稿(new→create)

config/routes.rb
# resources :tweets, only: [:index]

#    |
#    V

resources :tweets, only: [:index, :new, :create]
#省略
app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  def index
  end
####↓追記↓################################################
  def new
    @tweet = Tweet.new
  end
  def create
    @tweet = Tweet.new(tweet_params)
    if @tweet.save
      redirect_to root_path
    else
      render action: :new
    end
  end
  private
  def tweet_params
    params.require(:tweet).permit(:text).merge(user_id: current_user.id)
  end
####↑追記↑#################################################
end
new.html.erb
<%= form_with(model: @tweet, local:true) do |f| %>
  <%= f.text_area :text %>
  <%= f.submit '投稿' %>
<% end %>
app/views/layouts/application.html.erb
<!--省略-->
<body>
  <header style="height: 50px; background-color: grey;">
    <% if user_signed_in? %>
      <%= current_user.nickname %>
<!--↓追記↓----------------------------------->
      <%= link_to "投稿", new_tweet_path %>
<!--↑追記↑----------------------------------->
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
    <% else %>
      <%= link_to "ログイン", new_user_session_path %>
      <%= link_to "会員登録", new_user_registration_path %>
    <% end %>
  </header>
  <%= yield %>
</body>
<!--省略-->

hellob.png
hello.png

⑤一覧(index)

app/controllers/tweets_controller.rb
#省略
def index
####↓追記↓#####################################################
  @tweets = Tweet.all.includes(:user).order("created_at DESC")
####↑追記↑#####################################################
end
#省略
app/views/tweets/index.html.erb
× <div>※確認用</div>

<!--     |     -->
<!--     V     -->

<% @tweets.each do |t| %>
  <div><span style="color: red;"><%= t.user.nickname %></span><%= t.text %></div>
<% end %>

index.png
includesなし              includesあり
back1.png back2.png

⑥詳細(show)・編集(edit→update)・削除(destroy)

config/routes.rb
# resources :tweets, only: [:index, :new, :create]

#    |
#    V

resources :tweets
#省略
app/controllers/tweets_controller.rb
#省略
def show
  @tweet = Tweet.find(params[:id])
end
#省略
app/views/tweets/index.html.erb
×  <div><span style="color: red;"><%= t.user.nickname %></span><%= t.text %></div>

<!--    |    -->
<!--    V    -->

<div><%= link_to tweet_path(t.id) do %><span style="color: red;"><%= t.user.nickname %></span><%= t.text %><% end %></div>
app/views/tweets/show.html.erb
<div><span style="color: red;"><%= @tweet.user.nickname %></span><%= @tweet.text %></div>
<%= link_to "編集",  edit_tweet_path(@tweet.id) %><%= link_to "削除",  tweet_path(@tweet.id),method: :delete  %>
app/controllers/tweets_controller.rb
#省略
def edit
  @tweet = Tweet.find(params[:id])
end
def update
  @tweet = Tweet.find(params[:id])
  if @tweet.update(tweet_params)
    redirect_to tweet_path(params[:id])
  else
    render action: :edit
  end
end
def destroy
  @tweet = Tweet.find(params[:id])
  @tweet.delete
  redirect_to root_path
end
#省略
app/views/tweets/edit.html.erb
<%= form_with(model: @tweet, local:true) do |f| %>
  <%= f.text_area :text %>
  <%= f.submit '投稿' %>
<% end %>

削除後確認画面が欲しければ、

app/controllers/tweets_controller.rb
#省略
def destroy
  @tweet = Tweet.find(params[:id])
  @tweet.delete
#  redirect_to root_path
end
#省略
app/views/tweets/destroy.html.erb
<div>削除しました</div>
<%= link_to "トップに戻る", root_path %>

⑦編集・削除の権限設定

app/views/tweets/show.html.erb
× <%= link_to "編集",  edit_tweet_path(@tweet.id) %><%= link_to "削除",  tweet_path(@tweet.id),method: :delete %>

<!--    |    -->
<!--    V    -->

<% if user_signed_in? && current_user.id == @tweet.user_id %>
  <%= link_to "編集",  edit_tweet_path(@tweet.id) %><%= link_to "削除",  tweet_path(@tweet.id),method: :delete %>
<% end %>
app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
####↓追記↓###############################################
  before_action :unless_signin, only: [:new, :create,]
  before_action :unless_mytweet, only: [:edit, :update, :destroy]
####↑追記↑###############################################

#省略

  private

#省略

####↓追記↓###############################################
  def unless_signin
    redirect_to tweets_path unless user_signed_in?
  end
  def unless_mytweet
    redirect_to tweets_path unless user_signed_in? && current_user.id == Tweet.find(params[:id]).user.id
  end
####↑追記↑###############################################
end

⑧一覧ページネーション

Gemfile
#省略
gem 'kaminari'
Terminal
$ bundle install
Terminal
control + c
$ rails s
# @tweets = Tweet.all.includes(:user).order("created_at DESC")

#    |
#    V

@tweets = Tweet.includes(:user).order("created_at DESC").page(params[:page]).per(5)
app/views/tweets/index.html.erb
<!--省略-->
<%= paginate(@tweets) %>

⑨コメント機能

cheaptweet (2).jpeg

Terminal
$ rails g model comment
db/migrate/2020xxxxxxxx_create_comments.rb
#省略
  t.string :text, null: false
  t.references :user, foreign_key: true, null: false
  t.references :tweet, foreign_key: true, null: false
#省略
Terminal
$ rails db:migrate

comment.png

app/models/user.rb
#省略
 has_many :comments
#省略
app/models/tweet.rb
#省略
 has_many :comments
#省略
app/models/comment.rb
#省略
 validates :text, presence: true
 belongs_to :user
 belongs_to :tweet
#省略
config/routes.rb
# resources :tweets

#    |
#    V

resources :tweets do
  resources :comments, only: :create
end
app/controllers/tweets_controller.rb
#省略
def show
  @tweet = Tweet.find(params[:id])
####↓追記↓#############################################
  @comment = Comment.new
  @comments = Comment.where(tweet_id: params[:id]).order("created_at DESC").page(params[:page]).per(5)
####↑追記↑#############################################
end
#省略
Terminal
$ rails g controller comments
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    redirect_to tweets_path unless user_signed_in?
    @comment = Comment.new(params_comment)
    @comment.save
    redirect_to tweet_path(params[:tweet_id])
  end
  private
  def params_comment
    params.require(:comment).permit(:text).merge(user_id: current_user.id, tweet_id: params[:tweet_id])
  end
end
app/views/tweets/show.html.erb
<!--省略-->
<% if user_signed_in? %>
  <%= form_with(model: [@tweet, @comment], local: true) do |f| %>
    <%= f.text_area :text %>
    <%= f.submit 'コメント'%>
  <% end %>
<% end %>
<% if @comments %>
  <% @comments.each do |c| %>
    <div><span style="color: blue;"><%= c.user.nickname %></span><%= c.text %></div>
  <% end %>
  <%= paginate(@comments) %>
<% end %>

come.png

⑩ユーザー投稿一覧

Terminal
$ rails g controller users show
config/routes.rb
# devise_for :usersより下に
#省略
resources :users, only: :show
#省略
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @tweets = Tweet.where(user_id: params[:id]).order("created_at DESC").page(params[:page]).per(5)
    @nickname = User.find(params[:id]).nickname
  end
end
app/views/layouts/application.html.erb
× <%= current_user.nickname %>

<!--      |       -->
<!--      V       -->

<%= link_to user_path(current_user.id) do %><%= current_user.nickname %><% end %>
app/views/tweets/index.html.erb
× <div><%= link_to tweet_path(t.id) do %><span style="color: red;"><%= t.user.nickname %></span><%= t.text %><% end %></div>

<!--      |       -->
<!--      V       -->

<div><%= link_to user_path(t.user.id),style:"color: red;" do %><%= t.user.nickname %><% end %><%= link_to tweet_path(t.id) do %><%= t.text %><% end %></div>
app/views/tweets/show.html.erb
× <div><span style="color: red;"><%= @tweet.user.nickname %></span><%= @tweet.text %></div>

<!--      |       -->
<!--      V       -->

<div><%= link_to user_path(@tweet.user.id),style:"color: red;" do %><%= @tweet.user.nickname %><% end %><%= @tweet.text %></div>
app/views/users/show.html.erb
<p><%= @nickname %>の投稿一覧</p>
<% @tweets.each do |t| %>
  <div><%= link_to tweet_path(t.id) do %><%= t.text %><% end %></div>
<% end %>
<%= paginate(@tweets) %>

anna.png

⑪検索機能

Terminal
rails g controller tweets::searches
config/routes.rb
#省略
 namespace :tweets do
   resources :searches, only: :index
 end
#省略
# resources :tweets do より上に
app/models/tweet.rb
#省略
  def self.search(search)
    if search
      Tweet.where('text LIKE(?)', "%#{search}%").includes(:user)
    else
      Tweet.all.includes(:user)
    end
  end
#省略
app/controllers/tweets/searches_controller.rb
class Tweets::SearchesController < ApplicationController
  def index
    @tweets = Tweet.search(params[:keyword]).order("created_at DESC").page(params[:page]).per(5)
  end
end
app/views/tweets/index.html.erb
<%= form_with(url: tweets_searches_path, local: true, method: :get) do |f| %>
  <%= f.text_field :keyword %>
  <%= f.submit "検索" %>
<% end %>
<!-- 省略 -->
app/views/tweets/searches/index.html.erb
<%= form_with(url: tweets_searches_path, local: true, method: :get) do |f| %>
  <%= f.text_field :keyword %>
  <%= f.submit "検索" %>
<% end %>
<% @tweets.each do |t| %>
  <div><%= link_to user_path(t.user.id),style:"color: red;" do %><%= t.user.nickname %><% end %><%= link_to tweet_path(t.id) do %><%= t.text %><% end %></div>
<% end %>
<%= paginate(@tweets) %>

まとめ

網羅的でいい教材ですね。

これで難しい場合は以下をまわってみてください。

超最低限のRailsアプリを丁寧に作る(もう一度きちんと復習して初心者を卒業しよう)
『メッセージを投稿』できる最低限のRailsアプリを丁寧に作る(これで初心者完全卒業!)

次のレベルに行きたければ以下に行ってみてください。

ツイッター風Railsアプリをデプロイする(前編:unicornのみで取り急ぎ繋げよう)
『メッセージと複数画像の投稿』ができる最低限のRailsアプリを丁寧に作る
『2ページ遷移して会員登録』できる最低限のRailsアプリを丁寧に作る(deviseをウィザード形式に拡張)
『非同期でのメッセージ投稿』が理解できる最低限のRailsアプリを丁寧に作る(Ajax苦手の自分とお別れしよう)

annaPanda8170
長いコードを"上から順に"説明されても理解できないですよね? "必要な順に"コードを増やしてゆくスタイルで記事を書いていくつもりです。 私の記事は半分パブリックなものだと思っているので、どんどん意見を吸収して育てていきたいです。
https://annapanda.xyz/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away