みやちゃ
皆さんこんにちは、みやちゃです!
物理とプログラミングについて解説しています!
今回は「日記投稿サービスを作る」#1です。
これから順にRailsを詳しく解説していきながら、サービスを作っていきます。
ここでは、過去に私が疑問に思ったことについて全て解説しているので、かなり詳しくなっています。
初学者の方に向けた内容となっています。
モデルを作成するときのコマンドを詳細に理解できる(rails g、rails db:migrate...etc)
ルーティングの設定の仕方がわかる
HTTPメソッド(GET、POST、、)を理解できる
作成した投稿をViewに表示できる
以下のような流れで進めていきます!
日記投稿サービスの概要の説明
Railsの基本理念
投稿Modelの作成
Controller、Viewの作成
HTTPメソッドについて
補足 インスタンス変数
目次
日記投稿サービス 概要
日記を投稿できるサービス
ユーザーができること
・日記の投稿、コメント、送信
・ユーザーのフォロー
・投稿のいいね
(今後追加していくかも)
今、「rails new」しただけの何もしていない状態です。
Railsの基本理念
Railsの基本理念は、
1、同じことを繰り返すな(Don't Repeat Yourself:DRY)
同じコードをあちこちに書くと、エラーが起きた時に余計な手間がかかってしまいます。
そのため、一箇所に書くことがベストとされているそうです。
2、設定より規約を優先する
Railsは、WEBアプリケーションを開発する上で最適と考えられるものがデフォルトに設定されているそうです。
そのため、とりあえず規約に従えば、最適な道を進めるということですね。
これからの開発は上の基本理念を意識して開発していきたいと思います!
投稿モデル
投稿モデルの作成
$rails g model post body:text title:string
をターミナルで実行します。
「g」はgenerateの意味です。
「rails g model モデル名 カラム名:データ型」で宣言できます。
「rails g model post」で「Postモデルを作成する」ことを宣言しています。
その後は、カラム(column)とそのカラムのデータ型を指定しています。(カラムとデータは下で解説)
意味は、「bodyカラムをtext型で」「titleカラムをstring型で」です。
上の宣言を実行すると、
invoke active_record
create db/migrate/20200828130931_create_posts.rb
create app/models/post.rb
invoke test_unit
create test/models/post_test.rb
create test/fixtures/posts.yml
となりました。
上の「rails g」コマンドによって、db/migrateのフォルダにmigrationファイルができました。
migration(マイグレーション)とは?
マイグレーションは、データベーススキーマ(データベースの設計図)を管理する役割を持ったドメイン固有言語(特定のタスク向けに設計されたコンピュータ言語)です。
rails db:migrateを実行することで、このmigrationファイルの内容を基にデータベースが書き加えられます。
今できたmigrationファイルを見てみます。
class CreatePosts < ActiveRecord::Migration[6.0]
def change
create_table :posts do |t|
t.text :body
t.string :title
t.timestamps
end
end
end
「モデルをこのカラムで作るよー」的なことが書いてあります。
(注意)rails db:migrateを既にしたmigrationファイルをいじるときは、rails db:rollbackする必要があります。(検索必須)
migrationファイルがうまくできているので、次のコマンドを実行します。
$rails db:migrate
を実行しましょう。
これで、Postモデルが作成されたはずです。
dbフォルダにあるschema.rbファイルを見てみます。
create_table "posts", force: :cascade do |t|
t.text "body"
t.string "title"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
上のように記述されています。
うまくいったようです。
db/schema.rbには、現在のスキーマ(設計図)が反映されます。モデルのカラムを確認するときなどに役立ちます。
(自分へ、migrateをした後は、再起動すべし)
カラムとデータ型
データ型には、
・string:文字列(1〜255文字まで)
・text:長い文字列(かなり)
・integer:整数
・float:浮動少数
・datetime:日時
・date:日付
など色々あります。
それぞれのカラムに適したデータ型を選択しましょう。
上では、bodyカラムにtext型を、titleカラムにstring型を指定しました。
bodyカラムには、大量の文字数が保存されるでしょうから、text型に、
titleカラムは、そこまでの文字数は必要ないと思うので、string型に、それぞれ指定しました。
投稿のコントローラー作成
Postモデルが作成できました。
次に下のコマンドで、コントローラーを作成します。
$rails g controller posts
上のコマンドを実行すると、
create app/controllers/posts_controller.rb
invoke erb
create app/views/posts
invoke test_unit
create test/controllers/posts_controller_test.rb
invoke helper
create app/helpers/posts_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/posts.scss
となりました。
app/controllersフォルダに、posts_controller.rbファイルが作成されたとありますね。
うまくいっているみたいです。
しかし、なぜ「posts」と複数形なのでしょうか?
調べて見たところ、規約に複数形を使うのがベストと書いてあります。
理由としては、「resourcesなどのデフォルトのルーティングジェネレータがそのまま利用できる」、「名前付きルーティングヘルパーの用法がアプリケーション全体で一貫する」とあります。
(参考 https://railsguides.jp/action_controller_overview.html)
前者について、routes.rbでルーティングを設定しますが、その際に簡単に一括ルーティングができるresourcesが利用できなくなり、いちいち「:path」や「:controller」を指定しなくちゃいけなくなるようです。
面倒ですね。
controllerは複数形で命名した方が良さそうですね。
では、posts_controller.rbにindexメソッドを定義していきます。
indexでは、投稿を一覧表示させます。
posts_controller.rbに以下のように記述します。
def index
@posts = Post.all
end
indexメソッドを定義し、その中で@posts = Post.allと定義しました。
@がつく変数はインスタンス変数です。
インスタンス変数は、一度作成されると、他のメソッドでも値を取り出したり格納したりすることができます。(ローカル変数はメソッド内では使用できるが、メソッドを抜けると値は消えてしまいます。)
(記事の下の補足で詳しく解説してます。)
@postsの中には、作成された全てのpost(投稿)が保存されています。
viewの追加
上でコントローラーのindexメソッドが定義できました。
では、viewを追加していきます。
上で、controllerを作成した時に、同時にviewフォルダにpostsフォルダが作成されているはずです。
そこに「index.html.erb」というファイルを作成します。
そして、以下のように記述します。
<h1>投稿一覧</h1>
ルーティングの設定
viewの記述ができたので、ルーティングを設定していきます。
configフォルダにあるroutes.rbファイルに以下のように記述していきましょう。
get 'posts', to: 'posts#index'
上では、'posts'というリクエスト(URL)があったら、「postsコントローラのindexアクションを呼び出す」ということを宣言してます。
localohost:3000/postsを開いて、「投稿一覧」と表示されるか見てみましょう。
ちゃんと表示されてますね。
上では、'posts'というリクエストを指定しましたが、
実験で、以下のようにリクエストの文字を「hoge」にしてみてもうまくいきます。(localohost:3000/hoge)
get 'hoge', to: 'posts#index'
自由に設定できるのですね。
postsに直しておきます。
HTTPメソッド
上でroutes.rbに「get 'posts', to: 'posts#index'」と記述しました。
get??
GETとはHTTPメソッドと呼ばれるやつです。
HTTPメソッドには、GET、POST、PUT、DELETE、PATCHがあります。
これらのHTTPメソッドには、それぞれに役割・特徴があります。
GETは「データを取得する」、
POSTは「情報の新規作成」、
PUT/PATCHは「情報を更新する」、
DELETEは「情報を削除する」
時にそれぞれ使われます。
indexのルーティングでは、Postモデルの一覧情報を"取得して"、viewに表示させたいのでGETを使いました。
それぞれのメソッドに適したHTTPメソッドを指定していきましょう。
index.html.erbには、「投稿一覧」という文字を表示させるコードしか書いていませんでした。
では、実際に投稿を表示させるためのコードを記述していきます。
view/postsフォルダにあるindex.html.erbを開いて以下のように記述します。
<h1>投稿一覧</h1>
<% @posts.each do |post| %>
<%= post.title %>
<%= post.body %>
<% end %>
posts_controller.rbのindexメソッドを見返すと、@postsというインスタンス変数にPost.all(作成された投稿の全て)を定義しました。
@posts.each do |post| は、
『「作成された全ての投稿」の各々』を、<% end %>までの間、「post」という文字列で扱う
という意味です。
post.titleは、それぞれの投稿(post)に保存されたtitleカラムの情報を持ってきてくれます。
post.bodyは、それぞれの投稿(post)に保存されたbodyカラムの情報を持ってきてくれます。
これで、ブラウザを更新すると、投稿が一覧表示されるはずです。
その前に、投稿を作成しましょう。
terminalで、以下のコマンドを実行してください。
$rails c
rails cは、rails consoleの訳です。
上のコマンドを実行すると、以下のように表示されます。
irb(main):001:0>
rails consoleを起動すると、そこで、rubyコードを実行できるようになります。
例えば、何かを実装したいけれど、よくわからないエラーが出てしまい、どこまで成功できているのかわからないという時に、rails cで、ちょっとずつ書きながら、どこでエラーが出るのかを調べることができます。
では、投稿を作成しましょう。
以下を実行して、Postモデルをcreateします。
irb(main):001:0> Post.create(body: "最初の投稿です。", title: "post1")
これを実行すると、
irb(main):001:0> Post.create(body: "最初の投稿です。", title: "post1")
(6.5ms) SELECT sqlite_version(*)
(0.2ms) begin transaction
Post Create (4.8ms) INSERT INTO "posts" ("body", "title", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["body", "最初の投稿です。"], ["title", "post1"], ["created_at", "2020-08-28 14:18:46.121869"], ["updated_at", "2020-08-28 14:18:46.121869"]]
(2.7ms) commit transaction
=> #<Post id: 1, body: "最初の投稿です。", title: "post1", created_at: "2020-08-28 14:18:46", updated_at: "2020-08-28 14:18:46">
となりました。うまく作成できたみたいです。
確認のために、
irb(main):002:0> Post.find(1)
を実行します。
このコマンドは、「Postモデルの中で、idが1のPostを見つける」という意味です。
irb(main):002:0> Post.find(1)
Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<Post id: 1, body: "最初の投稿です。", title: "post1", created_at: "2020-08-28 14:18:46", updated_at: "2020-08-28 14:18:46">
ちゃんと作成されているみたいですね。
では、ブラウザ(localhost:3000/posts)を更新してみましょう。
ちゃんと表示されていますね。
投稿をあと2つくらい追加してみます。
ちゃんと表示できてますね。
一行になってしまっているので、<p>タグで挟んで改行しましょう。
<h1>投稿一覧</h1>
<% @posts.each do |post| %>
<p><%= post.title %></p>
<p><%= post.body %></p>
<% end %>
これでうまくいけるはずです。
うまく改行されていますね。
補足(インスタンス変数)
コントローラーのところで、@posts のように先頭に@をつけたような変数はインスタンス変数と呼ばれることを解説しました。
また、インスタンス変数は、メソッドを抜けても値は保存され使用することができるのでした。
それに対し、ローカル変数は、メソッドを抜けると値は消えてしまうのでした。
ローカル変数について、ちょっと実験してみます。
def index
@posts = Post.all
post_1 = Post.find(1) #idが1のPostを見つける
end
posts_controller.rbに上のように記述します。
post_1はローカル変数です。
これがメソッドを出て、viewでも使えるかを見てみます。
<h1>投稿一覧</h1>
<% @posts.each do |post| %>
<p><%= post.title %></p>
<p><%= post.body %></p>
<% end %>
<p><%= post_1.title %></p>
index.html.erbを上のように記述します。
ブラウザを更新して、ターミナルで確認してみます。
ActionView::Template::Error (undefined local variable or method `post_1' for #<#<Class:0x00007f85845033e8>:0x00007f85845008a0>
Did you mean? @posts):
5: <p><%= post.body %></p>
6: <% end %>
7:
8: <p><%= post_1.title %></p>
app/views/posts/index.html.erb:8
定義されていないと怒られてしまいました。
ローカル変数は、メソッドを抜けると使用できず、メソッド内のみ使用可能のようです。
まとめ
今回は、私が開発するサービスのご紹介と投稿モデル作成、コントローラー作成、indexメソッドの定義まで解説しました。
どんどん開発して、ブログにあげていきます!
ここまでご覧いただきありがとうございます!