0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【FormObject】使ってみた

Last updated at Posted at 2021-11-09

##環境
Ruby 3.0.2
Rails 6.1.4.1

##状況

userとemployeeテーブルの作成と更新を一つの画面で行いたいとき、
accepts_nested_attributes_forは非推奨なのでFormObjectを使ってリファクタリングしてみた。

app/models/employee.rb
class Employee < ApplicationRecord
...
  accepts_nested_attributes_for :user
...
end

##メリット
コントローラーがすっきりする

##EmployeeForm

app/forms/employee_form.rb
class EmployeeForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :firstname, :string
  attribute :lastname, :string
  attribute :number, :integer
  attribute :email, :string
  attribute :role, :string
  attr_reader :employee

  with_options presence: true do
    validates :firstname
    validates :lastname
  end

  delegate :persisted?, to: :employee

  def initialize(attributes = nil, employee: Employee.new)
    @employee = employee
    attributes ||= default_attributes
    super(attributes)
  end

  def save
    return false if invalid?
    ActiveRecord::Base.transaction do
      employee.update!(attributes.except('role'))
      employee.user.update!(role: role) unless employee.user.nil?
      employee
    end
  rescue => e
    Rails.logger.error e.message
    false
  end

  def to_model
    employee
  end
  
  private

  def default_attributes
    {
      firstname: employee.firstname,
      lastname: employee.lastname,
      number: employee.number,
      email: employee.email,
      role: employee.user.try(:role)
    }
  end
end
  • include ActiveModel::Model→モデルと同じようにバリデーションを書ける
  • include ActiveModel::Attributes→クラスメソッドattributeに属性名と型を渡すと、attr_accessorと同じように属性が使えるようになる
  • a ||= xxx : aが偽か未定義ならaにxxxを代入する
  • #initializeではFormオブジェクトの値を初期化している。 #superActiveModel::Model#initializeを呼び出しており、書き込みメソッド(#firstname=など)を用いて値を代入している。(super は現在のメソッドがオーバーライドしているメソッドを呼び出す)
  • #persisted?#to_modelはビューの表示(#form_with)に必要なメソッド。#persisted?は作成・更新に応じてフォームのアクションをPOST・PATCHに切り替えてくれる。 また#to_modelはアクションのURLを適切な場所に切り替えてくれる。
  • employee.update!(attributes.except('role'))で属性を一つ一つ指定しているやり方が多かったが、できるだけ少ないコードで書きたかったため無理やりexceptしている。

##Controller

app/controllers/manage/employees_controller.rb
class Manage::EmployeesController < ApplicationController
  before_action :set_employee, only: [:edit, :update, :destroy]

  def index
    @employees = Employee.all
  end

  def new
    @employee_form = EmployeeForm.new
  end

  def create
    @employee_form = EmployeeForm.new(employee_form_params)
    if @employee_form.save
      redirect_to new_manage_employee_path, notice: '設定の追加が完了しました'
    else
      redirect_to new_manage_employee_path, alert: '設定の追加に失敗しました'
    end
  end

  def edit
    @employee_form = EmployeeForm.new(employee: @employee)
  end

  def update
    @employee_form = EmployeeForm.new(employee_form_params, employee: @employee)
    if @employee_form.save
      redirect_to edit_manage_employee_path(@employee), notice: '設定の更新が完了しました'
    else
      redirect_to edit_manage_employee_path(@employee), alert: '設定の更新に失敗しました'
    end
  end

  def destroy
  end

  private

  def set_employee
    @employee = Employee.find(params[:id])
  end

  def employee_form_params
    params.require(:employee).permit(
      :firstname, 
      :lastname, 
      :number, 
      :email, 
      :role
    )
  end
end

##View

new.html.slim
= form_with model: @employee_form, url: manage_preferences_employee_path, local: true do |f|
  = render 'form', form: @employee_form, f: f
  • new.html.slimとedit.html.slimから@employee_formをformとして渡す想定

##参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?