レコードの一覧表示
私たちの投稿を掲載する
私たちのブログアプリでは、最初にすべての投稿タイトルのリストを表示したいと考えています。 そして、ここにいくつかの記事があるデータベースがあるので、実際にここに表示するものがあります!
開始するには、hello_worldアクションで3つのことが必要であることを思い出してください:
1 a route
2 an action
3 view
ここにもそれぞれのものが必要ですので、/ list_postsのルートを定義して物事を説明しましょう:
config/routes.rb
Rails.application.routes.draw do
get '/list_posts' => 'application#list_posts'
end
application#list_postsアクションは次を指します:
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def list_posts
connection = SQLite3::Database.new 'db/development.sqlite3'
connection.results_as_hash = true
posts = connection.execute("SELECT * FROM posts")
render 'application/list_posts', locals: { posts: posts }
end
end
私たちのlist_postsアクションの中には、以前のDB初期化ロジックが表示され、次にSELECTクエリがあり、すべての投稿をハッシュとして取得します。 その後、localsとして一緒にそれらをビューに渡すだけです。
ビューはまだ定義されていないなので、次にそれをしましょう:
<!-- app/views/application/list_posts.html.erb -->
<html>
<body>
<div class="posts">
<% posts.each do |post| %>
<div class="post">
<h2 class="title">
<%= post['title'] %>
</h2>
<small class="meta">
<span class="author">by <%= post['author'] %> -</span>
<em class="created_at"><%= post['created_at'] %></em>
</small>
</div>
<hr />
<% end %>
</div>
</body>
</html>
ここにはかなりのマークアップがありますが、その大半はこれを除いてよく知られているはずです:
<% posts.each do |post| %>
<!-- ... -->
<% end %>
ここでは、ERBビュー内で配列をどのように反復処理できるかを確認します。上記では、posts.eachを使用してポストをループし、各ポストに対して<div>を作成します。 (このポスト変数はブロックの<%end%>の前のどこでも利用できます)。
ここで注意すべき重要なことは、<%=>とは対照的に<=%>(=を除く)を使用することです。 これにより、戻り値をページに配置せずにRubyを実行することができます。
それ以外にも、<%= post [‘title’]%>のような行で適切なポスト値をポスト<div>の別の場所に配置するだけです。
そして、/ list_postsにアクセスすると、3つの投稿のタイトルと、それぞれの著者の名前と作成日が表示されます。
ユーザーに投稿を表示する
これで/ list_postsが動作するようになったので、/ show_post /:idのために何かを実装してみましょう。これはユーザーが特定の投稿を読むために行く場所です。
まず、ルートが必要です。
### config/routes.rb ###
Rails.application.routes.draw do
get '/list_posts' => 'application#list_posts'
get '/show_post/:id' => 'application#show_post'
end
ここでは、コントローラーアクションで使用するパラメータとしてIDを取得するためのURLパターンを設定することができます。
それから、show_postアクションが必要になります:
### app/controllers/application_controller.rb ###
class ApplicationController < ActionController::Base
# ...
def show_post
connection = SQLite3::Database.new 'db/development.sqlite3'
connection.results_as_hash = true
post = connection.execute("SELECT * FROM posts WHERE posts.id = ? LIMIT 1", params['id']).first
render 'application/show_post', locals: { post: post }
end
end
このshow_postアクションでは、まず特定のIDでデータベースから投稿を取得する必要があります。connection.executeでもう一度SQLをDBに対して実行しますが、今回は.firstを呼び出します。その理由は、LIMIT 1の場合のように、connection.executeは常に単一の要素を含むことになっても、配列を返すためです。
これをデータベースで使用する方法は面白いですが、見てみましょう:
connection.execute("SELECT * FROM posts WHERE posts.id = ? LIMIT 1", params['id'])
私たちの目的には、次の行と同じ効果があります:
connection.execute("SELECT * FROM posts WHERE posts.id = #{params['id']} LIMIT 1")
オリジナルのステートメントの?文字は、2番目のパラメータの値で置き換えられるプレースホルダです(params [‘id’])。これは、ユーザーの入力(この場合はURLからの)をSQLステートメントに含めるより安全な方法です。Railsはステートメントが安全であることを確認します。 2番目のステートメントは安全ではありません。ユーザーの入力を直接ステートメントに含めたからです。この技術は、SQLインジェクション攻撃に対する保護と呼ばれています。 このシリーズの後半でこのトピックに着きます。
しかし、私たちのアクションの最後の行に戻ると、私たちは私たちの投稿をshow_postビューに渡すだけであることがわかります。 言えば、今ここでそれを定義しましょう:
<!-- app/views/application/show_post.html.erb -->
<html>
<body>
<div class="post">
<h2 class="title">
<%= post['title'] %>
</h2>
<small class="meta">
<span class="author">by <%= post['author'] %> -</span>
<em class="created_at"><%= post['created_at'] %></em>
</small>
<p class="body"><%= post['body'] %></p>
</div>
<br />
</body>
</html>
このビューは基本的にlist_postsビューと同じですが、ループを持たず、ポストボディをレンダリングします。
/ show_post / 1にアクセスと、投稿の詳細と投稿の本文が表示されます。
リンク
最後に、これらの2つのページの間にいくつかのリンクを作ってみましょう:
<!-- app/views/application/show_post.html.erb -->
<html>
<body>
<!-- link to list of posts -->
<a href="/list_posts">Back to Posts</a>
<div class="post">
<!-- ... -->
</div>
<br />
</body>
</html>
<!-- app/views/application/list_posts.html.erb -->
<html>
<body>
<div class="posts">
<% posts.each do |post| %>
<div class="post">
<h2 class="title">
<!-- link to post -->
<a href="/show_post/<%= post['id'] %>"><%= post['title'] %></a>
</h2>
<!-- ... -->
</div>
<hr />
<% end %>
</div>
</body>
</html>
showビューからlistビューへのリンクは単純で静的なリンクです:
<a href="/list_posts">Back to Posts</a>
listビューのリンクはループの中にあったので、少し複雑になります:
<a href="/show_post/<%= post['id'] %>"><%= post['title'] %></a>
ここでは、投稿IDを使用してhrefを動的に作成し、リンクテキストを各投稿の投稿のタイトルに設定します。
DB接続用の共有ロジックの抽出
show_postで、list_postsで使用したデータベースに接続するために同じ2行を繰り返していることに気づいたかもしれません。このロジックをコントローラー内の独自の接続メソッドに移して、両方のアクションを調整して使用してみましょう。
class ApplicationController < ActionController::Base
def list_posts
posts = connection.execute("SELECT * FROM posts")
render 'application/list_posts', locals: { posts: posts }
end
def show_post
post = connection.execute("SELECT * FROM posts WHERE posts.id = ? LIMIT 1", params['id']).first
render 'application/show_post', locals: { post: post }
end
private
def connection
db_connection = SQLite3::Database.new 'db/development.sqlite3'
db_connection.results_as_hash = true
db_connection
end
end
これは素晴らしいことです。なぜなら、後でデータベース接続について変更する必要がある場合、変更をただちに行うことができるからです。さらに、他のアクションでデータベース接続を使用する必要がある場合は、connectionを使用することもできます。