はじめに
こんにch… え?忙しい?? んじゃぁスタート!!!
具体的な手順
①アプリ立ち上げ
Terminal
$ cd Desktop
$ rails _5.2.4.1_ new cheaptweet -d mysql
$ cd cheaptweet
$ rails db:create
$ rails s
webBrowser
localhost:3000
②テーブル作成
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
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
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>
④投稿(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>
<!--省略-->
⑤一覧(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 %>
⑥詳細(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) %>
⑨コメント機能
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
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 %>
⑩ユーザー投稿一覧
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) %>
⑪検索機能
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アプリを丁寧に作る
・『2ページ遷移して会員登録』できる最低限のRailsアプリを丁寧に作る(deviseをウィザード形式に拡張)
・『非同期でのメッセージ投稿』が理解できる最低限のRailsアプリを丁寧に作る(Ajax苦手の自分とお別れしよう)