[1. Create the skeleton of the app](# 1-Create the skeleton of the app) [2. Start form object](# 2-Start form object)
It was difficult to understand, but as the title says, form objects are used to save and update information in multiple models.
First is an image diagram
Use the form object to implement the format that needs to go through multiple models in this way.
Now let's think about what kind of description is required in the form object.
-Migration of validation set in the model that uses form object -Description that enables the form object to be used in the controller ・ Description through the model
Roughly, you'll need these three! Now, let's create a format to register user information and address information at the same time.
% rails _6.0.0_ new format_app -d mysql
% cd format_app
% rails db:create
After that, let's connect to the local host and check if you can connect to the rails initial screen
% rails g controller formats index
Routing settings
Rails.application.routes.draw do
root 'formats#index'
resources :formats, only: [:index, :new, :create]
end
Controller action settings First of all, only user registration and browsing will be possible
class FormatsController < ApplicationController
def index
@formats = User.all
end
def new
@format = User.new
end
def create
@format = User.create(format_params)
redirect_to root_path
end
private
def format_params
params.require(:user).permit(:name, :name_kana, :nickname)
end
end
% rails g model user
% rails g model address
Migration file settings
XXXXXXXXXX_create_user.rb
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.string :name_kana, null: false
t.string :nickname, null: false
t.timestamps
end
end
end
XXXXXXXXXX_create_address.rb
class CreateAddresses < ActiveRecord::Migration[6.0]
def change
create_table :addresses do |t|
t.string :postal_code, default: "", null: false
t.integer :prefecture_id, null: false
t.string :city, default: ""
t.string :house_number, default: ""
t.string :building_name, default: ""
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
Model settings
user.rb ・ Association settings -> user model parent and address model child
・ Validation settings -> Prevent the value from becoming empty -> Names are only kanji, hiragana, and katakana -> Katakana is the only frigana -> Nickname is only single-byte alphanumeric characters
class User < ApplicationRecord
has_one :address
with_options presence: true do
validates :name, format: {with:/\A[Ah-Hmm-One-龥]/, message: "is invalid. Please input full-width characters."}
validates :name_kana, format: {with:/\A[A-Car-]+\z/, message: "is invalid. Please input full-width katakana characters."}
validates :nickname, format: {with:/\A[a-z0-9]+\z/i, message: "is invalid. Please input half-width characters."}
end
end
address.rb ・ Association settings -> user model parent and address model child
class Address < ApplicationRecord
belongs_to :user
end
<div class="wrapper">
<div class="btn">
<%= link_to 'to register', new_format_path %>
</div>
<div class="formats">
<% @formats.each do |format| %>
<div class="format">
<div class="format-name"><%= format.name %>/<%= format.name_kana %>/<%= format.nickname %></div>
</div>
<% end %>
</div>
</div>
Create a format that allows you to register only user information
<%= form_with(model: @format, url: formats_path, local: true) do |form| %>
<h1>Enter user name</h1>
<div class="field">
<%= form.label :name, "Name (full-width)" %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :name_kana, "Frikana (double-byte katakana)" %>
<%= form.text_field :name_kana %>
</div>
<div class="field">
<%= form.label :nickname, "Nickname (half-width alphanumeric characters)" %>
<%= form.text_field :nickname %>
</div>
<div class="actions">
<%= form.submit "to register" %>
</div>
<% end %>
% mkdir app/forms
% touch app/forms/user_address.rb
Then define the same class name as the file name inside
class UserAddress
end
When using the form object, embed the description ActiveModel :: Model in the file generated above. This makes user_address.rb act like a model. See below.
class UserAddress
include ActiveModel::Model
end
This file is now a pseudo model.
Next, set the values that can be referenced / updated in this file. The value required at the time of user registration earlier
It was: name,: name_kana,: nickname.
And I want to register the address further, so add the following value. Zip code (: postal_code), prefecture (: prefecture_id), city (: city), address (: house_number), building name (: building_name)
So, use attr_accessor to describe so that you can refer to and change the contents of the instance variable.
class UserAddress
include ActiveModel::Model
attr_accessor :name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name
end
Next, we will put together the validation of the user model and address model in the form object (user_address.rb). The reason for doing this is to improve manageability and readability.
class User < ApplicationRecord
has_one :address
# ----------from here-------------
with_options presence: true do
validates :name, format: {with:/\A[Ah-Hmm-One-龥]/, message: "is invalid. Please input full-width characters."}
validates :name_kana, format: {with:/\A[A-Car-]+\z/, message: "is invalid. Please input full-width katakana characters."}
validates :nickname, format: {with:/\A[a-z0-9]+\z/i, message: "is invalid. Input half-width characters."}
end
# -------Cut up to here----------
end
class UserAddress
include ActiveModel::Model
attr_accessor :name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name
# -----Paste the cut part from here-----
with_options presence: true do
validates :name, format: {with:/\A[Ah-Hmm-One-龥]/, message: "is invalid. Please input full-width characters."}
validates :name_kana, format: {with:/\A[A-Car-]+\z/, message: "is invalid. Please input full-width katakana characters."}
validates :nickname, format: {with:/\A[a-z0-9]+\z/i, message: "is invalid. Input half-width characters."}
end
# -----Paste the cut part up to here-----
end
And I will add the validation of the address model. This time's notes ・ The postal code should be a specification that allows you to register only numbers, 3 digits, hyphens, and 4 digits in that order. -Prefectures use active_hash so that they cannot be registered unless the prefecture is selected.
I will not touch on active_hash so much, so please refer to other articles.
class UserAddress
include ActiveModel::Model
attr_accessor :name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name
with_options presence: true do
validates :name, format: {with:/\A[Ah-Hmm-One-龥]/, message: "is invalid. Please input full-width characters."}
validates :name_kana, format: {with:/\A[A-Car-]+\z/, message: "is invalid. Please input full-width katakana characters."}
validates :nickname, format: {with:/\A[a-z0-9]+\z/i, message: "is invalid. Please input half-width characters."}
# -----From here, validation of the address model-----
validates :postal_code, format: {with: /\A[0-9]+\z/, message: "is invalid. Please input half-width characters."}
validates :prefecture_id, numericality: { other_than: 0, message: "can't be blank" }
# -----Up to this point, validation of the address model-----
end
end
Now that the integration of user model and address model validation is complete, it's time to edit how the controller works.
The first thing you want to do with the controller is to register the value. Therefore, we need to create a method that allows values to be registered in the form object via two models. Let's save this time in an easy-to-understand manner.
class UserAddress
include ActiveModel::Model
attr_accessor :name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name
# -----Validation description omitted-----
def save
#Here is a description that saves the value via two models
end
end
The models I want to go through this time are User.rb and Address.rb, so they are as follows.
class UserAddress.rb
include ActiveModel::Model
attr_accessor :name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name
# -----Validation description omitted-----
def save
user = User.create(name: name, name_kana: name_kana, nickname: nickname)
Address.create(postal_code: postal_code, prefecture_id: prefecture_id, city: city, house_number: house_number, building_name: building_name, user_id: user.id)
end
end
As a result, the description of the controller is also changed to the description via the form object.
class FormatsController < ApplicationController
def index
@formats = User.includes(:address) #Change here
end
def new
# @format = User.new Delete here
@format = UserAddress.new #Add here
end
def create
# @format = User.create(format_params)Delete here
@format = UserAddress.new(format_params) #Add here
@format.save # user_addres.Use the method defined in rb here
redirect_to root_path
end
private
def format_params
#Add the value required for address registration in permit
params.require(:user_address).permit(:name, :name_kana, :nickname, :postal_code, :prefecture_id, :city, :house_number, :building_name)
end
end
Edit the view file to create an address item registration field.
Added active_hash to Gemfile
gem 'active_hash'
Create the following file and describe it
class Prefecture < ActiveHash::Base
self.data = [
{id: 0, name: '--'}, {id: 1, name: 'Hokkaido'}, {id: 2, name: 'Aomori Prefecture'},
{id: 3, name: 'Iwate Prefecture'}, {id: 4, name: 'Miyagi Prefecture'}, {id: 5, name: 'Akita'},
{id: 6, name: 'Yamagata Prefecture'}, {id: 7, name: 'Fukushima Prefecture'}, {id: 8, name: 'Ibaraki Prefecture'},
{id: 9, name: 'Tochigi Prefecture'}, {id: 10, name: 'Gunma Prefecture'}, {id: 11, name: 'Saitama'},
{id: 12, name: 'Chiba'}, {id: 13, name: 'Tokyo'}, {id: 14, name: 'Kanagawa Prefecture'},
{id: 15, name: 'Niigata Prefecture'}, {id: 16, name: 'Toyama Prefecture'}, {id: 17, name: 'Ishikawa Prefecture'},
{id: 18, name: 'Fukui prefecture'}, {id: 19, name: 'Yamanashi Prefecture'}, {id: 20, name: 'Nagano Prefecture'},
{id: 21, name: 'Gifu Prefecture'}, {id: 22, name: 'Shizuoka Prefecture'}, {id: 23, name: 'Aichi prefecture'},
{id: 24, name: 'Mie Prefecture'}, {id: 25, name: 'Shiga Prefecture'}, {id: 26, name: 'Kyoto'},
{id: 27, name: 'Osaka'}, {id: 28, name: 'Hyogo prefecture'}, {id: 29, name: 'Nara Prefecture'},
{id: 30, name: 'Wakayama Prefecture'}, {id: 31, name: 'Tottori prefecture'}, {id: 32, name: 'Shimane Prefecture'},
{id: 33, name: 'Okayama Prefecture'}, {id: 34, name: 'Hiroshima Prefecture'}, {id: 35, name: 'Yamaguchi Prefecture'},
{id: 36, name: 'Tokushima Prefecture'}, {id: 37, name: 'Kagawa Prefecture'}, {id: 38, name: 'Ehime Prefecture'},
{id: 39, name: 'Kochi Prefecture'}, {id: 40, name: 'Fukuoka Prefecture'}, {id: 41, name: 'Saga Prefecture'},
{id: 42, name: 'Nagasaki Prefecture'}, {id: 43, name: 'Kumamoto Prefecture'}, {id: 44, name: 'Oita Prefecture'},
{id: 45, name: 'Miyazaki prefecture'}, {id: 46, name: 'Kagoshima prefecture'}, {id: 47, name: 'Okinawa Prefecture'}
]
end
Add association to model
class Address < ApplicationRecord
belongs_to :user
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to_active_hash :prefecture
end
Edit the view as below
<%= form_with(model: @format, url: formats_path, local: true) do |form| %>
<h1>Enter user name</h1>
<div class="field">
<%= form.label :name, "Name (full-width)" %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :name_kana, "Frikana (double-byte katakana)" %>
<%= form.text_field :name_kana %>
</div>
<div class="field">
<%= form.label :nickname, "Nickname (half-width alphanumeric characters)" %>
<%= form.text_field :nickname %>
</div>
<%#Add from here%>
<h1>Enter your address</h1>
<div class="field">
<%= form.label :postal_code, "Zip code (including hyphens)" %>
<%= form.text_field :postal_code %>
</div>
<div class="field">
<%= form.label :prefecture_id, "Prefectures" %>
<%= form.collection_select :prefecture_id, Prefecture.all, :id, :name, {} %>
</div>
<div class="field">
<%= form.label :city, "Municipalities (optional)" %>
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :house_number, "Address (optional)" %>
<%= form.text_field :house_number %>
</div>
<div class="field">
<%= form.label :building_name, "Building name (optional)" %>
<%= form.text_field :building_name %>
</div>
<%#Added up to here%>
<div class="actions">
<%= form.submit "to register" %>
</div>
<% end %>
Let's display the registered value.
<div class="wrapper">
<div class="btn">
<%= link_to 'to register', new_format_path %>
</div>
<div class="formats">
<% @formats.each do |format| %>
<div class="format">
<div class="format-name">User information:<%= format.name %>/<%= format.name_kana %>/<%= format.nickname %></div>
<div class="format-name">Address information:<%= format.address.postal_code%>/<%= format.address.prefecture.name%>/<%= format.address.city%>/<%= format.address.house_number%>/<%= format.address.building_name%></div>
</div>
<% end %>
</div>
</div>
This is completed!