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

# はじめに
こんにch…　え？忙しい？？　んじゃぁスタート！！！

# 具体的な手順
[完成品GitHub](https://github.com/annaPanda8170/cheaptweet)
## ①アプリ立ち上げ
```bash:Terminal
$ cd Desktop
$ rails _5.2.4.1_ new cheaptweet -d mysql
$ cd cheaptweet
$ rails db:create
$ rails s
```

```:webBrowser
localhost:3000
```
<img width="282" alt="rails.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a85a72b0-b03f-df0b-410c-700b86edf09b.png">

## ②テーブル作成
![cheaptweet.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/bef2fbfb-6e1c-8686-73e6-3c65166ffcb8.jpeg)

```ruby:Gemfile
# 省略
gem 'devise'
```

```bash:Terminal
$ bundle install
$ rails g devise:install

control + c
$ rails s

$ rails g devise user
```

```ruby:db/migrate/2020xxxxxxxxx_devise_create_users.rb
# 省略
 t.string :nickname,           null: false
# 省略
```

```terminal:Terminal
$ rails db:migrate
```
<img width="796" alt="aa.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/4e8e3c67-1cd3-1e9c-bdab-442baeb4c347.png">


```bash:Terminal
$ rails g model tweet
```

```ruby:db/migrate/2020xxxxxxxxxxxx_create_tweets.rb
# 省略
 t.string :text, null: false
 t.references :user, foreign_key: true, null: false
# 省略
```

```bash:Terminal
$ rails db:migrate
```
<img width="300" alt="text.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/6bbc8cb7-8d50-7640-eb16-c4b5c62628e0.png">


```ruby:app/models/user.rb
#省略
 validates :nickname ,presence: true
 has_many :tweets
#省略
```

```ruby:app/models/tweet.rb
#省略
 validates :text ,presence: true
 belongs_to :user
#省略
```
## ③会員登録・ログイン・ログアウトのみの基本循環構築
```ruby: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
```

```bash:Terminal
$ rails g devise:views
```

```erb:app/views/devise/registrations/new.html.erb
<!--省略-->
<div class="field">
  <%= f.label :nickname %><br />
  <%= f.text_field :nickname, autofocus: true %>
</div>
<!--省略-->
<!--  他の autofocus: true を削除 -->
```

```bash:Terminal
$ rails g controller tweets index
```

```ruby:config/routes.rb
# get 'tweets/index'

#    |
#    v

resources :tweets, only: [:index]
root 'tweets#index'
#省略
```

```erb: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>
<!--省略-->
```

```erb:app/views/tweets/index.html.erb
<div>※確認用</div>
```

<img width="200" alt="main1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/b0e483f5-d393-8046-3432-c56786c7621a.png">   <img width="200" alt="main2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/ddacd370-b21c-15ea-ce6a-c9cf98268739.png">
<img width="200" alt="sign_up.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/cc3edf0d-35f4-cb5e-887e-53a7df664a25.png">
<img width="200" alt="login.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/36f0ac4a-e1d0-445e-7953-c4428e694835.png">

## ④投稿(new→create)

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

#    |
#    V

resources :tweets, only: [:index, :new, :create]
#省略
```

```ruby: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
```

```erb:new.html.erb
<%= form_with(model: @tweet, local:true) do |f| %>
  <%= f.text_area :text %>
  <%= f.submit '投稿' %>
<% end %>
```

```erb: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>
<!--省略-->
```
<img width="226" alt="hellob.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/b31a1831-014c-45df-0aa8-e6312a2a24f6.png">
<img width="350" alt="hello.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/e6756053-b29e-7292-9d39-6585b2ac9561.png">




## ⑤一覧(index)

```ruby:app/controllers/tweets_controller.rb
#省略
def index
####↓追記↓#####################################################
  @tweets = Tweet.all.includes(:user).order("created_at DESC")
####↑追記↑#####################################################
end
#省略
```

```erb: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 %>
```
<img width="181" alt="index.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/65be5336-60d4-22cd-775d-af1fd6e7d68b.png">
    includesなし　　　　　　　　　　　　　　includesあり
<img width="300" alt="back1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/29581364-012e-87f7-041e-7c34d66806df.png">　<img width="300" alt="back2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/1bd0d6de-8bbf-0f4c-a3b2-b0c2c3cec8be.png">
## ⑥詳細(show)・編集(edit→update)・削除(destroy)
```ruby:config/routes.rb
# resources :tweets, only: [:index, :new, :create]

#    |
#    V

resources :tweets
#省略
```

```ruby:app/controllers/tweets_controller.rb
#省略
def show
  @tweet = Tweet.find(params[:id])
end
#省略
```

```erb: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>
```

```erb: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  %>
```

```ruby: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
#省略
```

```erb:app/views/tweets/edit.html.erb
<%= form_with(model: @tweet, local:true) do |f| %>
  <%= f.text_area :text %>
  <%= f.submit '投稿' %>
<% end %>
```

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

```ruby:app/controllers/tweets_controller.rb
#省略
def destroy
  @tweet = Tweet.find(params[:id])
  @tweet.delete
#  redirect_to root_path
end
#省略
```

```erb:app/views/tweets/destroy.html.erb
<div>削除しました</div>
<%= link_to "トップに戻る", root_path %>
```
## ⑦編集・削除の権限設定

```erb: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 %>
```

```ruby: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
```
## ⑧一覧ページネーション

```ruby:Gemfile
#省略
gem 'kaminari'
```

```bash:Terminal
$ bundle install
```

```bash:Terminal
control + c
$ rails s
```

```ruby:
# @tweets = Tweet.all.includes(:user).order("created_at DESC")

#    |
#    V

@tweets = Tweet.includes(:user).order("created_at DESC").page(params[:page]).per(5)
```

```erb:app/views/tweets/index.html.erb
<!--省略-->
<%= paginate(@tweets) %>
```

## ⑨コメント機能
![cheaptweet (2).jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/a80ec66a-c574-6fae-680b-c9d6a4034d91.jpeg)

```bash:Terminal
$ rails g model comment
```

```ruby: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
#省略
```

```bash:Terminal
$ rails db:migrate
```

<img width="339" alt="comment.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/049efdc8-e083-de78-fca2-f6590f397d8e.png">

```ruby:app/models/user.rb
#省略
 has_many :comments
#省略
```

```ruby:app/models/tweet.rb
#省略
 has_many :comments
#省略
```

```ruby:app/models/comment.rb
#省略
 validates :text, presence: true
 belongs_to :user
 belongs_to :tweet
#省略
```

```ruby:config/routes.rb
# resources :tweets

#    |
#    V

resources :tweets do
  resources :comments, only: :create
end
```

```ruby: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
#省略
```

```bash:Terminal
$ rails g controller comments
```

```ruby: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
```

```erb: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 %>
```

<img width="277" alt="come.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/190b320c-c963-7f40-9b0d-39781bdf681d.png">
## ⑩ユーザー投稿一覧

```bash:Terminal
$ rails g controller users show
```

```ruby:config/routes.rb
# devise_for :usersより下に
#省略
resources :users, only: :show
#省略
```

```ruby: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
```

```erb:app/views/layouts/application.html.erb
× <%= current_user.nickname %>

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

<%= link_to user_path(current_user.id) do %><%= current_user.nickname %><% end %>
```

```erb: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>
```

```erb: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>
```

```erb: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) %>
```

<img width="249" alt="anna.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559746/e950cd9d-6128-9bc1-fc8d-159ace26157b.png">
## ⑪検索機能
```bash:Terminal
rails g controller tweets::searches
```

```ruby:config/routes.rb
#省略
 namespace :tweets do
   resources :searches, only: :index
 end
#省略
# resources :tweets do より上に
```

```ruby:app/models/tweet.rb
#省略
  def self.search(search)
    if search
      Tweet.where('text LIKE(?)', "%#{search}%").includes(:user)
    else
      Tweet.all.includes(:user)
    end
  end
#省略
```

```ruby: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
```

```erb: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 %>
<!-- 省略 -->
```

```erb: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アプリを丁寧に作る（もう一度きちんと復習して初心者を卒業しよう）](https://qiita.com/annaPanda8170/items/8856425be9d563fbffa7)
・[『メッセージを投稿』できる最低限のRailsアプリを丁寧に作る(これで初心者完全卒業!)](https://qiita.com/annaPanda8170/items/c042d533a3afb28213ee)

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

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