お気に入り機能サンプル②(完結)
引き続き、Railsでお気に入り機能のサンプルを作成していきます。
前回は各ユーザが持っているお気に入り一覧を表示させるところまで作成しました。
今回はログインしたユーザがお気に入りの本を登録・削除する、という仕上げの部分を行っていきます。
コントローラ
まずコマンドラインからfavorites
コントローラーを作成します。
rails g controller favorites
次にお気に入りを登録するcreate
、お気に入りを削除するdestroy
アクションを記述します。
セッションに格納したログインユーザのIDと、本一覧画面から選んだ特定の本のIDを初期値として新しいレコードを作ることで、ユーザ別のお気に入りを登録できる様になります。
お気に入りの削除は、ユーザ別のお気に入り一覧(ユーザ詳細画面内)から特定のお気に入りを選びdestroy
メソッドを使うことで可能になります。
- app/controllers/favorites_controller.rb
#お気に入り登録用アクション def create @user_id = session[:id] #ログインしたユーザのID @book_id = Book.find(params[:id]).id #特定の本のID #book_idに@book_id、user_idに@user_idを入れて、Favoriteモデルに新しいオブジェクトを作る @favorite = Favorite.new(book_id: @book_id, user_id: @user_id) if @favorite.save #保存に成功した場合、本一覧画面に戻る redirect_to books_path end end #お気に入り削除用アクション def destroy @favorite = Favorite.find(params[:id]) if @favorite.destroy #削除に成功した場合、ログインしているユーザの詳細画面に戻る redirect_to user_path(session[:id]) end
ルーティング
先ほど作成したfavorites
コントローラと各アクションにルーティングをしていきます。
お気に入りを登録するcreate
アクションは、本一覧から特定の本を選んで登録するので、resources books
の入れ子でルーティングをしています。その際アクション名をadd
に指定しています。
お気に入りを削除するdestroy
アクションは複数リソースのonly
オプションでルーティングしています。
- config/routes.rb
root "top#index" resource :sessions, only: [:new, :create, :destroy] resources :users #以下今回追加分 resources :books do member do #本一覧画面からお気に入り登録をする post "add", to: "favorites#create" end end #個人ページからお気に入りを削除する resources :favorites, only: [:destroy]
ビュー
本一覧画面にお気に入り登録のリンクを記述します。
- app/views/books/index.html.erb
<table> <thead> <tr> <th></th><th>タイトル</th><th>価格</th> </tr> </thead> <tbody> <% @books.each do |book| %> <tr> <!-- お気に入り登録ボタン ルートに従いパスを記述、POSTメソッドに指定 --> <td><%= link_to "登録", add_book_path(book), method: :post %></td> <!-- 本の詳細へのリンク --> <td><%= link_to book.name,book_path(book) %></td> <td><%= book.price %></td> </tr> <% end %> </tbody> </table>
ユーザの詳細画面を修正していきます。
削除リンクのパスfavorite_path
の引数を@favorite[i][:id]
にすることで、お気に入りレコードのIDが取得できるので、これでレコードを削除するパスが生成されます。(実は@favorite[i]
でもいけます)
- app/views/users/show.html.erb
<h3>お気に入りの本</h3> <table> <thead> <tr> <th>削除</th> <th>タイトル</th> <th>価格</th> </tr> </thead> <tbody> <!-- 削除リンクの為にindex付きのメソッドを使用 --> <% @user.books.each_with_index do |book, i| %> <tr> <td><%= link_to "削除",favorite_path(@favorites[i][:id]), method: :delete %></td> <td><%= book.name %></td> <td><%= book.price %></td> </tr> <% end %> </tbody> </table>
これでひとまずログインしたユーザが本をお気に入りを登録・削除できる機能が実装できました。
以上。
お気に入り機能サンプル①
Ruby on Railsで、ログインしたユーザが本一覧の中から気になる本をお気に入りに登録・削除するというサンプルを作っていきたいと思います!
ちょっと長くなりそうなので二回に分けて書いていきたいと思います。
Rails 4.25
ruby 2.23
今回の機能はこちらの記事からの続きになっています。
テーブル設計
中間テーブルを作る事で、テーブル同士は多対多の関係を結ぶことができます。
関係を結んだ後は、お互いのテーブルから他方にアクセスできるようになります。
一人のユーザは複数のお気に入りの本を持ち、一冊の本は複数のユーザにお気に入り登録されています。
1 | 多 / 多 | 1 |
---|---|---|
User | Favorite(中間テーブル) | Book |
モデル
rails g model favorite
app/models/favorites.rb
belongs_to
でusers、booksテーブルとそれぞれ1対多の関係を結びます。
class Favorite < ActiveRecord::Base belongs_to :user #User:Favorite => 1:多 belongs_to :book #Book:Favorite => 1:多 end
app/models/users.rb
has_many
でfavaritesテーブルと1対多の関係を結びます。
has_many
とthrough:
を使い、favoritesテーブルを通してbooksテーブルと多対多の関係を結びます。
ここでbooks
が複数形なことに注意しましょう。
class User < ActiveRecord::Base has_many :favorites #User:Favorite => 1:多 has_many :books, through: :favorites #クラスメソッド省略 end
Bookモデル作成
Bookモデルを作成します。コマンドラインで下記コマンドを入力します。
rails g model book name:string price:integer
app/models/books.rb
先ほどと同様に、
has_many
でfavaritesテーブルと1対多の関係を結びます。
has_many
とthrough:
を使い、favoritesテーブルを通してusersテーブルと多対多の関係を結びます。
ここでもusers
が複数形なことに注意しましょう。
class Book < ActiveRecord::Base has_many :favorites #User:Favorite => 1:多 has_many :users, through: :favorites end
これでひとまずモデル同士の紐づけは完了しました。
マイグレーションファイル/テーブル作成
favoritesテーブルを作っていきます。
db/migrate/yyyymmddhhmmss_create_favorites.rb
カラムの設定をしてなかったので、マイグレーションファイルに直接記述していきます。
class CreateFavorites < ActiveRecord::Migration def change create_table :favorites do |t| t.references :user, null:false #外部キー t.references :book, null:false #外部キー t.timestamps null: false end add_index :favorites, :user_id #インデックス add_index :favorites, :book_id #インデックス end end
次にマイグレーションファイルを実行してテーブルを作成します。
rake db:migrate
これでfavoritesテーブルができました。テーブル同士の紐づけも完了しました。
シードデータ
db/seeds.rb
シードデータに適当なデータを準備しておきます。
Book.create!(name: "マギ", price: 500) Book.create!(name: "シンドバッドの冒険", price: 600) Book.create!(name: "海皇紀", price: 580) User.create!(name: "アラジン", email: "magi@hoge.com", password: "magimagi") Favorite.create!(user_id: 1, book_id: 1) Favorite.create!(user_id: 1, book_id: 3)
テーブルに反映します。
rake db:reset
ルーティング
ユーザへのルーティングを行います。
root "top#index" resource :sessions, only: [:new, :create, :destroy] resources :users #追加分
コントローラ
コントローラを作成していきます。
app/controllers/users_controller.rb
今回は特定のユーザの詳細ページにアクセスすると、ユーザの登録したお気に入り一覧が出る所まで実装します。
※今回の機能とは直接関係ないので、ユーザ一覧のコード等は省略します。
def show @user = User.find(params[:id]) #特定のユーザーが登録したお気に入りを全て取得する @favorites = Favorite.where("user_id = ?", @user) end
ビュー
app/views/user/show.html.erb
ユーザ詳細ページにユーザが登録したお気に入りの本一覧を表示します。
@user.books
で特定のユーザが持つお気に入りの本にアクセスできます。
<h3>お気に入りの本</h3> <table> <thead> <tr> <th>削除</th> <th>タイトル</th> <th>価格</th> </tr> </thead> <tbody> <% @user.books.each do |book| %> <tr> <td><%= book.name %></td> <td><%= book.price %></td> </tr> <% end %> </tbody> </table>
これで各ユーザ別お気に入りリストが見れるようになりました。 次回はお気に入りの登録・削除を実装していきます。
参考
ログイン & ログアウト機能サンプル
WEBサービスで確実にお世話になるログイン&ログアウト機能のサンプルを作ってみたのでメモします。
Rails 4.25
ruby 2.23
データベース
usersテーブル
こんな感じのテーブルを用意してください。値は何でもいいです。作り方は下記を参照。
id | name | password | |
---|---|---|---|
1 | アラジン | magi@hoge.com | magimagi |
ルーティング
- /config/routes.rb
#ルートにアクセスするとtopコントローラーのindexアクションを呼び出す。 root "top#index" #CRUD操作のnew/create/destroyのみ使用する。リソースが単数形なのに注意。 resource :sessions, only: [:new, :create, :destroy] #個別にルーティングする方法もある。 #get "signin", to: "sessions#new" #post "signin", to: "sessions#create" #delete "signout", to: "sessions#destroy"
個別ルーティングの場合はこうなる。
モデル
- /app/models/user.rb
#クラスメソッド class << self def check(email, password) user = self.find_by(email: email) user_pass = self.find_by(password: password) #変数userとuser_passのidが一致すれば、変数userを返す if user == user_pass user else nil end end end
コントローラー
/app/controllers/top_controller.rb
中身は空で大丈夫です。
/app/controllers/sessions_controller.rb
#ログイン画面を呼び出す為のアクションなので中身は何もない def new end #セッションを取得/ログイン def create #user.rbに書いたクラスメソッドcheckを使う。フォームからの値をparamsメソッドで受け取る。 @user = User.check(params[:session][:email], params[:session][:password]) #@userがtrueなら、@userのnameをハッシュsessionにキー[:name]でセットする。 if @user session[:name] = @user.name #メッセージをハッシュflashにキー[:success]でセットする。 flash[:success] = "ログインに成功しました。" #rootのページに遷移する。 redirect_to root_path else flash.now[:error] = "メールアドレスとパスワードが一致しません。" render "new" end end #セッションを破棄/ログアウト def destroy #session[:name]に入れた値をdeleteメソッドで削除する。 session.delete(:name) redirect_to new_sessions_path #個別ルーティングの場合 #redirect_to signin_path end
ビュー
- /app/views/sessions/new.html.erb
<h1>Sign in</h1> <!-- ログイン失敗のメッセージを出す --> <%= flash[:error] %> <%= form_for(:session, url: sessions_path) do |f| %> <!-- 個別ルーティングの場合 --> <%#= form_for(:session, url: signin_path) do |f| %> <%= f.label :email %> <%= f.text_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.submit "Sign in" %> <% end %>
- /app/views/top/index.html.erb
<!-- ログアウトリンク --> <%= link_to "Log Out", :sessions, method: :delete %> <!-- 個別ルーティングの場合 --> <%# link_to "Log Out", signout_path, method: :delete %> <!-- ログイン成功のメッセージを出す --> <p><%= flash[:success] %></p> <!-- ログインしたユーザーの名前を表示する --> <p>Welcom<%= session[:name] %>さん</p>
とても簡易的ですが、ひとまずログインしたのがどのユーザーかを認識できるようになりました。
マイグレーションファイルとは?/モデルの基本
Railsを勉強していると、最初の内はチュートリアル等の言われるがままに作業して中の動きが分からないまま・・・なんてことが多いと思います。筆者も分からないまま作業を進めていることが多くて悩んでいます。。
今回はモデルにおける簡単な内部の動きが少し分かったので、基本的なコマンドと一緒に記載しておきます。
マイグレーションファイルとは
まずモデルを作成する上で、マイグレーションファイルという名称が聞き慣れない言葉で頭に中々入って来ずに苦労しました。
モデルを作る際に同時に作成されるスクリプトファイルなのですが、このマイグレーションファイルを使うことでSQL文を直接書かずにデータべースにテーブルを作成・変更することができます。
つまりデータベースの種類(MySQL,PostgreSQL...)に左右されなくて済みます。(もっと言えばSQLが分からなくてもある程度大丈夫。)
また、テーブルの作成・変更をする毎にマイグレーションファイルが作成されるので、そのファイルを使用してマイグレーションファイル実行前の状態にも戻せます。
モデルとテーブル
頻繁に使うと思われるモデルとテーブルの操作方法について記載していきます。
rails g model モデル名 カラム名:データ型 例: rails g model user name:string age:integer gender:integer
rake db:migrate
モデル・マイグレーション削除
コマンドラインで下記コマンドを入力すると、作成したモデルとマイグレーションが削除されます。(因みにコントローラも
destroy
を使って削除できます)
(delete
というのもあります。destroy
との違いはここを参照)
rails destroy model モデル名 例: rails destroy model user
rake db:rollback
シードデータ
開発されたアプリの、データベースにおける初期状態のレコードを扱うファイルのことで、このファイルに記載したものが初期レコードになります。
シードデータの書き込み
/db/seeds.rb
に初期化用のデータ(シードデータ)を記述します。
例: User.create!(name: "田中太郎", age: 28, gender: 0)
シードデータの投入
コマンドラインで下記コマンドを入力すると、先ほど記述したシードデータがデータべースに投入されます。
rake db:seed
シードデータのリセット
コマンドラインで下記コマンドを入力すると、ファイルの記述内容でデータベースがリセットされる。
開発途中でデータベースを消したり増やしたりしても、これを使えば初期状態までデータベースを戻すことができます。
rake db:reset
とりあえず今回はここまで。今後も追記予定です。
以上
ルーティングの基本
Railsにおいてルーティングは非常に大事かつ、ややこしくて混乱しやすいと思うので、理解できた範囲をまとめていきます。(実際筆者は大分混乱しました)
root画面の変更
アプリケーション作成直後、URLにlocalhost:3000
と打つと写真の様にRailsのWelcome画面が出ますが、Welcome画面以外を表示したいときの記述。
これでコントローラーに記述されたアクションに紐付いたViewが呼び出されるようになります。
# /config/routes.rb root "コントローラー名#アクション名" #例 #topコントローラーのindexアクションに紐づくView、/top/index.html.erb が呼び出される root "top#index"
リソースベースのルーティング - CRUD操作の追加
1つのコントローラーに対してCRUD操作のアクションを自動的に紐づけてくれる記述。(index
/show
/new
/create
/edit
/update
/destroy
が設定されます。便利!)
# /config/routes.rb resources :コントローラー名 #CRUDの中の一部のアクションのみ使いたい場合は、限定した書き方もできる resources :コントローラー名, only: [:アクション名, :アクション名] #例 resources :books #usersコントローラーのnewとdestroyアクションのみ使える resources :users, only: [:new, :destroy]
リソースに任意のアクション追加
リソースベースのルーティングで任意のアクションを追加する記述。(search
など)
# /config/routes.rb resources :コントローラー名 do #collectionメソッドのブロック内のアクションは複数のデータを扱う collection {HTTPメソッド名 "アクション名"} #コントローラー名(単数)メソッドのブロック内のアクションは単数のデータを扱う コントローラー名(単数) {HTTPメソッド名 "アクション名"} end #例 resources :books do collection {get "search"} #本の検索 book {patch "suspend","restore"} #本の公開停止・再開 end
URLをカスタマイズする方法
任意の名称でURLをカスタマイズする記述。(localhost:3000/users/new
をlocalhost:/signup
というURLに変更したい時など)
# /config/routes.rb match "任意のパス", to: "コントローラー名#アクション名", via: "HTTPメソッド名" #例 resources :users #localhost:3000/signupにアクセスすると、usersコントローラーのnewアクションをHTTPメソッドのgetで呼び出す match "/signup", to: "users#new", via: "get"
RailsにBootstrapを適用時モーダルウィンドウがうまく動いてくれなかった件
サインイン画面をモーダルウィンドウで実装していたんですが、モーダルウィンドウを呼びだしたら一瞬出てきてすぐ引っ込む、という挙動で大ハマりしたので記録。
View設定
まずサンプルとしてapplication.html.erb
の中身から見ていきます。
head
タグ内のstylesheet_link_tag
とjavascript_include_tag
に注目。
それぞれがapplication.css
とapplication.js
の設定ファイルを読み込む。
/app/viewa/layouts/application.html.erb <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Sampleだよ</title> <!-- /app/assets/stylesheets/application.css の読み込み --> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <!-- /app/assets/javascripts/application.js の読み込み --> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <!-- IE9未満時用の読み込み --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <!-- 省略 --> </body> </html>
CSS設定
下記の様にコードを変更する事で/assets/stylesheets
内のbootstrap.min.js
を読み込むことができます。
=
の前に*
を付ければ設定を有効、消せば無効となります。
app/assets/stylesheets/application.css /* 追加 */ *= require bootstrap.min /* 初期設定は有効 => 他のCSSファイルも適用したいなら無効 */ = require_tree . *= require_self
Javascript設定
=
の前に//
を付ければ設定を有効、消せば無効となります。
app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require turbolinks //= require bootstrap.min # 追加 = require_tree . //初期設定は有効 => 無効
require_tree .とは一体何か?
先ほどからcssやjsファイルで有効化したり無効化したりしているrequire_tree .
とは一体どういう意味を持つのでしょうか?
それはapp/assets/javascripts(stylesheets)/
配下にある全ファイルを読み込む という意味を持ちます。(勉強していたらSassの項目で出てきました)
これによって不具合が起こっていたみたいです。(内部的な動きはまだ理解できていませんが、おそらく全てのファイルを読み込むことで設定がバッティングしてるんじゃないかと推測されます。)
今回はCSSにBootstrap以外の設定を読み込ませていなかったので、require_tree .
を無効化しなくても大丈夫でしたが、別の設定も読み込ませたい場合は無効化する必要が出てくると思われます。
参考サイトを見る限りconfig/application.rb
にも記述をしないといけないみたですが、今の所よく分かっていないので今後も調査が必要です。
redirect_to と render の違いについて
Rails-4.25
上記のメソッドの使い方の違いが曖昧だったので調べてみました。
ざっくり結論から言うと、
redirect_to
はアクションそのものを呼び出してページを遷移するrender
はアクションはそのままだけどviewだけ指定ものを使用する(controllerの変数などはそのまま)
という具合です。
例えばindex.html.erb
にindex
アクションでMember
モデルから全件取得された下記のレコードが表示されているとする。
名前 | 年齢 |
---|---|
田中一郎 | 25 |
鈴木一郎 | 22 |
田中花子 | 24 |
<!-- index.html.erb --> <table> <tr> <th>名前</th><th>年齢</th> </tr> <% @members.each do |member| %> <tr> <td><%= member.name %></td><td><%= member.age %></td> </tr> <% end %> </table>
ここから田中という名前の人のみを検索したい、となった時にsearch
というアクションを呼び出し、search.html.erb
に遷移し検索結果を表示する。
# members_controller.rb # 検索フォームが空なら全件取得、そうでないなら名前をあいまい検索して search.html.erb に遷移 def search if params[:name] == "" @members = Member.all else @members = Member.where("name like ?", "%"+params[:name]+"%") end end
名前 | 年齢 |
---|---|
田中一郎 | 25 |
田中花子 | 24 |
<!-- search.html.erb --> <table> <tr> <th>名前</th><th>年齢</th> </tr> <% @members.each do |member| %> <tr> <td><%= member.name %></td><td><%= member.age %></td> </tr> <% end %> </table>
ただこの方法だと、ほとんどviewの表示形式が同じなのにも関わらず、search.html.erb
というファイルを作成しなければなりません。
サイトの形式にもよりますが、index.html.erb
のファイルをそのまま使えるとしたら、先ほどのコードは下記のように修正できます。
# 検索フォームが空ならindexアクションを呼び出し、index.html.erbに遷移 # members_pathはビューヘルパー表記 今回の場合は /members もしくは /members/index を表す # 検索フォームが空でないなら名前をあいまい検索し、searchアクションを呼び出し、index.html.erb に遷移 def search if params[:name] == "" redirect_to members_path else @members = Member.where("name like ?", "%"+params[:name]+"%") render action: "index" end end
こうすると余計なファイル(今回でいうとsearch.html.erb
)を作成しなくても検索結果が表示可能に
ります。
以上
参考
[Rails] renderとredirect_toの違い - 拝啓、シーシュポス Railsのリダイレクト(redirect_to)でよく使う5つの方法 | NESTonline Blog