見出し画像

Railsで挫折した人のためのSinatra -4-

DBとの連携

前回はコチラ

前回では疑似的なデータで俳句一覧の表示や詳細ページなどを出していましたが、今回からはDBと連携していきます。
sqlite3というgemを使ってSqliteをDBとして使っていきます。

まず、必要なgemをinstallしていきます。
以下の4つをGemfileの一番下に追記してください

...
 
gem "rake"
gem "sinatra-contrib"
gem "sinatra-activerecord"
gem "sqlite3"

次に、sinatra-app以下にdbディレクトリを作り、init.sqlファイルを作成して以下のように記述してください。

/db/init.sql ↓

DROP TABLE IF EXISTS haikus;

create table haikus (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 main TEXT NOT NULL
);

insert into haikus values (1, "古池や蛙飛こむ水のおと");
insert into haikus values (2, "閑さや岩にしみ入蝉の声");

こちらではデータベース上にhaikuテーブルを作ってデータを挿入します。
データを挿入するものをdumpファイルとも言ったりします。
データベースに値を格納するためには、値の構造ごとにテーブルというものが必要でそれを用意してあげなければいけません。
そのためのファイルです。
データベース自体が大きな倉庫だとしたら、テーブルは棚ですね、そしてそれぞれの値が棚に積まれている商品と考えるといいかもしれません。
できましたら、以下のコマンドでdbを作成しましょう。
/db以下にsinatra.dbが生成されれば大丈夫です。

$ cd db

$ sqlite3 sinatra.db < init.sql

今回はDBの操作にORMとしてRailsでおなじみのActiveRecordを使っていきます。/models/haiku.rbにある疑似データ等を消すので大幅に記述が変わります。以下のように修正してください。
/models/haiku.rb ↓

require 'bundler'
Bundler.require

ActiveRecord::Base.establish_connection(
 adapter: 'sqlite3',
 database: './db/sinatra.db'
)

class Haiku < ActiveRecord::Base
end

bundlerが必要なため先頭で呼び出しているのと、ActiveRecordでどのDBと連携するのかを記述していきます。そして、HaikuクラスにActiveRecordを継承させます。
これで、Haikuクラスに対してActiveRecordの各メソッドが使えるようになります。そのため、コントローラーも変更しなければいけません。
/controllers/haiku.rb を以下のように修正しましょう。

require './controllers/base.rb'
require './models/haiku.rb'
class HaikuController < Base
 get '/' do
   @haikulist = Haiku.all # 修正
   erb :index
 end
 get '/:id' do
   @haiku = Haiku.find(params[:id]) # 修正
   erb :show
 end
end

これで楽にSQLを発行できる様になりました。
それではdockerを再起動して http://localhost:4567 で各ページを確認しましょう。

さて、DBを連携させたので、早速データの追加と削除機能を追加しましょう。

ActiveRecordを使ったSQLの発行

まずは、新規データの作成です。/views/index.erb と/controllers/haiku.rbを以下のように修正してください。

/views/index.erb ↓

<main>
 <div class="container">
   <form method="post" action="/haiku/create">
     <p><b>Body</b></p>
     <p><input type= "text" name="main"></p>
     <p><input type= "submit" value="Create"></p>
   </form>
   <div>
     <h1>Haiku list</h1>
     <% @haikulist.each do |haiku| %>
       <p><b>No.<%= haiku[:id] %></b> <a href=<%="/haiku/#{haiku[:id]}"%> ><%= haiku[:main] %></a></p>
     <% end %>
   </div>
 </div> 
</main>  
<style>
.container {
 display: flex;
 justify-content: start;
}
.container div {
 margin: 50px 30px;
}
.container form {
 margin: 70px 30px;
 width: 250px;
}
.container form input {
 width: 100%;
 height: 30px;
}
</style>

上の方にformを設置してactionを/haiku/createに飛ばすよう設定しています。下の方は軽くスタイルを付けて見た目を整理しています。

/controllers/haiku.rb ↓

require './controllers/base.rb'
require './models/haiku.rb'
class HaikuController < Base
 get '/' do
   @haikulist = Haiku.all
   erb :index
 end
 get '/:id' do
   @haiku = Haiku.find(params[:id])
   erb :show
 end
 # 以下を追加
 post '/create' do
   Haiku.create!({:main => params[:main]})  
   puts params
   redirect '/haiku' 
 end
end

actionで/haiku/create/にformの情報が飛ばされてくるのでpost '/create' を用意して処理を追加しています。

dockerを再起動して http://localhost:4567 にアクセスして新しい俳句を作成してみましょう!

続いて削除機能です。今度はhaikuコントローラーから変更しましょう。

/controllers/haiku.rb ↓

require './controllers/base.rb'
require './models/haiku.rb'
class HaikuController < Base
 get '/' do
   @haikulist = Haiku.all
   erb :index
 end
 get '/:id' do
   @haiku = Haiku.find(params[:id])
   erb :show
 end
 post '/create' do
   Haiku.create!({:main => params[:main]})  
   puts params
   redirect '/haiku' 
 end
 # 以下を追加
 post '/:id/delete' do
   @haiku = Haiku.find(params[:id])
   @haiku.delete
   redirect '/haiku'
 end
end

/haiku/:id/deleteにルーティングを用意してidから見つけてきたインスタンスを削除しています。
erbファイルには削除するボタンとして/haiku/:id/deleteに対してpostメソッドを飛ばすaction等を用意すると実装できそうです。

/views/show.erb ↓

<main>
 <h1>Haiku details</h1>
 <p>
   <%= @haiku[:main] %>
 </p>
 // 以下を追加
 <form method="post" action=<%= "/haiku/#{@haiku[:id]}/delete" %>>
   <input type="submit" value="Delete">
 </form>
</main>

formを用意して/haiku/:id/deleteに対してpostメソッドを飛ばすボタンを用意しました。

Validationの発行と付随処理

最後にvalidationというものを設定しましょう。

/models/haiku.rbを以下の様に修正してください↓

require 'bundler'
Bundler.require

ActiveRecord::Base.establish_connection(
 adapter: 'sqlite3',
 database: './db/sinatra.db'
)

class Haiku < ActiveRecord::Base
 validates :main, presence: true # 追加部分
end

上記の追加部分を追加すると投稿フォームにて、空欄で投稿したときにエラーでアプリが止まります。
空欄での投稿を受け付けないようにしたということです。
ただ、空白で投稿したときエラー画面に送られてしまうのは良くないので、投稿が失敗した際の処理をcontrollerに追加すると良いです。
以下の様に/controllers/haiku.rbを修正してください。

require './controllers/base.rb'
require './models/haiku.rb'
class HaikuController < Base
 get '/' do
   @haikulist = Haiku.all
   erb :index
 end
 get '/:id' do
   @haiku = Haiku.find(params[:id])
   erb :show
 end
 post '/create' do
 # ここから修正
   @haiku = Haiku.new({:main => params[:main]})
   if @haiku.save
     redirect '/haiku'
   else
     @haikulist = Haiku.all # ※注意
     erb :index
   end
 # ここまで
 end
 post '/:id/delete' do
   @haiku = Haiku.find(params[:id])
   @haiku.delete
   redirect '/haiku'
 end
end

上記の修正では今までHaiku.createとしていたところをHaiku.newと@haiku.saveの2段階に分けることで@haiku.saveの失敗をif文で判断して分岐するようになります。
else以降ではvalidationに引っかかることで@haiku.saveが失敗しますので、index.erbを再度出力しています。
このときerbファイルをそのまま出力するため、index.erbを形成するための@haikulistというインスタンス変数を保持せずに出力しようとするので直前などで渡してあげなければいけません(# ※ 注意部分)

これでエラーのときの処理が一応実装できましたが、ユーザーが使っていてこの処理に出会ったとき何がだめだったのかがわかりません。
エラー文を出してあげると親切でしょう。
エラー文の出力は色々方法があるのですが、今回はエラー表示のすべてを任せるためにerrorsページを作りましょう。
/views以下にerrors.erbを作り以下のように記述してください。

<main>
 <% if @errors.present? %>
 <p class="error-message"><%= @errors.full_messages.count %> errors occurred !!!!</p>
 <ul>
   <% @errors.full_messages.each do |message| %>
       <li class="error-message"><%=message%></li>
   <% end%>
 </ul>
 <% end %>
</main>
<style>
.error-message {
 color: red;
 font-size: 14px;
}
</style>

@errors変数にエラーの配列を貰えればすべて出力するという簡単なビューページです。

それでは、このページに@errors変数を渡す、かつ、このページを出力をする処理を/controllers/haiku.rbに追記及び修正しましょう。

require './controllers/base.rb'
require './models/haiku.rb'
class HaikuController < Base
 get '/' do
   @haikulist = Haiku.all
   erb :index
 end
 get '/:id' do
   @haiku = Haiku.find(params[:id])
   erb :show
 end
 post '/create' do
   @haiku = Haiku.new({:main => params[:main]})
   if @haiku.save
     redirect '/haiku'
   else
   # 修正部分
     if @haiku.errors.present?
       @errors = @haiku.errors
       erb :errors
     end
   # 修正部分ここまで
   end
 end
 post '/:id/delete' do
   @haiku = Haiku.find(params[:id])
   @haiku.delete
   redirect '/haiku'
 end
end

出来ましたら、dockerを立ち上げ直して、再度試してみましょう。
フォームの中身を空欄で投稿してエラーページに遷移できれば成功です。

さて、これで一応の機能は揃いました。
今章ではDBとの接続から追加機能や削除機能の実装を見てきました。

ただこのままだとどのユーザーでもすべての俳句を削除することが出来てしまいます。つぎの章ではユーザーを作成して基本的なログイン機能を作っていきましょう。

それでは、お疲れさまでした。

次へ >

いいなと思ったら応援しよう!