住所自動入力機能(jpostalとjp_prefectureの導入)
内容
住所登録/検索機能を実装する
住所登録機能
投稿時に住所(都道府県、市区町村)も一緒に登録できるようにする
postsテーブルにaddressesテーブルを紐付けるため、Formオブジェクトパターンを使用して投稿時に複数のテーブルに保存できるようにする
ちなみに入力は、jpostalとjp_prefectureというgemを用いて住所自動入力を実装する
1. jp_prefectureの導入
jquery-railsとjp_prefectureを導入する
今回は既にjQueryは導入済みのため省略
Gemfile
gem 'jp_prefecture'
ターミナル
% bundle install % rails s
2. モデルを作成し、マイグレーションファイルを編集
Postモデルは作成済みのため、Addressモデルのみ作成し、マイグレーションファイルを編集する
ターミナル
% rails g model address
db/migrate/**************_create_addresses.rb
create_table :addresses do |t| t.integer :postal_code, null: false t.integer :prefecture_code, null: false t.string :city, null: false t.string :street, null: false t.references :post, null: false, foreign_key: true t.timestamps end
ターミナル
% rails db:migrate
3. アソシエーションを記述
app/models/post.rb
has_one :address, dependent: :destroy
app/models/address.rb
belongs_to :post
4. モデルを編集
公式サイトを参考にモデルを編集する
JpPrefecture
address.rb
include JpPrefecture jp_prefecture :prefecture_code def prefecture_name JpPrefecture::Prefecture.find(code: prefecture_code).try(:name) end def prefecture_name=(prefecture_name) self.prefecture_code = JpPrefecture::Prefecture.find(name: prefecture_name).code end
5. 新たにmodelsディレクトリ直下にファイルを作成し、クラスを定義
app/modelsディレクトリ配下にpost_address.rbを作成
post_address.rb
class PostAddress include ActiveModel::Model attr_accessor :map_link, :distance, :course, :slope, :traffic, :crowd, :view, :comment, :user_id, :postal_code, :prefecture_code, :city, :street with_options presence: true do validates :map_link, format: { with: /\A#{URI::DEFAULT_PARSER.make_regexp(%w[http https])}\z/, message: 'はhttpもしくはhttpsで始まるURLで入力してください' } validates :distance, numericality: { greater_than: 0 } validates :course validates :slope validates :traffic validates :crowd validates :view validates :comment, length: { maximum: 140 } validates :user_id validates :postal_code, format: { with: /\A[0-9]{7}\z/ } validates :prefecture_code validates :city validates :street end end
postsテーブルに保存されるuser_idには、本来belongs_to :userのアソシエーションにより、バリデーションが設定されている
しかし、post_addressクラスにはアソシエーションを定義することはできないため、belongs_toによるバリデーションを行うことができない
そこで、post_addressクラスでuser_idに対してバリデーションを新たに設定
6. 元々記述していたpost.rbのバリデーションを削除
7. データをテーブルに保存する処理を記述
post_address.rb
class PostAddress # 略 def save post = Post.create(map_link: map_link, distance: distance, course: course, slope: slope, traffic: traffic, crowd: crowd, view: view, comment: comment, user_id: user_id) Address.create(postal_code: postal_code, prefecture_code: prefecture_code, city: city, street: street, post_id: post.id) end end
8. コントローラーを編集
postsコントローラーを以下のように編集する
posts_controller.rb
def new @post_address = PostAddress.new end def create @post_address = PostAddress.new(post_params) if @post_address.valid? @post_address.save redirect_to posts_path else render :new end end def post_params params.require(:post_address).permit(:map_link, :distance, :course, :slope, :traffic, :crowd, :view, :comment, :image, :postal_code, :prefecture_code, :city, :street).merge(user_id: current_user.id) end
valid?メソッドを使用しているのは、PostAddressクラスがApplicationRecordを継承していないことにより、saveメソッドにはバリデーションを実行する機能がないため
9. ビューを編集(新規投稿ページ)
生成したインスタンスをnew.html.erbにおいて利用
app/views/posts/new.html.erb
# 修正前 <%= form_with model: @post, local: true do |f| %> # 修正後 <%= form_with model: @post_address, local: true do |f| %> # 下記追加 <div class="weight-bold-text"> 郵便番号(ハイフンなし) <span class="indispensable">必須</span> </div> <%= f.text_field :postal_code, autocomplete: 'postal_code', class: "form-control", id: 'postal_code' %> <div class="weight-bold-text"> 都道府県 <span class="indispensable">必須</span> </div> <%= f.collection_select :prefecture_code, JpPrefecture::Prefecture.all, :code, :name, { prompt: '選択してください' }, class: 'form-control', id: 'prefecture_code' %> <div class="weight-bold-text"> 市区町村 <span class="indispensable">必須</span> </div> <%= f.text_field :city, autocomplete: 'city', class: "form-control", id: 'city' %> <div class="weight-bold-text"> 番地 <span class="indispensable">必須</span> </div> <%= f.text_field :street, autocomplete: 'street', class: "form-control", id: 'street' %>
10. jquery.jpostal.jsを導入
公式GitHubを参考に導入
jquery.jpostal.js
設置方法は2種類あり、自分のサーバに郵便データを設置する方法としない方法がある
jpostal-1006.appspot.comで公開しているので、jquery.jpostal.jsやjson/*.jsonを設置する必要がない、サイト運営者の定期的な郵便データ更新作業も必要ない、と記載されているので今回は自分のサーバにはデータを設置しない方法で導入する(おそらくこちらの方が楽)
まず、jquery本体とjquery.jpostal.jsをインクルードする(今回はjqueryは導入済みのためインクルードの必要はなかった)
application.html.erb
<head> <script type="text/javascript" src="//jpostal-1006.appspot.com/jquery.jpostal.js"></script> </head>
app/javascript配下にaddress_autofill.jsを作成し、コードを記述
app/javascript/address_autofill.js
$(function(){ $('#postal_code').jpostal({ postcode: ['#postal_code'], address: { '#prefecture_code': '%3', '#city': '%4', '#street': '%5%6%7', } }); });
公式サイトではjQueryのreadyメソッドを使用しているが、現在は非推奨となっているため修正している
最後にapplication.jsでaddress_autofill.jsを読み込む
app/javascript/packs/address_autofill.js
require("../address_autofill")
以上で導入完了
11. Formオブジェクトのedit/update
ここは難しく説明ができないため割愛
12. Formオブジェクトのテストコード
所感
理解できていないコードを写すのはよくないと痛感。。。 でもできることも増やしていかないといけないので、やりながら理解していくしかない!